There is no threadsafe way to attach/detach media pipelines from media engines while media is flowing

RESOLVED FIXED in Firefox 38

Status

()

defect
RESOLVED FIXED
5 years ago
4 years ago

People

(Reporter: bwc, Assigned: bwc)

Tracking

Trunk
mozilla38
Points:
---
Dependency tree / graph
Bug Flags:
in-testsuite +

Firefox Tracking Flags

(firefox38 fixed)

Details

Attachments

(1 attachment, 3 obsolete attachments)

No description provided.
We need to be able to detach or attach either a MediaPipelineReceive or MediaPipelineTransmit from a MediaEngine at any time. Right now, the attachment is indirect via a WebrtcAudioConduit or WebrtcVideoConduit. The conduits are not safe to init or destroy while media is flowing, because we'll race on accesses to mOtherDirection, mVoiceEngine, mVideoEngine, and probably other stuff. It is not clear whether we can simply create both conduits from the get-go, whether we need them or not for a given m-line, and attach/detach MediaPipelines to them as needed.
So, it looks possible to create both conduits from the outset, but we would need to alter how we route stuff like the routing for SendPacket:

https://dxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp#792

Note that this assumes that both conduits are always attached to a pipeline transport (mTransport), this may not be the case if their lifetime is made independent of the pipelines.

There may be other gotchas too. Will keep looking.
What would happen if we moved away from having two conduits, to having just one, with the following properties:

1. Conduits would have mTransmitterTransport and mReceiverTransport, set by MediaPipelineTransmit and MediaPipelineReceive respectively.
2. RTP data would be passed to mTransmitterTransport, if set.
3. RTCP data would be passed to mReceiverTransport if set, otherwise mTransmitterTransport if set (I think this is functionally equivalent to what we do now, even though it isn't quite right for sender reports).
3a. Eventually it would be very nice to check whether the RTCP data is a sender or receiver report, and route it appropriately.
4. If/when an m-section is disabled, the associated conduit and media engine can be disposed of.
5. If/when a MediaPipelineTransmit goes away, the PipelineTransport will be detached on STS, and the conduit's |mTransmitterTransport| unset on the webrtc.org thread. Same thing goes for Receive.
6. If/when a MediaPipelineTransmit is created, it will attach a PipelineTransport on STS, and set the conduit's |mTransmitterTransport| on the webrtc.org thread. Same thing goes for Receive.
7. Creation/destruction of MediaPipelineReceiveVideo would result in the conduit's |mRenderer| being set and unset.
Hmm, I guess there's no straightforward way to run an arbitrary command on a webrtc.org thread, so we may need some locking for setting/unsetting the transport and renderer. Probably no big deal.
One thing that worries me is whether it will be easy/possible to update the codecs on a pre-existing conduit.
Another potential gotcha. With video conduits, we do not always attach as an external renderer here:

https://dxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp#469

Not sure whether we need to be able to set/unset this on the fly.
Assignee: nobody → docfaraday
Status: NEW → ASSIGNED
Comment on attachment 8561727 [details] [diff] [review]
(WIP) Use a single bidirectional media conduit that MediaPipelines can attach/detach at will

Review of attachment 8561727 [details] [diff] [review]:
-----------------------------------------------------------------

Seems to work for audio, but video is unhappy for some reason. Not sure why.
Attachment #8561727 - Flags: feedback?(rjesup)
Attachment #8561727 - Attachment is obsolete: true
Attachment #8561727 - Flags: feedback?(rjesup)
Attachment #8562208 - Flags: feedback?(rjesup)
Comment on attachment 8562208 [details] [diff] [review]
(WIP) Use a single bidirectional media conduit that MediaPipelines can attach/detach at will

Review of attachment 8562208 [details] [diff] [review]:
-----------------------------------------------------------------

Definitely looks reasonable; went over it in detail and compared to bug 864654's old patch

::: media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ +815,2 @@
>  //WebRTC::RTP Callback Implementation
>  int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len)

Add // Called on AudioGUM or MSG thread

@@ +841,4 @@
>    }
>  }
>  
>  int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)

Add // Called on WebRTC Process thread

@@ +842,5 @@
>  }
>  
>  int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)
>  {
>    CSFLogDebug(logTag,  "%s : channel %d", __FUNCTION__, channel);

Change to:  
CSFLogDebug(logTag,  "%s : channel %d , len %d, first rtcp = %u ",
            __FUNCTION__, channel, len, ((uint8_t *) data)[1]);

::: media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ +1179,2 @@
>  //WebRTC::RTP Callback Implementation
>  int WebrtcVideoConduit::SendPacket(int channel, const void* data, int len)

Add // Called on MSG thread

@@ +1193,4 @@
>    }
>  }
>  
>  int WebrtcVideoConduit::SendRTCPPacket(int channel, const void* data, int len)

Add // Called on webrtc Process thread

::: media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ +136,5 @@
> +      conduit_->StopReceiving();
> +    } else {
> +      conduit_->SetTransmitterTransport(nullptr);
> +      conduit_->StopTransmitting();
> +    }

Should this be done before or after DetachMediaStream?
Attachment #8562208 - Flags: feedback?(rjesup) → feedback+
Attachment #8562208 - Attachment is obsolete: true
(In reply to Randell Jesup [:jesup] from comment #10)
> Comment on attachment 8562208 [details] [diff] [review]
> (WIP) Use a single bidirectional media conduit that MediaPipelines can
> attach/detach at will
> 
> Review of attachment 8562208 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Definitely looks reasonable; went over it in detail and compared to bug
> 864654's old patch
> 
> ::: media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
> @@ +815,2 @@
> >  //WebRTC::RTP Callback Implementation
> >  int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len)
> 
> Add // Called on AudioGUM or MSG thread
> 
> @@ +841,4 @@
> >    }
> >  }
> >  
> >  int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)
> 
> Add // Called on WebRTC Process thread
> 
> @@ +842,5 @@
> >  }
> >  
> >  int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len)
> >  {
> >    CSFLogDebug(logTag,  "%s : channel %d", __FUNCTION__, channel);
> 
> Change to:  
> CSFLogDebug(logTag,  "%s : channel %d , len %d, first rtcp = %u ",
>             __FUNCTION__, channel, len, ((uint8_t *) data)[1]);
> 
> ::: media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
> @@ +1179,2 @@
> >  //WebRTC::RTP Callback Implementation
> >  int WebrtcVideoConduit::SendPacket(int channel, const void* data, int len)
> 
> Add // Called on MSG thread
> 
> @@ +1193,4 @@
> >    }
> >  }
> >  
> >  int WebrtcVideoConduit::SendRTCPPacket(int channel, const void* data, int len)
> 
> Add // Called on webrtc Process thread
> 

   I would expect these two to be called on the same thread/s...

> ::: media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
> @@ +136,5 @@
> > +      conduit_->StopReceiving();
> > +    } else {
> > +      conduit_->SetTransmitterTransport(nullptr);
> > +      conduit_->StopTransmitting();
> > +    }
> 
> Should this be done before or after DetachMediaStream?

   I think before. I've changed it.
Attachment #8562423 - Attachment is obsolete: true
Comment on attachment 8563164 [details] [diff] [review]
(WIP) Use a single bidirectional media conduit that MediaPipelines can attach/detach at will

Review of attachment 8563164 [details] [diff] [review]:
-----------------------------------------------------------------

For some reason, mozreview won't let me put just this patch up for review, it insists on grabbing the parent changeset (part 2 of the renegotiation bug) no matter what revset syntax I use (but not the parent's parent, which is part 1 of the renegotiation bug).
Attachment #8563164 - Flags: review?(rjesup)
> > >  //WebRTC::RTP Callback Implementation
> > >  int WebrtcVideoConduit::SendPacket(int channel, const void* data, int len)
> > 
> > Add // Called on MSG thread
> > 

> > >  int WebrtcVideoConduit::SendRTCPPacket(int channel, const void* data, int len)
> > 
> > Add // Called on webrtc Process thread
> > 
> 
>    I would expect these two to be called on the same thread/s...

RTCP (at least periodic RTCP) is generated off the webrtc Process timer thread; in reality I suspect RTCP may also be generated directly on other threads such as MSG for things like NACK.  Might be worth checking (breakpoint it or print the tread ID), so we can annotate it correctly.
Comment on attachment 8563164 [details] [diff] [review]
(WIP) Use a single bidirectional media conduit that MediaPipelines can attach/detach at will

Review of attachment 8563164 [details] [diff] [review]:
-----------------------------------------------------------------

::: media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ +103,5 @@
> +  mPtrVoEVideoSync = nullptr;
> +  mPtrVoERTP_RTCP = nullptr;
> +  mPtrRTP = nullptr;
> +
> +  // only one opener can call Delete.  Have it be the last to close.

This comment might no longer be relevant

@@ +465,5 @@
>      CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__);
>      return kMediaConduitInvalidReceiveCodec;
>    }
>  
> +  condError = StartReceiving();

Maybe keep the comment

@@ +761,5 @@
> +MediaConduitErrorCode
> +WebrtcAudioConduit::StopReceiving()
> +{
> +  // Are we receiving already? If so, stop receiving and playout
> +  // since we can't apply new recv codec when the engine is playing.

This comment should likely move back to where this was copied from

@@ +842,4 @@
>    }
>  }
>  
> +// Called on WebRTC Process thread

See VideoConduit.cpp discussion; change to "called on webrtc Process thread and perhaps on other threads".  Audio is mostly just timed RR/SR RTCP's.

::: media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ +1199,4 @@
>    }
>  }
>  
> +// Called on webrtc Process thread

I realize I said Process thread, but I think this may also be called from other WebRTC code (NACK, etc), which means MSG or other random WebRTC threads.  So "called from multiple threads including Process"

::: media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ +1,2 @@
>  /* This Source Code Form is subject to the terms of the Mozilla Public
> + * mNumStreams = numStreams;

remove....

::: media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ +360,5 @@
> +                          " to level " << level <<
> +                          ". This requires re-creating the MediaPipeline.");
> +    // Since we do not support changing the conduit on a pre-existing
> +    // MediaPipeline (the conduit is where stuff like echo cancellation and RTCP
> +    // happens, which is determined by what tracks appear at the same level).

echo cancellation no longer is done at the conduit level; that's done in getUserMedia now.

::: media/webrtc/signaling/test/signaling_unittests.cpp
@@ +1429,5 @@
>      }
>  
>  
>      // Check feedback method for video
> +    if (flags & PIPELINE_VIDEO && !(flags & PIPELINE_SEND)) {

Add parens around bit ops!
Attachment #8563164 - Flags: review?(rjesup) → review+
Have incorporated feedback, and try looks great:

https://treeherder.mozilla.org/#/jobs?repo=try&revision=2c1f6d733751
Have done some testing on TSAN, and not seeing any new races or lock-order problems.
https://hg.mozilla.org/mozilla-central/rev/95b94b9901f1
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Flags: in-testsuite+
Resolution: --- → FIXED
Target Milestone: --- → mozilla38
You need to log in before you can comment on or make changes to this bug.