Bug 1394602: don't allow SSRC changes with Bundle. r?bwc draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Fri, 01 Dec 2017 15:54:57 -0800
changeset 775429 b25bfdb2ddcbf55c7d8b66cf55281aaaa4c43a21
parent 768965 97160a734959af73cc97af0bf8d198e301ebedae
push id104722
push userdrno@ohlmeier.org
push dateSat, 31 Mar 2018 06:44:37 +0000
reviewersbwc
bugs1394602
milestone61.0a1
Bug 1394602: don't allow SSRC changes with Bundle. r?bwc MozReview-Commit-ID: Kgb0lghAY7r
dom/media/tests/mochitest/templates.js
media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
media/webrtc/signaling/src/media-conduit/VideoConduit.h
media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -478,17 +478,17 @@ function PC_LOCAL_REMOVE_RTCPMUX_FROM_OF
 
 function PC_LOCAL_REMOVE_SSRC_FROM_OFFER(test) {
   test.originalOffer.sdp = sdputils.removeSSRCs(test.originalOffer.sdp);
   info("Updated no SSRCs offer: " + JSON.stringify(test.originalOffer));
 };
 
 function PC_REMOTE_REMOVE_SSRC_FROM_ANSWER(test) {
   test.originalAnswer.sdp = sdputils.removeSSRCs(test.originalAnswer.sdp);
-  info("Updated no SSRCs answer: " + JSON.stringify(test.originalAnswerr));
+  info("Updated no SSRCs answer: " + JSON.stringify(test.originalAnswer));
 };
 
 var addRenegotiation = (chain, commands, checks) => {
   chain.append(commands);
   chain.append(commandsPeerConnectionOfferAnswer);
   if (checks) {
     chain.append(checks);
   }
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -377,16 +377,18 @@ public:
    * 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
    * and attaches the new to the Conduit.
    */
   virtual MediaConduitErrorCode AttachRenderer(RefPtr<mozilla::VideoRenderer> aRenderer) = 0;
   virtual void DetachRenderer() = 0;
 
+  virtual void DisableSsrcChanges() = 0;
+
   bool SetRemoteSSRC(unsigned int ssrc) override = 0;
 
   /**
    * Function to deliver a capture video frame for encoding and transport.
    * If the frame's timestamp is 0, it will be automatcally generated.
    *
    * NOTE: ConfigureSendMediaCodec() must be called before this function can
    *       be invoked. This ensures the inserted video-frames can be
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -269,16 +269,18 @@ WebrtcVideoConduit::WebrtcVideoConduit(R
   , mDenoising(false)
   , mLockScaling(false)
   , mSpatialLayers(1)
   , mTemporalLayers(1)
   , mCodecMode(webrtc::kRealtimeVideo)
   , mCall(aCall) // refcounted store of the call object
   , mSendStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
   , mRecvStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
+  , mAllowSsrcChange(true)
+  , mWaitingForInitialSsrc(true)
   , mRecvSSRC(0)
   , mRecvSSRCSetInProgress(false)
   , mSendCodecPlugin(nullptr)
   , mRecvCodecPlugin(nullptr)
   , mVideoStatsTimer(NS_NewTimer())
 {
   mRecvStreamConfig.renderer = this;
 
@@ -873,33 +875,34 @@ WebrtcVideoConduit::ConfigureSendMediaCo
   }
 
   return condError;
 }
 
 bool
 WebrtcVideoConduit::SetRemoteSSRC(unsigned int ssrc)
 {
-  CSFLogDebug(LOGTAG, "%s: SSRC %u (0x%x)", __FUNCTION__, ssrc, ssrc);
-  mRecvStreamConfig.rtp.remote_ssrc = ssrc;
-
   unsigned int current_ssrc;
   if (!GetRemoteSSRC(&current_ssrc)) {
     return false;
   }
 
   if (current_ssrc == ssrc) {
     return true;
   }
 
   bool wasReceiving = mEngineReceiving;
   if (StopReceiving() != kMediaConduitNoError) {
     return false;
   }
 
+  CSFLogDebug(LOGTAG, "%s: SSRC %u (0x%x)", __FUNCTION__, ssrc, ssrc);
+  mRecvStreamConfig.rtp.remote_ssrc = ssrc;
+  mWaitingForInitialSsrc = false;
+
   // This will destroy mRecvStream and create a new one (argh, why can't we change
   // it without a full destroy?)
   // We're going to modify mRecvStream, we must lock.  Only modified on MainThread.
   // All non-MainThread users must lock before reading/using
   {
     MutexAutoLock lock(mCodecMutex);
     // On the next StartReceiving() or ConfigureRecvMediaCodec, force
     // building a new RecvStream to switch SSRCs.
@@ -1964,86 +1967,88 @@ WebrtcVideoConduit::DeliverPacket(const 
   }
 
   return kMediaConduitNoError;
 }
 
 MediaConduitErrorCode
 WebrtcVideoConduit::ReceivedRTPPacket(const void* data, int len, uint32_t ssrc)
 {
-  // Handle the unknown ssrc (and ssrc-not-signaled case).
-  // We can't just do this here; it has to happen on MainThread :-(
-  // We also don't want to drop the packet, nor stall this thread, so we hold
-  // the packet (and any following) for inserting once the SSRC is set.
-  bool queue = mRecvSSRCSetInProgress;
-  if (queue || mRecvSSRC != ssrc) {
-    // capture packet for insertion after ssrc is set -- do this before
-    // sending the runnable, since it may pull from this.  Since it
-    // dispatches back to us, it's less critial to do this here, but doesn't
-    // hurt.
-    UniquePtr<QueuedPacket> packet((QueuedPacket*) malloc(sizeof(QueuedPacket) + len-1));
-    packet->mLen = len;
-    memcpy(packet->mData, data, len);
-    CSFLogDebug(LOGTAG, "queuing packet: seq# %u, Len %d ",
-                (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
-    if (queue) {
+  if (mAllowSsrcChange || mWaitingForInitialSsrc) {
+    // Handle the unknown ssrc (and ssrc-not-signaled case).
+    // We can't just do this here; it has to happen on MainThread :-(
+    // We also don't want to drop the packet, nor stall this thread, so we hold
+    // the packet (and any following) for inserting once the SSRC is set.
+    bool queue = mRecvSSRCSetInProgress;
+    if (queue || mRecvSSRC != ssrc) {
+      // capture packet for insertion after ssrc is set -- do this before
+      // sending the runnable, since it may pull from this.  Since it
+      // dispatches back to us, it's less critial to do this here, but doesn't
+      // hurt.
+      UniquePtr<QueuedPacket> packet((QueuedPacket*) malloc(sizeof(QueuedPacket) + len-1));
+      packet->mLen = len;
+      memcpy(packet->mData, data, len);
+      CSFLogDebug(LOGTAG, "queuing packet: seq# %u, Len %d ",
+                  (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
+      if (queue) {
+        mQueuedPackets.AppendElement(Move(packet));
+        return kMediaConduitNoError;
+      }
+      // a new switch needs to be done
+      // any queued packets are from a previous switch that hasn't completed
+      // yet; drop them and only process the latest SSRC
+      mQueuedPackets.Clear();
       mQueuedPackets.AppendElement(Move(packet));
+
+      CSFLogDebug(LOGTAG, "%s: switching from SSRC %u to %u", __FUNCTION__,
+                  mRecvSSRC, ssrc);
+      // we "switch" here immediately, but buffer until the queue is released
+      mRecvSSRC = ssrc;
+      mRecvSSRCSetInProgress = true;
+      queue = true;
+
+      // Ensure lamba captures refs
+      RefPtr<WebrtcVideoConduit> self = this;
+      nsCOMPtr<nsIThread> thread;
+      if (NS_WARN_IF(NS_FAILED(NS_GetCurrentThread(getter_AddRefs(thread))))) {
+        return kMediaConduitRTPProcessingFailed;
+      }
+      NS_DispatchToMainThread(media::NewRunnableFrom([self, thread, ssrc]() mutable {
+            // Normally this is done in CreateOrUpdateMediaPipeline() for
+            // initial creation and renegotiation, but here we're rebuilding the
+            // Receive channel at a lower level.  This is needed whenever we're
+            // creating a GMPVideoCodec (in particular, H264) so it can communicate
+            // errors to the PC.
+            WebrtcGmpPCHandleSetter setter(self->mPCHandle);
+            self->SetRemoteSSRC(ssrc); // this will likely re-create the VideoReceiveStream
+            // We want to unblock the queued packets on the original thread
+            thread->Dispatch(media::NewRunnableFrom([self, ssrc]() mutable {
+                  if (ssrc == self->mRecvSSRC) {
+                    // SSRC is set; insert queued packets
+                    for (auto& packet : self->mQueuedPackets) {
+                      CSFLogDebug(LOGTAG, "Inserting queued packets: seq# %u, Len %d ",
+                                  (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
+
+                      if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
+                        CSFLogError(LOGTAG, "%s RTP Processing Failed", __FUNCTION__);
+                        // Keep delivering and then clear the queue
+                      }
+                    }
+                    self->mQueuedPackets.Clear();
+                    // we don't leave inprogress until there are no changes in-flight
+                    self->mRecvSSRCSetInProgress = false;
+                  }
+                  // else this is an intermediate switch; another is in-flight
+
+                  return NS_OK;
+                }), NS_DISPATCH_NORMAL);
+            return NS_OK;
+          }));
       return kMediaConduitNoError;
     }
-    // a new switch needs to be done
-    // any queued packets are from a previous switch that hasn't completed
-    // yet; drop them and only process the latest SSRC
-    mQueuedPackets.Clear();
-    mQueuedPackets.AppendElement(Move(packet));
-
-    CSFLogDebug(LOGTAG, "%s: switching from SSRC %u to %u", __FUNCTION__,
-                mRecvSSRC, ssrc);
-    // we "switch" here immediately, but buffer until the queue is released
-    mRecvSSRC = ssrc;
-    mRecvSSRCSetInProgress = true;
-    queue = true;
-
-    // Ensure lamba captures refs
-    RefPtr<WebrtcVideoConduit> self = this;
-    nsCOMPtr<nsIThread> thread;
-    if (NS_WARN_IF(NS_FAILED(NS_GetCurrentThread(getter_AddRefs(thread))))) {
-      return kMediaConduitRTPProcessingFailed;
-    }
-    NS_DispatchToMainThread(media::NewRunnableFrom([self, thread, ssrc]() mutable {
-          // Normally this is done in CreateOrUpdateMediaPipeline() for
-          // initial creation and renegotiation, but here we're rebuilding the
-          // Receive channel at a lower level.  This is needed whenever we're
-          // creating a GMPVideoCodec (in particular, H264) so it can communicate
-          // errors to the PC.
-          WebrtcGmpPCHandleSetter setter(self->mPCHandle);
-          self->SetRemoteSSRC(ssrc); // this will likely re-create the VideoReceiveStream
-          // We want to unblock the queued packets on the original thread
-          thread->Dispatch(media::NewRunnableFrom([self, ssrc]() mutable {
-                if (ssrc == self->mRecvSSRC) {
-                  // SSRC is set; insert queued packets
-                  for (auto& packet : self->mQueuedPackets) {
-                    CSFLogDebug(LOGTAG, "Inserting queued packets: seq# %u, Len %d ",
-                                (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
-
-                    if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
-                      CSFLogError(LOGTAG, "%s RTP Processing Failed", __FUNCTION__);
-                      // Keep delivering and then clear the queue
-                    }
-                  }
-                  self->mQueuedPackets.Clear();
-                  // we don't leave inprogress until there are no changes in-flight
-                  self->mRecvSSRCSetInProgress = false;
-                }
-                // else this is an intermediate switch; another is in-flight
-
-                return NS_OK;
-              }), NS_DISPATCH_NORMAL);
-          return NS_OK;
-        }));
-    return kMediaConduitNoError;
   }
 
   CSFLogVerbose(LOGTAG, "%s: seq# %u, Len %d, SSRC %u (0x%x) ", __FUNCTION__,
                 (uint16_t)ntohs(((uint16_t*) data)[1]), len,
                 (uint32_t) ntohl(((uint32_t*) data)[2]),
                 (uint32_t) ntohl(((uint32_t*) data)[2]));
 
   if (DeliverPacket(data, len) != kMediaConduitNoError) {
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -305,16 +305,20 @@ public:
                              uint64_t* bytesReceived,
                              uint32_t* cumulativeLost,
                              int32_t* rttMs) override;
   bool GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
                            unsigned int* packetsSent,
                            uint64_t* bytesSent) override;
   uint64_t MozVideoLatencyAvg();
 
+  void DisableSsrcChanges() override {
+    mAllowSsrcChange = false;
+  }
+
 private:
   DISALLOW_COPY_AND_ASSIGN(WebrtcVideoConduit);
 
   /** Shared statistics for receive and transmit video streams
    */
   class StreamStatistics {
   public:
     void Update(const double aFrameRate, const double aBitrate);
@@ -530,16 +534,20 @@ private:
   // WEBRTC.ORG Call API
   RefPtr<WebRtcCallWrapper> mCall;
 
   webrtc::VideoSendStream::Config mSendStreamConfig;
   VideoEncoderConfigBuilder mEncoderConfig;
 
   webrtc::VideoReceiveStream::Config mRecvStreamConfig;
 
+  // Are SSRC changes without signaling allowed or not
+  bool mAllowSsrcChange;
+  bool mWaitingForInitialSsrc;
+
   // accessed on creation, and when receiving packets
   uint32_t mRecvSSRC; // this can change during a stream!
 
   // The runnable to set the SSRC is in-flight; queue packets until it's done.
   bool mRecvSSRCSetInProgress;
   struct QueuedPacket {
     int mLen;
     uint8_t mData[1];
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -858,16 +858,25 @@ TransceiverImpl::UpdateVideoConduit()
   // for the remote_ssrc that will be used by the far-end sender.
   if (!mJsepTransceiver->mRecvTrack.GetSsrcs().empty()) {
     MOZ_MTLOG(ML_DEBUG, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
               " Setting remote SSRC " <<
               mJsepTransceiver->mRecvTrack.GetSsrcs().front());
     conduit->SetRemoteSSRC(mJsepTransceiver->mRecvTrack.GetSsrcs().front());
   }
 
+  // TODO (bug 1423041) once we pay attention to receiving MID's in RTP packets
+  // (see bug 1405495) we could make this depending on the presence of MID in
+  // the RTP packets instead of relying on the signaling.
+  if (mJsepTransceiver->HasBundleLevel() &&
+      (!mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() ||
+       !mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()->GetExt(webrtc::RtpExtension::kMIdUri))) {
+    conduit->DisableSsrcChanges();
+  }
+
   if (mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() &&
       mJsepTransceiver->mRecvTrack.GetActive()) {
     const auto& details(*mJsepTransceiver->mRecvTrack.GetNegotiatedDetails());
 
     UpdateConduitRtpExtmap(details, LocalDirection::kRecv);
 
     PtrVector<VideoCodecConfig> configs;
     nsresult rv = NegotiatedDetailsToVideoCodecConfigs(details, &configs);