|
@@ -0,0 +1,343 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Nils Ohlmeier [:drno] <drno@ohlmeier.org>
|
|
|
+# Date 1512172497 28800
|
|
|
+# Node ID 9d5db4dacc58fe526ac7d4eb8cb757babad363bd
|
|
|
+# Parent 7613af7a7b4dd5d15dc8ebd0086576400e18f59a
|
|
|
+Bug 1394602: don't allow SSRC changes with Bundle. r=bwc
|
|
|
+
|
|
|
+MozReview-Commit-ID: Kgb0lghAY7r
|
|
|
+
|
|
|
+diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js
|
|
|
+--- 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);
|
|
|
+ }
|
|
|
+diff --git a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
|
|
|
+--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
|
|
|
++++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
|
|
|
+@@ -379,16 +379,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
|
|
|
+diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
|
|
|
+--- 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(¤t_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) {
|
|
|
+diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.h b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
|
|
|
+--- 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:
|
|
|
+ // Don't allow copying/assigning.
|
|
|
+ WebrtcVideoConduit(const WebrtcVideoConduit&) = delete;
|
|
|
+ void operator=(const WebrtcVideoConduit&) = delete;
|
|
|
+
|
|
|
+ /** Shared statistics for receive and transmit video streams
|
|
|
+ */
|
|
|
+ class StreamStatistics {
|
|
|
+@@ -532,16 +536,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];
|
|
|
+diff --git a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
|
|
|
+--- 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);
|