diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index 0461b044168..13b0a06a777 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -15,14 +15,20 @@ namespace dom { MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID) : mStream(aStream), mTrackID(aTrackID), mEnded(false), mEnabled(true) { - memset(&mID, 0, sizeof(mID)); nsresult rv; nsCOMPtr uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv); + + nsID uuid; + memset(&uuid, 0, sizeof(uuid)); if (uuidgen) { - uuidgen->GenerateUUIDInPlace(&mID); + uuidgen->GenerateUUIDInPlace(&uuid); } + + char chars[NSID_LENGTH]; + uuid.ToProvidedString(chars); + mID = NS_ConvertASCIItoUTF16(chars); } MediaStreamTrack::~MediaStreamTrack() @@ -38,11 +44,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) void -MediaStreamTrack::GetId(nsAString& aID) +MediaStreamTrack::GetId(nsAString& aID) const { - char chars[NSID_LENGTH]; - mID.ToProvidedString(chars); - aID = NS_ConvertASCIItoUTF16(chars); + aID = mID; } void diff --git a/dom/media/MediaStreamTrack.h b/dom/media/MediaStreamTrack.h index ae2e1a2ce50..393491b1ac1 100644 --- a/dom/media/MediaStreamTrack.h +++ b/dom/media/MediaStreamTrack.h @@ -44,7 +44,7 @@ public: // WebIDL virtual void GetKind(nsAString& aKind) = 0; - void GetId(nsAString& aID); + void GetId(nsAString& aID) const; void GetLabel(nsAString& aLabel) { aLabel.Truncate(); } bool Enabled() { return mEnabled; } void SetEnabled(bool aEnabled); @@ -53,12 +53,16 @@ public: // Notifications from the MediaStreamGraph void NotifyEnded() { mEnded = true; } + // Webrtc allows the remote side to name tracks whatever it wants, and we + // need to surface this to content. + void AssignId(const nsAString& aID) { mID = aID; } + protected: virtual ~MediaStreamTrack(); nsRefPtr mStream; TrackID mTrackID; - nsID mID; + nsString mID; bool mEnded; bool mEnabled; }; diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 527965ddac7..e60edc80f6f 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -132,12 +132,16 @@ skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabl [test_peerConnection_toJSON.html] skip-if = toolkit == 'gonk' # b2g (Bug 1059867) -# multistream is not ready quite yet (bug 1056650) -# [test_peerConnection_twoAudioStreams.html] -# [test_peerConnection_twoAudioVideoStreams.html] -# [test_peerConnection_twoAudioVideoStreamsCombined.html] -# [test_peerConnection_twoVideoStreams.html] -# [test_peerConnection_addSecondAudioStream.html] +[test_peerConnection_twoAudioStreams.html] +skip-if = toolkit == 'gonk' # b2g (Bug 1059867) +[test_peerConnection_twoAudioVideoStreams.html] +skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078) +[test_peerConnection_twoAudioVideoStreamsCombined.html] +skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078) +[test_peerConnection_twoVideoStreams.html] +skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078) +# Renegotiation is not yet supported (bug 1017888) +#[test_peerConnection_addSecondAudioStream.html] # Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases [test_zmedia_cleanup.html] diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index 68afae060d1..db83f8e643b 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -1689,7 +1689,7 @@ PeerConnectionWrapper.prototype = { } } - var element = createMediaElement(type, this.label + '_' + side); + var element = createMediaElement(type, this.label + '_' + side + this.streams.length); this.mediaCheckers.push(new MediaElementChecker(element)); element.mozSrcObject = stream; element.play(); @@ -2321,6 +2321,27 @@ PeerConnectionWrapper.prototype = { }, + checkMsids : function PCW_checkMsids() { + function _checkMsids(desc, streams, sdpLabel) { + streams.forEach(function(stream) { + stream.getTracks().forEach(function(track) { + // TODO(bug 1089798): Once DOMMediaStream has an id field, we + // should be verifying that the SDP contains + // a=msid: + ok(desc.sdp.match(new RegExp("a=msid:[^ ]+ " + track.id)), + sdpLabel + " SDP contains track id " + track.id ); + }); + }); + } + + _checkMsids(this.localDescription, + this._pc.getLocalStreams(), + "local"); + _checkMsids(this.remoteDescription, + this._pc.getRemoteStreams(), + "remote"); + }, + verifySdp : function PCW_verifySdp(desc, expectedType, offerConstraintsList, offerOptions, trickleIceCallback) { info("Examining this SessionDescription: " + JSON.stringify(desc)); diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js index d012635d5bf..b21ef1623a0 100644 --- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -492,6 +492,20 @@ var commandsPeerConnection = [ }); } ], + [ + 'PC_LOCAL_CHECK_MSID', + function (test) { + test.pcLocal.checkMsids(); + test.next(); + } + ], + [ + 'PC_REMOTE_CHECK_MSID', + function (test) { + test.pcRemote.checkMsids(); + test.next(); + } + ], [ 'PC_LOCAL_CHECK_STATS', function (test) { diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp index 8ba9acdef99..e26920fe7c4 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp @@ -1513,6 +1513,8 @@ JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr* parsedp) return NS_ERROR_INVALID_ARG; } + std::set trackIds; + for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { if (parsed->GetMediaSection(i).GetPort() == 0) { // Disabled, let this stuff slide. @@ -1554,6 +1556,26 @@ JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr* parsedp) return NS_ERROR_INVALID_ARG; } } + + std::string streamId; + std::string trackId; + nsresult rv = GetIdsFromMsid(*parsed, + parsed->GetMediaSection(i), + &streamId, + &trackId); + + if (NS_SUCCEEDED(rv)) { + if (trackIds.count(trackId)) { + JSEP_SET_ERROR("track id:" << trackId + << " appears in more than one m-section at level " << i); + return NS_ERROR_INVALID_ARG; + } + + trackIds.insert(trackId); + } else if (rv != NS_ERROR_NOT_AVAILABLE) { + // Error has already been set + return rv; + } } *parsedp = Move(parsed); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index ceaa74ecd99..403f74637cf 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -547,17 +547,13 @@ nsresult MediaPipelineTransmit::Init() { return MediaPipeline::Init(); } -void MediaPipelineTransmit::AttachToTrack(TrackID track_id) { - char track_id_string[11]; +void MediaPipelineTransmit::AttachToTrack(const std::string& track_id) { ASSERT_ON_THREAD(main_thread_); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id); - description_ = pc_ + "| "; description_ += conduit_->type() == MediaSessionConduit::AUDIO ? "Transmit audio[" : "Transmit video["; - description_ += track_id_string; + description_ += track_id; description_ += "]"; // TODO(ekr@rtfm.com): Check for errors @@ -613,10 +609,11 @@ nsresult MediaPipelineTransmit::TransportReady_s(TransportInfo &info) { } nsresult MediaPipelineTransmit::ReplaceTrack(DOMMediaStream *domstream, - TrackID track_id) { + const std::string& track_id) { // MainThread, checked in calls we make - MOZ_MTLOG(ML_DEBUG, "Reattaching pipeline to stream " - << static_cast(domstream->GetStream()) << " conduit type=" << + MOZ_MTLOG(ML_DEBUG, "Reattaching pipeline " << description_ << " to stream " + << static_cast(domstream->GetStream()) + << " track " << track_id << " conduit type=" << (conduit_->type() == MediaSessionConduit::AUDIO ?"audio":"video")); if (domstream_) { // may be excessive paranoia @@ -624,7 +621,7 @@ nsresult MediaPipelineTransmit::ReplaceTrack(DOMMediaStream *domstream, } domstream_ = domstream; // Detach clears it stream_ = domstream->GetStream(); - //track_id_ = track_id; not threadsafe to change this; and we don't need to + track_id_ = track_id; AttachToTrack(track_id); return NS_OK; } @@ -1165,15 +1162,11 @@ void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk( #endif nsresult MediaPipelineReceiveAudio::Init() { - char track_id_string[11]; ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(ML_DEBUG, __FUNCTION__); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_); - description_ = pc_ + "| Receive audio["; - description_ += track_id_string; + description_ += track_id_; description_ += "]"; listener_->AddSelf(new AudioSegment()); @@ -1336,15 +1329,11 @@ NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) { } nsresult MediaPipelineReceiveVideo::Init() { - char track_id_string[11]; ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(ML_DEBUG, __FUNCTION__); - // We can replace this when we are allowed to do streams or std::to_string - PR_snprintf(track_id_string, sizeof(track_id_string), "%d", track_id_); - description_ = pc_ + "| Receive video["; - description_ += track_id_string; + description_ += track_id_; description_ += "]"; #ifdef MOZILLA_INTERNAL_API diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index c190accfd0e..660b2ca1580 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -77,7 +77,7 @@ class MediaPipeline : public sigslot::has_slots<> { nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + const std::string& track_id, int level, RefPtr conduit, RefPtr rtp_transport, @@ -130,9 +130,9 @@ class MediaPipeline : public sigslot::has_slots<> { nsAutoPtr filter); virtual Direction direction() const { return direction_; } - virtual TrackID trackid() const { return track_id_; } + virtual const std::string& trackid() const { return track_id_; } virtual int level() const { return level_; } - virtual bool IsVideo() const { return false; } + virtual bool IsVideo() const = 0; bool IsDoingRtcpMux() const { return (rtp_.type_ == MUX); @@ -233,10 +233,10 @@ class MediaPipeline : public sigslot::has_slots<> { // Written on the main thread. // Used on STS and MediaStreamGraph threads. // May be changed by rtpSender.replaceTrack() - TrackID track_id_; // The track on the stream. + std::string track_id_; // The track on the stream. // Written and used as with the stream_; // Not used outside initialization in MediaPipelineTransmit - int level_; // The m-line index (starting at 1, to match convention) + int level_; // The m-line index (starting at 0, to match convention) RefPtr conduit_; // Our conduit. Written on the main // thread. Read on STS thread. @@ -352,6 +352,7 @@ public: nsCOMPtr main_thread, nsCOMPtr sts_thread, DOMMediaStream *domstream, + const std::string& track_id, int level, bool is_video, RefPtr conduit, @@ -359,7 +360,7 @@ public: RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, - domstream->GetStream(), TRACK_INVALID, level, + domstream->GetStream(), track_id, level, conduit, rtp_transport, rtcp_transport, filter), listener_(new PipelineListener(conduit)), domstream_(domstream), @@ -369,7 +370,7 @@ public: // Initialize (stuff here may fail) virtual nsresult Init() MOZ_OVERRIDE; - virtual void AttachToTrack(TrackID track_id); + virtual void AttachToTrack(const std::string& track_id); // Index used to refer to this before we know the TrackID // Note: unlike MediaPipeline::trackid(), this is threadsafe @@ -403,7 +404,7 @@ public: // track to be part of a different stream (since we don't support // multiple tracks of a type in a stream yet). bug 1056650 virtual nsresult ReplaceTrack(DOMMediaStream *domstream, - TrackID track_id); + const std::string& track_id); // Separate class to allow ref counting @@ -518,7 +519,7 @@ class MediaPipelineReceive : public MediaPipeline { nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + const std::string& track_id, int level, RefPtr conduit, RefPtr rtp_transport, @@ -547,17 +548,23 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + // This comes from an msid attribute. Everywhere + // but MediaStreamGraph uses this. + const std::string& media_stream_track_id, + // This is an integer identifier that is only + // unique within a single DOMMediaStream, which is + // used by MediaStreamGraph + TrackID numeric_track_id, int level, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipelineReceive(pc, main_thread, sts_thread, - stream, track_id, level, conduit, rtp_transport, - rtcp_transport, filter), + stream, media_stream_track_id, level, conduit, + rtp_transport, rtcp_transport, filter), listener_(new PipelineListener(stream->AsSourceStream(), - track_id, conduit)) { + numeric_track_id, conduit)) { } virtual void DetachMediaStream() MOZ_OVERRIDE { @@ -568,6 +575,7 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { } virtual nsresult Init() MOZ_OVERRIDE; + virtual bool IsVideo() const MOZ_OVERRIDE { return false; } private: // Separate class to allow ref counting @@ -610,17 +618,24 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive { nsCOMPtr main_thread, nsCOMPtr sts_thread, MediaStream *stream, - TrackID track_id, + // This comes from an msid attribute. Everywhere + // but MediaStreamGraph uses this. + const std::string& media_stream_track_id, + // This is an integer identifier that is only + // unique within a single DOMMediaStream, which is + // used by MediaStreamGraph + TrackID numeric_track_id, int level, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport, nsAutoPtr filter) : MediaPipelineReceive(pc, main_thread, sts_thread, - stream, track_id, level, conduit, rtp_transport, - rtcp_transport, filter), + stream, media_stream_track_id, level, conduit, + rtp_transport, rtcp_transport, filter), renderer_(new PipelineRenderer(this)), - listener_(new PipelineListener(stream->AsSourceStream(), track_id)) { + listener_(new PipelineListener(stream->AsSourceStream(), + numeric_track_id)) { } // Called on the main thread. @@ -638,6 +653,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive { } virtual nsresult Init() MOZ_OVERRIDE; + virtual bool IsVideo() const MOZ_OVERRIDE { return true; } private: class PipelineRenderer : public VideoRenderer { diff --git a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp index 097332df3f9..1bea2677724 100644 --- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp +++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp @@ -16,6 +16,7 @@ #include "signaling/src/jsep/JsepTransport.h" #ifdef MOZILLA_INTERNAL_API +#include "MediaStreamTrack.h" #include "nsIPrincipal.h" #include "nsIDocument.h" #include "mozilla/Preferences.h" @@ -338,14 +339,18 @@ MediaPipelineFactory::CreateMediaPipelineReceiving( } } + // We need to choose a numeric track id for MediaStreamGraph to use. Must be + // unique within the MediaStream, so level + 1 should be fine (cannot use 0). + TrackID numericTrackId = aTrackPair.mLevel + 1; + if (aTrack.GetMediaType() == SdpMediaSection::kAudio) { pipeline = new MediaPipelineReceiveAudio( mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetStream(), - // Use the level + 1 as the track id. 0 is forbidden - aTrackPair.mLevel + 1, + aTrack.GetTrackId(), + numericTrackId, aTrackPair.mLevel, static_cast(aConduit.get()), // Ugly downcast. aRtpFlow, @@ -358,8 +363,8 @@ MediaPipelineFactory::CreateMediaPipelineReceiving( mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetStream(), - // Use the level + 1 as the track id. 0 is forbidden - aTrackPair.mLevel + 1, + aTrack.GetTrackId(), + numericTrackId, aTrackPair.mLevel, static_cast(aConduit.get()), // Ugly downcast. aRtpFlow, @@ -377,7 +382,15 @@ MediaPipelineFactory::CreateMediaPipelineReceiving( return rv; } - stream->StorePipeline(aTrackPair.mLevel, SdpMediaSection::kVideo, pipeline); + rv = stream->StorePipeline(aTrack.GetTrackId(), + RefPtr(pipeline)); + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " << + static_cast(rv)); + return rv; + } + + stream->SyncPipeline(pipeline); return NS_OK; } @@ -415,10 +428,17 @@ MediaPipelineFactory::CreateMediaPipelineSending( // Now we have all the pieces, create the pipeline RefPtr pipeline = new MediaPipelineTransmit( - mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), - stream->GetMediaStream(), aTrackPair.mLevel, - aTrack.GetMediaType() == SdpMediaSection::kVideo, aConduit, aRtpFlow, - aRtcpFlow, filter); + mPC->GetHandle(), + mPC->GetMainThread().get(), + mPC->GetSTSThread(), + stream->GetMediaStream(), + aTrack.GetTrackId(), + aTrackPair.mLevel, + aTrack.GetMediaType() == SdpMediaSection::kVideo, + aConduit, + aRtpFlow, + aRtcpFlow, + filter); #ifdef MOZILLA_INTERNAL_API // implement checking for peerIdentity (where failure == black/silence) @@ -438,7 +458,13 @@ MediaPipelineFactory::CreateMediaPipelineSending( return rv; } - stream->StorePipeline(aTrackPair.mLevel, pipeline); + rv = stream->StorePipeline(aTrack.GetTrackId(), + RefPtr(pipeline)); + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " << + static_cast(rv)); + return rv; + } return NS_OK; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 567f94e2110..653d85570ae 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -178,10 +178,18 @@ private: class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback { public: - TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints, + TracksAvailableCallback(const std::list& audioTrackIds, + const std::list& videoTrackIds, + const std::set& preexistingTrackIds, nsRefPtr aObserver) - : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints) - , mObserver(aObserver) {} + : DOMMediaStream::OnTracksAvailableCallback( + // Once DOMMediaStream can handle more than one of each, this will change. + (audioTrackIds.empty() ? 0 : DOMMediaStream::HINT_CONTENTS_AUDIO) | + (videoTrackIds.empty() ? 0 : DOMMediaStream::HINT_CONTENTS_VIDEO)) + , mObserver(aObserver) + , mAudioTrackIds(audioTrackIds) + , mVideoTrackIds(videoTrackIds) + , mPreexistingTrackIds(preexistingTrackIds) {} virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE { @@ -197,24 +205,68 @@ public: nsTArray> tracks; aStream->GetTracks(tracks); - for (uint32_t i = 0; i < tracks.Length(); i++) { + + for (size_t i = 0; i < tracks.Length(); i++) { + if (mPreexistingTrackIds.count( + PeerConnectionImpl::GetTrackId(*tracks[i]))) { + continue; + } + + AssignNextIdToTrack(tracks[i]); JSErrorResult rv; mObserver->OnAddTrack(*tracks[i], rv); - CSFLogInfo(logTag, "Calling OnAddTrack"); + CSFLogInfo(logTag, "Calling OnAddTrack(%s)", + PeerConnectionImpl::GetTrackId(*tracks[i]).c_str()); if (rv.Failed()) { - CSFLogError(logTag, ": OnAddTrack(%d) failed! Error: %u", i, - static_cast(rv.ErrorCode())); + CSFLogError(logTag, ": OnAddTrack(%u) failed! Error: %u", + static_cast(i), + static_cast(rv.ErrorCode())); } } + JSErrorResult rv; CSFLogInfo(logTag, "Calling OnAddStream"); mObserver->OnAddStream(*aStream, rv); if (rv.Failed()) { - CSFLogError(logTag, ": OnAddStream() failed! Error: %u", static_cast(rv.ErrorCode())); + CSFLogError(logTag, ": OnAddStream() failed! Error: %u", + static_cast(rv.ErrorCode())); + } + + if (!mAudioTrackIds.empty() || !mVideoTrackIds.empty()) { + CSFLogError(logTag, "Failed to assign %u audio and %u video tracks!", + static_cast(mAudioTrackIds.size()), + static_cast(mVideoTrackIds.size())); } } + + void AssignNextIdToTrack(MediaStreamTrack* track) + { + std::list* trackIds; + + if (track->AsAudioStreamTrack()) { + trackIds = &mAudioTrackIds; + } else if (track->AsVideoStreamTrack()) { + trackIds = &mVideoTrackIds; + } else { + MOZ_ASSERT(false, "Track is neither an AudioStreamTrack nor " + "VideoStreamTrack"); + return; + } + + if (trackIds->empty()) { + MOZ_ASSERT(false, "Too many new MediaStreamTracks were created"); + return; + } + + track->AssignId(NS_ConvertUTF8toUTF16(trackIds->front().c_str())); + trackIds->pop_front(); + } + private: nsRefPtr mObserver; + std::list mAudioTrackIds; + std::list mVideoTrackIds; + const std::set mPreexistingTrackIds; }; #endif @@ -1594,15 +1646,11 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) } else { // Add the tracks. This code is pretty complicated because the tracks // come in arbitrary orders and we want to group them by streamId. - // We go through all the tracks and then for each track that represents - // a new stream id, go through the rest of the tracks and deal with - // them at once. size_t numTracks = mJsepSession->GetRemoteTrackCount(); - MOZ_ASSERT(numTracks <= 3); - bool hasAudio = false; - bool hasVideo = false; - std::set streamsToNotify; + // Group tracks by stream id + std::map>> tracksByStreamId; + for (size_t i = 0; i < numTracks; ++i) { RefPtr track; nrv = mJsepSession->GetRemoteTrack(i, &track); @@ -1618,10 +1666,17 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) continue; } + tracksByStreamId[track->GetStreamId()].push_back(track); + } + + for (auto i = tracksByStreamId.begin(); i != tracksByStreamId.end(); ++i) { + std::string streamId = i->first; + std::vector>& tracks = i->second; + nsRefPtr info = - mMedia->GetRemoteStreamById(track->GetStreamId()); + mMedia->GetRemoteStreamById(streamId); if (!info) { - nsresult nrv = CreateRemoteSourceStreamInfo(&info, track->GetStreamId()); + nsresult nrv = CreateRemoteSourceStreamInfo(&info, streamId); if (NS_FAILED(nrv)) { pco->OnSetRemoteDescriptionError( kInternalError, @@ -1640,31 +1695,35 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) } } - streamsToNotify.insert(track->GetStreamId()); + // TODO(bug 1017888): Only get new tracks for renegotiation + std::list newAudioTrackIds; + std::list newVideoTrackIds; + // TODO(bug 1017888): Fill in for renegotiation + std::set preexistingTrackIds; + + for (auto j = tracks.begin(); j != tracks.end(); ++j) { + RefPtr track = *j; + if (track->GetMediaType() == SdpMediaSection::kAudio) { + info->AddTrack(track->GetTrackId()); + newAudioTrackIds.push_back(track->GetTrackId()); + } else if (track->GetMediaType() == SdpMediaSection::kVideo) { + info->AddTrack(track->GetTrackId()); + newVideoTrackIds.push_back(track->GetTrackId()); + } else { + MOZ_ASSERT(false); + continue; + } - if (track->GetMediaType() == mozilla::SdpMediaSection::kAudio) { - MOZ_ASSERT(!hasAudio); - (void)hasAudio; - hasAudio = true; - info->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; - } else if (track->GetMediaType() == mozilla::SdpMediaSection::kVideo) { - MOZ_ASSERT(!hasVideo); - (void)hasVideo; - hasVideo = true; - info->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; } - } - for (auto i = streamsToNotify.begin(); i != streamsToNotify.end(); ++i) { // Now that the streams are all set up, notify about track availability. // TODO(bug 1017888): Suppress on renegotiation when no change. - nsRefPtr info = - mMedia->GetRemoteStreamById(*i); - MOZ_ASSERT(info); - #ifdef MOZILLA_INTERNAL_API TracksAvailableCallback* tracksAvailableCallback = - new TracksAvailableCallback(info->mTrackTypeHints, pco); + new TracksAvailableCallback(newAudioTrackIds, + newVideoTrackIds, + preexistingTrackIds, + pco); info->GetMediaStream()->OnTracksAvailable(tracksAvailableCallback); #else pco->OnAddStream(info->GetMediaStream(), jrv); @@ -1875,6 +1934,18 @@ PeerConnectionImpl::PrincipalChanged(DOMMediaStream* aMediaStream) { } #endif +std::string +PeerConnectionImpl::GetTrackId(const MediaStreamTrack& aTrack) +{ +#ifdef MOZILLA_INTERNAL_API + nsString wideTrackId; + aTrack.GetId(wideTrackId); + return NS_ConvertUTF16toUTF8(wideTrackId).get(); +#else + return aTrack.GetId(); +#endif +} + nsresult PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack, const Sequence>& aStreams) @@ -1896,48 +1967,28 @@ PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack, CSFLogError(logTag, "%s: Track is not in stream", __FUNCTION__); return NS_ERROR_FAILURE; } - uint32_t hints = aMediaStream.GetHintContents() & - ((aTrack.AsAudioStreamTrack()? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) | - (aTrack.AsVideoStreamTrack()? DOMMediaStream::HINT_CONTENTS_VIDEO : 0)); - - // XXX Remove this check once addStream has an error callback - // available and/or we have plumbing to handle multiple - // local audio streams. bug 1056650 - if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && - mNumAudioStreams > 0) { - CSFLogError(logTag, "%s: Only one local audio stream is supported for now", - __FUNCTION__); - return NS_ERROR_FAILURE; - } - - // XXX Remove this check once addStream has an error callback - // available and/or we have plumbing to handle multiple - // local video streams. bug 1056650 - if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && - mNumVideoStreams > 0) { - CSFLogError(logTag, "%s: Only one local video stream is supported for now", - __FUNCTION__); - return NS_ERROR_FAILURE; - } - uint32_t num = mMedia->LocalStreamsLength(); std::string streamId; - // TODO(bug 1089798): These ids should really come from the MS. - nsresult res = mMedia->AddStream(&aMediaStream, hints, &streamId); + std::string trackId = PeerConnectionImpl::GetTrackId(aTrack); + // TODO(bug 1089798): streamId should really come from the MS. + nsresult res = mMedia->AddTrack(&aMediaStream, &streamId, trackId); if (NS_FAILED(res)) { return res; } + CSFLogDebug(logTag, "Added track (%s) to stream %p", + trackId.c_str(), &aMediaStream); + if (num != mMedia->LocalStreamsLength()) { aMediaStream.AddPrincipalChangeObserver(this); } - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { + if (aTrack.AsAudioStreamTrack()) { res = mJsepSession->AddTrack(new JsepTrack( mozilla::SdpMediaSection::kAudio, streamId, - "audio_track_id", + trackId, JsepTrack::kJsepTrackSending)); if (NS_FAILED(res)) { std::string errorString = mJsepSession->GetLastError(); @@ -1948,11 +1999,19 @@ PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack, mNumAudioStreams++; } - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { + if (aTrack.AsVideoStreamTrack()) { +#ifdef MOZILLA_INTERNAL_API + if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { + // Before this code was moved, this would silently ignore just like it + // does now. Is this actually what we want to do? + return NS_OK; + } +#endif + res = mJsepSession->AddTrack(new JsepTrack( mozilla::SdpMediaSection::kVideo, streamId, - "video_track_id", + trackId, JsepTrack::kJsepTrackSending)); if (NS_FAILED(res)) { std::string errorString = mJsepSession->GetLastError(); @@ -1977,6 +2036,15 @@ PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack, DOMMediaStream& aStream) { PC_AUTO_ENTER_API_CALL(true); + JSErrorResult jrv; + nsRefPtr pco = do_QueryObjectReferent(mPCObserver); + if (!pco) { + return NS_ERROR_UNEXPECTED; + } + + std::string origTrackId = PeerConnectionImpl::GetTrackId(aThisTrack); + std::string newTrackId = PeerConnectionImpl::GetTrackId(aWithTrack); + // TODO: Do an aStream.HasTrack() check on both track args someday. // // The proposed API will be that both tracks must already be in the same @@ -1984,9 +2052,15 @@ PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack, // track per type, we allow replacement with an outside track not already // in the same stream. This works because sync happens receiver-side and // timestamps are tied to capture. - // - // Since a track may be replaced more than once, the track being replaced - // may not be in the stream either, so we check neither arg right now. + + if (!aStream.HasTrack(aThisTrack)) { + CSFLogError(logTag, "Track to replace (%s) is not in stream", + origTrackId.c_str()); + pco->OnReplaceTrackError(kInvalidMediastreamTrack, + ObString("Track to replace is not in stream"), + jrv); + return NS_OK; + } // XXX This MUST be addressed when we add multiple tracks of a type!! // This is needed because the track IDs used by MSG are from TrackUnion @@ -1997,41 +2071,38 @@ PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack, // TrackUnionStream's TrackID's, this value won't currently match what is used in // MediaPipelineTransmit. Bug 1056652 // TrackID thisID = aThisTrack.GetTrackID(); - TrackID withID = aWithTrack.GetTrackID(); + // - bool success = false; - for(uint32_t i = 0; i < media()->LocalStreamsLength(); ++i) { - LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i); - // XXX use type instead of TrackID - bug 1056650 - int pipeline = info->HasTrackType(&aStream, !!(aThisTrack.AsVideoStreamTrack())); - if (pipeline >= 0) { - // XXX GetStream() will likely be invalid once a track can be in more than one - info->ReplaceTrack(pipeline, aWithTrack.GetStream(), withID); - success = true; - break; - } - } - if (!success) { - return NS_ERROR_FAILURE; + nsRefPtr info = + media()->GetLocalStreamByDomStream(aStream); + + if (!info || !info->HasTrack(origTrackId)) { + CSFLogError(logTag, "Track to replace (%s) was never added", + origTrackId.c_str()); + pco->OnReplaceTrackError(kInvalidMediastreamTrack, + ObString("Track to replace was never added"), + jrv); + return NS_OK; } - nsRefPtr pco = do_QueryObjectReferent(mPCObserver); - if (!pco) { - return NS_ERROR_UNEXPECTED; - } - JSErrorResult rv; - - if (success) { - pco->OnReplaceTrackSuccess(rv); - } else { + nsresult rv = + info->ReplaceTrack(origTrackId, aWithTrack.GetStream(), newTrackId); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "Failed to replace track (%s)", + origTrackId.c_str()); pco->OnReplaceTrackError(kInternalError, ObString("Failed to replace track"), - rv); + jrv); + return NS_OK; } - if (rv.Failed()) { + + pco->OnReplaceTrackSuccess(jrv); + + if (jrv.Failed()) { CSFLogError(logTag, "Error firing replaceTrack callback"); return NS_ERROR_UNEXPECTED; } + return NS_OK; } @@ -2676,16 +2747,19 @@ PeerConnectionImpl::BuildStatsQuery_m( // Gather up pipelines from mMedia so they may be inspected on STS + std::string trackId; + if (aSelector) { + trackId = PeerConnectionImpl::GetTrackId(*aSelector); + } + for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { auto& pipelines = mMedia->GetLocalStreamByIndex(i)->GetPipelines(); if (aSelector) { if (mMedia->GetLocalStreamByIndex(i)->GetMediaStream()-> HasTrack(*aSelector)) { - // XXX use type instead of TrackID - bug 1056650 - for (auto it = pipelines.begin(); it != pipelines.end(); ++it) { - if (it->second->IsVideo() == !!aSelector->AsVideoStreamTrack()) { - query->pipelines.AppendElement(it->second); - } + auto it = pipelines.find(trackId); + if (it != pipelines.end()) { + query->pipelines.AppendElement(it->second); } } } else { @@ -2700,10 +2774,9 @@ PeerConnectionImpl::BuildStatsQuery_m( if (aSelector) { if (mMedia->GetRemoteStreamByIndex(i)-> GetMediaStream()->HasTrack(*aSelector)) { - for (auto it = pipelines.begin(); it != pipelines.end(); ++it) { - if (it->second->trackid() == aSelector->GetTrackID()) { - query->pipelines.AppendElement(it->second); - } + auto it = pipelines.find(trackId); + if (it != pipelines.end()) { + query->pipelines.AppendElement(it->second); } } } else { @@ -2844,7 +2917,7 @@ PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) { NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video"); nsString idstr = mediaType; idstr.AppendLiteral("_"); - idstr.AppendInt(mp.trackid()); + idstr.AppendInt(mp.level()); // Gather pipeline stats. switch (mp.direction()) { diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 5ddad70707e..57ba1557d2c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -601,6 +601,8 @@ public: virtual void PrincipalChanged(DOMMediaStream* aMediaStream) MOZ_OVERRIDE; #endif + static std::string GetTrackId(const dom::MediaStreamTrack& track); + private: virtual ~PeerConnectionImpl(); PeerConnectionImpl(const PeerConnectionImpl&rhs); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index 547db205285..d2d62977983 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -43,144 +43,46 @@ using namespace mozilla::dom; namespace mozilla { static const char* logTag = "PeerConnectionMedia"; -static const mozilla::TrackID TRACK_AUDIO = 0; -static const mozilla::TrackID TRACK_VIDEO = 1; -/* If the ExpectAudio hint is on we will add a track at the default first - * audio track ID (0) - * FIX - Do we need to iterate over the tracks instead of taking these hints? - */ -void -LocalSourceStreamInfo::ExpectAudio(const mozilla::TrackID aID) -{ - mAudioTracks.AppendElement(aID); -} - -void -LocalSourceStreamInfo::RemoveAudio(const mozilla::TrackID aID) -{ - mAudioTracks.RemoveElement(aID); -} - -// If the ExpectVideo hint is on we will add a track at the default first -// video track ID (1). -void -LocalSourceStreamInfo::ExpectVideo(const mozilla::TrackID aID) -{ - mVideoTracks.AppendElement(aID); -} - -void -LocalSourceStreamInfo::RemoveVideo(const mozilla::TrackID aID) -{ - mVideoTracks.RemoveElement(aID); -} - -unsigned -LocalSourceStreamInfo::AudioTrackCount() -{ - return mAudioTracks.Length(); -} - -unsigned -LocalSourceStreamInfo::VideoTrackCount() -{ - return mVideoTracks.Length(); -} - -void LocalSourceStreamInfo::DetachTransport_s() -{ - ASSERT_ON_THREAD(mParent->GetSTSThread()); - // walk through all the MediaPipelines and call the shutdown - // functions for transport. Must be on the STS thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { - it->second->ShutdownTransport_s(); - } -} - -void LocalSourceStreamInfo::DetachMedia_m() -{ - ASSERT_ON_THREAD(mParent->GetMainThread()); - // walk through all the MediaPipelines and call the shutdown - // functions. Must be on the main thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { - it->second->ShutdownMedia_m(); - } - mAudioTracks.Clear(); - mVideoTracks.Clear(); - mMediaStream = nullptr; -} - -#if 0 -// XXX bug 1056652 makes this not very useful for transmit streams -// NOTE: index is != the trackid in the MediaStream -int LocalSourceStreamInfo::HasTrack(DOMMediaStream* aStream, TrackID aTrack) -{ - if (aStream != mMediaStream) { - return -1; - } - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->trackid_locked() == aTrack) { - return it->first; - } - } - return -1; -} -#endif - -// NOTE: index is != the trackid in the MediaStream -int LocalSourceStreamInfo::HasTrackType(DOMMediaStream* aStream, bool aIsVideo) -{ - if (aStream != mMediaStream) { - return -1; - } - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->IsVideo() == aIsVideo) { - return it->first; - } - } - return -1; -} - -// XXX revisit once we support multiple tracks of a type - bug 1056650 -nsresult LocalSourceStreamInfo::ReplaceTrack(int aMLine, +nsresult LocalSourceStreamInfo::ReplaceTrack(const std::string& oldTrackId, DOMMediaStream* aNewStream, - TrackID aNewTrack) + const std::string& newTrackId) { - // Note aMLine != aOldTrack! - mozilla::RefPtr pipeline = mPipelines[aMLine]; - MOZ_ASSERT(pipeline); - if (NS_SUCCEEDED(static_cast(pipeline.get())->ReplaceTrack(aNewStream, aNewTrack))) { - return NS_OK; + mozilla::RefPtr pipeline = mPipelines[oldTrackId]; + + if (!pipeline || !mTracks.count(oldTrackId)) { + CSFLogError(logTag, "Failed to find track id %s", oldTrackId.c_str()); + return NS_ERROR_NOT_AVAILABLE; } - return NS_ERROR_FAILURE; + + nsresult rv = + static_cast(pipeline.get())->ReplaceTrack( + aNewStream, newTrackId); + NS_ENSURE_SUCCESS(rv, rv); + + mTracks.erase(oldTrackId); + mTracks.insert(newTrackId); + + return NS_OK; } -void RemoteSourceStreamInfo::DetachTransport_s() +void SourceStreamInfo::DetachTransport_s() { ASSERT_ON_THREAD(mParent->GetSTSThread()); // walk through all the MediaPipelines and call the shutdown // transport functions. Must be on the STS thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { + for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownTransport_s(); } } -void RemoteSourceStreamInfo::DetachMedia_m() +void SourceStreamInfo::DetachMedia_m() { ASSERT_ON_THREAD(mParent->GetMainThread()); // walk through all the MediaPipelines and call the shutdown // media functions. Must be on the main thread. - for (std::map >::iterator it = - mPipelines.begin(); it != mPipelines.end(); - ++it) { + for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownMedia_m(); } mMediaStream = nullptr; @@ -663,9 +565,9 @@ PeerConnectionMedia::UpdateIceMediaStream_s(size_t aMLine, } nsresult -PeerConnectionMedia::AddStream(DOMMediaStream* aMediaStream, - uint32_t hints, - std::string *stream_id) +PeerConnectionMedia::AddTrack(DOMMediaStream* aMediaStream, + std::string* streamId, + const std::string& trackId) { ASSERT_ON_THREAD(mMainThread); @@ -676,38 +578,9 @@ PeerConnectionMedia::AddStream(DOMMediaStream* aMediaStream, CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream); - // Adding tracks here based on nsDOMMediaStream expectation settings -#ifdef MOZILLA_INTERNAL_API - if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { - hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO); - } -#endif + nsRefPtr localSourceStream = + GetLocalStreamByDomStream(*aMediaStream); - if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO | - DOMMediaStream::HINT_CONTENTS_VIDEO))) { - CSFLogDebug(logTag, "Empty Stream !!"); - return NS_OK; - } - - // Now see if we already have this stream or another stream with - // tracks of the same type, since we only allow one track of each type. - // TODO(ekr@rtfm.com): remove this when multiple of each stream - // is allowed bug 1056650 - nsRefPtr localSourceStream = nullptr; - - for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { - auto& lss = mLocalSourceStreams[u]; - if (((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && lss->AudioTrackCount()) || - ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && lss->VideoTrackCount())) { - CSFLogError(logTag, "Only one stream of any given type allowed"); - return NS_ERROR_FAILURE; - } - if (aMediaStream == lss->GetMediaStream()) { - localSourceStream = lss; - *stream_id = lss->GetId(); - break; - } - } if (!localSourceStream) { std::string id; if (!mUuidGen->Generate(&id)) { @@ -717,50 +590,38 @@ PeerConnectionMedia::AddStream(DOMMediaStream* aMediaStream, localSourceStream = new LocalSourceStreamInfo(aMediaStream, this, id); mLocalSourceStreams.AppendElement(localSourceStream); - *stream_id = id; } - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { - localSourceStream->ExpectAudio(TRACK_AUDIO); - } - - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { - localSourceStream->ExpectVideo(TRACK_VIDEO); - } + localSourceStream->AddTrack(trackId); + *streamId = localSourceStream->GetId(); return NS_OK; } nsresult -PeerConnectionMedia::RemoveStream(DOMMediaStream* aMediaStream, - uint32_t hints, - uint32_t *stream_id) +PeerConnectionMedia::RemoveTrack(DOMMediaStream* aMediaStream, + const std::string& trackId) { MOZ_ASSERT(aMediaStream); ASSERT_ON_THREAD(mMainThread); - CSFLogDebug(logTag, "%s: MediaStream: %p", - __FUNCTION__, aMediaStream); + CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream); - for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { - nsRefPtr localSourceStream = mLocalSourceStreams[u]; - if (localSourceStream->GetMediaStream() == aMediaStream) { - *stream_id = u; - - if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { - localSourceStream->RemoveAudio(TRACK_AUDIO); - } - if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { - localSourceStream->RemoveAudio(TRACK_VIDEO); - } - if (!(localSourceStream->AudioTrackCount() + - localSourceStream->VideoTrackCount())) { - mLocalSourceStreams.RemoveElementAt(u); - } - return NS_OK; + size_t i; + for (i = 0; i < mLocalSourceStreams.Length(); ++i) { + if (mLocalSourceStreams[i]->GetMediaStream() == aMediaStream) { + break; } } - return NS_ERROR_ILLEGAL_VALUE; + if (i == mLocalSourceStreams.Length()) { + return NS_ERROR_ILLEGAL_VALUE; + } + + mLocalSourceStreams[i]->RemoveTrack(trackId); + if (!(mLocalSourceStreams[i]->GetTrackCount())) { + mLocalSourceStreams.RemoveElementAt(i); + } + return NS_OK; } void @@ -860,6 +721,33 @@ PeerConnectionMedia::GetLocalStreamById(const std::string& id) return nullptr; } +LocalSourceStreamInfo* +PeerConnectionMedia::GetLocalStreamByDomStream(const DOMMediaStream& stream) +{ + ASSERT_ON_THREAD(mMainThread); + for (size_t i = 0; i < mLocalSourceStreams.Length(); ++i) { + if (&stream == mLocalSourceStreams[i]->GetMediaStream()) { + return mLocalSourceStreams[i]; + } + } + + return nullptr; +} + +RemoteSourceStreamInfo* +PeerConnectionMedia::GetRemoteStreamByDomStream(const DOMMediaStream& stream) +{ + ASSERT_ON_THREAD(mMainThread); + for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) { + if (&stream == mRemoteSourceStreams[i]->GetMediaStream()) { + return mRemoteSourceStreams[i]; + } + } + + MOZ_ASSERT(false); + return nullptr; +} + RemoteSourceStreamInfo* PeerConnectionMedia::GetRemoteStreamByIndex(size_t aIndex) { @@ -905,19 +793,19 @@ UpdateFilterFromRemoteDescription_s( bool PeerConnectionMedia::UpdateFilterFromRemoteDescription_m( - int aMLine, + const std::string& trackId, nsAutoPtr filter) { ASSERT_ON_THREAD(mMainThread); RefPtr receive; for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) { - receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(aMLine); + receive = mRemoteSourceStreams[i]->GetPipelineByTrackId_m(trackId); } RefPtr transmit; for (size_t i = 0; !transmit && i < mLocalSourceStreams.Length(); ++i) { - transmit = mLocalSourceStreams[i]->GetPipelineByLevel_m(aMLine); + transmit = mLocalSourceStreams[i]->GetPipelineByTrackId_m(trackId); } if (receive && transmit) { @@ -935,8 +823,8 @@ PeerConnectionMedia::UpdateFilterFromRemoteDescription_m( NS_DISPATCH_NORMAL); return true; } else { - CSFLogWarn(logTag, "Could not locate level %d to update filter", - static_cast(aMLine)); + CSFLogWarn(logTag, "Could not locate track %s to update filter", + trackId.c_str()); } return false; } @@ -951,27 +839,6 @@ PeerConnectionMedia::AddRemoteStream(nsRefPtr aInfo) return NS_OK; } -nsresult -PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo) -{ - if (aIndex < 0 || - static_cast(aIndex) >= mRemoteSourceStreams.Length()) { - return NS_ERROR_ILLEGAL_VALUE; - } - - RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex); - MOZ_ASSERT(pInfo); - - if (aIsVideo) { - pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; - } else { - pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; - } - - return NS_OK; -} - - void PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, NrIceCtx::GatheringState state) @@ -1235,7 +1102,7 @@ PeerConnectionMedia::AnyCodecHasPluginID(uint64_t aPluginID) } bool -LocalSourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) +SourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) { // Scan the videoConduits for this plugin ID for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { @@ -1246,70 +1113,50 @@ LocalSourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) return false; } -bool -RemoteSourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID) +nsresult +SourceStreamInfo::StorePipeline( + const std::string& trackId, + const mozilla::RefPtr& aPipeline) { - // Scan the videoConduits for this plugin ID - for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { - if (it->second->Conduit()->CodecPluginID() == aPluginID) { - return true; - } - } - return false; -} - -void -LocalSourceStreamInfo::StorePipeline( - int aMLine, mozilla::RefPtr aPipeline) -{ - MOZ_ASSERT(mPipelines.find(aMLine) == mPipelines.end()); - if (mPipelines.find(aMLine) != mPipelines.end()) { + MOZ_ASSERT(mPipelines.find(trackId) == mPipelines.end()); + if (mPipelines.find(trackId) != mPipelines.end()) { CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__); - return; + return NS_ERROR_FAILURE; } - //TODO: Revisit once we start supporting multiple streams or multiple tracks - // of same type bug 1056650 - mPipelines[aMLine] = aPipeline; + mPipelines[trackId] = aPipeline; + return NS_OK; } void -RemoteSourceStreamInfo::StorePipeline( - int aMLine, bool aIsVideo, +RemoteSourceStreamInfo::SyncPipeline( mozilla::RefPtr aPipeline) { - MOZ_ASSERT(mPipelines.find(aMLine) == mPipelines.end()); - if (mPipelines.find(aMLine) != mPipelines.end()) { - CSFLogError(logTag, "%s: Request to store duplicate track %d", __FUNCTION__, aMLine); - return; - } - CSFLogDebug(logTag, "%s track %d %s = %p", __FUNCTION__, aMLine, aIsVideo ? "video" : "audio", - aPipeline.get()); - // See if we have both audio and video here, and if so cross the streams and sync them - // XXX Needs to be adjusted when we support multiple streams of the same type bug 1056650 - for (std::map::iterator it = mTypes.begin(); it != mTypes.end(); ++it) { - if (it->second != aIsVideo) { + // See if we have both audio and video here, and if so cross the streams and + // sync them + // TODO: Do we need to prevent multiple syncs if there is more than one audio + // or video track in a single media stream? What are we supposed to do in this + // case? + for (auto i = mPipelines.begin(); i != mPipelines.end(); ++i) { + if (i->second->IsVideo() != aPipeline->IsVideo()) { // Ok, we have one video, one non-video - cross the streams! - mozilla::WebrtcAudioConduit *audio_conduit = static_cast - (aIsVideo ? - mPipelines[it->first]->Conduit() : - aPipeline->Conduit()); - mozilla::WebrtcVideoConduit *video_conduit = static_cast - (aIsVideo ? - aPipeline->Conduit() : - mPipelines[it->first]->Conduit()); + mozilla::WebrtcAudioConduit *audio_conduit = + static_cast(aPipeline->IsVideo() ? + i->second->Conduit() : + aPipeline->Conduit()); + mozilla::WebrtcVideoConduit *video_conduit = + static_cast(aPipeline->IsVideo() ? + aPipeline->Conduit() : + i->second->Conduit()); video_conduit->SyncTo(audio_conduit); - CSFLogDebug(logTag, "Syncing %p to %p, %d to %d", video_conduit, audio_conduit, - aMLine, it->first); + CSFLogDebug(logTag, "Syncing %p to %p, %s to %s", + video_conduit, audio_conduit, + i->first.c_str(), aPipeline->trackid().c_str()); } } - //TODO: Revisit once we start supporting multiple streams or multiple tracks - // of same type bug 1056650 - mPipelines[aMLine] = aPipeline; - //TODO: move to attribute on Pipeline - mTypes[aMLine] = aIsVideo; } -RefPtr SourceStreamInfo::GetPipelineByLevel_m(int aMLine) { +RefPtr SourceStreamInfo::GetPipelineByTrackId_m( + const std::string& trackId) { ASSERT_ON_THREAD(mParent->GetMainThread()); // Refuse to hand out references if we're tearing down. @@ -1318,10 +1165,8 @@ RefPtr SourceStreamInfo::GetPipelineByLevel_m(int aMLine) { // RefPtr, since that reference won't be the last one // standing) if (mMediaStream) { - for (auto p = mPipelines.begin(); p != mPipelines.end(); ++p) { - if (p->second->level() == aMLine) { - return p->second; - } + if (mPipelines.count(trackId)) { + return mPipelines[trackId]; } } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index b04ccd1fc2c..9a81f0cea6c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -198,18 +198,38 @@ public: return mMediaStream; } + nsresult StorePipeline( + const std::string& trackId, + const mozilla::RefPtr& aPipeline); + + void AddTrack(const std::string& trackId) { mTracks.insert(trackId); } + void RemoveTrack(const std::string& trackId) { mTracks.erase(trackId); } + bool HasTrack(const std::string& trackId) const + { + return !!mTracks.count(trackId); + } + size_t GetTrackCount() const { return mTracks.size(); } + // This method exists for stats and the unittests. // It allows visibility into the pipelines and flows. - const std::map>& + const std::map>& GetPipelines() const { return mPipelines; } - mozilla::RefPtr GetPipelineByLevel_m(int aMLine); + mozilla::RefPtr GetPipelineByTrackId_m( + const std::string& trackId); const std::string& GetId() const { return mId; } + void DetachTransport_s(); + void DetachMedia_m(); + bool AnyCodecHasPluginID(uint64_t aPluginID); protected: - std::map> mPipelines; nsRefPtr mMediaStream; PeerConnectionMedia *mParent; const std::string mId; + // These get set up before we generate our local description, the pipelines + // are set up once offer/answer completes. + std::set mTracks; + // Indexed by track id, might contain pipelines for removed tracks + std::map> mPipelines; }; // TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo @@ -224,40 +244,19 @@ public: const std::string& aId) : SourceStreamInfo(aMediaStream, aParent, aId) {} - // Returns the mPipelines index for the track or -1. -#if 0 - int HasTrack(DOMMediaStream* aStream, mozilla::TrackID aMLine); -#endif - int HasTrackType(DOMMediaStream* aStream, bool aIsVideo); // XXX NOTE: does not change mMediaStream, even if it replaces the last track // in a LocalSourceStreamInfo. Revise when we have support for multiple tracks // of a type. - // Note aMLine != aOldTrack! It's the result of HasTrackType() - nsresult ReplaceTrack(int aMLine, DOMMediaStream* aNewStream, mozilla::TrackID aNewTrack); - - void StorePipeline(int aMLine, - mozilla::RefPtr aPipeline); + nsresult ReplaceTrack(const std::string& oldTrackId, + DOMMediaStream* aNewStream, + const std::string& aNewTrack); #ifdef MOZILLA_INTERNAL_API void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal, const mozilla::PeerIdentity* aSinkIdentity); #endif - void ExpectAudio(const mozilla::TrackID); - void ExpectVideo(const mozilla::TrackID); - void RemoveAudio(const mozilla::TrackID); - void RemoveVideo(const mozilla::TrackID); - unsigned AudioTrackCount(); - unsigned VideoTrackCount(); - void DetachTransport_s(); - void DetachMedia_m(); - - bool AnyCodecHasPluginID(uint64_t aPluginID); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo) -private: - nsTArray mAudioTracks; - nsTArray mVideoTracks; }; class RemoteSourceStreamInfo : public SourceStreamInfo { @@ -266,28 +265,17 @@ class RemoteSourceStreamInfo : public SourceStreamInfo { RemoteSourceStreamInfo(already_AddRefed aMediaStream, PeerConnectionMedia *aParent, const std::string& aId) - : SourceStreamInfo(aMediaStream, aParent, aId), - mTrackTypeHints(0) { + : SourceStreamInfo(aMediaStream, aParent, aId) + { } - void StorePipeline(int aMLine, bool aIsVideo, - mozilla::RefPtr aPipeline); - - void DetachTransport_s(); - void DetachMedia_m(); + void SyncPipeline(RefPtr aPipeline); #ifdef MOZILLA_INTERNAL_API void UpdatePrincipal_m(nsIPrincipal* aPrincipal); #endif - bool AnyCodecHasPluginID(uint64_t aPluginID); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo) - -public: - DOMMediaStream::TrackTypeHints mTrackTypeHints; - private: - std::map mTypes; }; class PeerConnectionMedia : public sigslot::has_slots<> { @@ -333,14 +321,18 @@ class PeerConnectionMedia : public sigslot::has_slots<> { // Handle complete media pipelines. nsresult UpdateMediaPipelines(const mozilla::JsepSession& session); - // Add a stream (main thread only) - nsresult AddStream(DOMMediaStream* aMediaStream, uint32_t hints, - std::string* stream_id); + // Add a track (main thread only) + // TODO(bug 1089798): Once DOMMediaStream has an id field, use it instead of + // letting PCMedia choose a streamId + nsresult AddTrack(DOMMediaStream* aMediaStream, + std::string* streamId, + const std::string& trackId); - // Remove a stream (main thread only) - nsresult RemoveStream(DOMMediaStream* aMediaStream, - uint32_t hints, - uint32_t *stream_id); + // Remove a track (main thread only) + // TODO(bug 1089798): Once DOMMediaStream has an id field, use it instead of + // passing |aMediaStream| + nsresult RemoveTrack(DOMMediaStream* aMediaStream, + const std::string& trackId); // Get a specific local stream uint32_t LocalStreamsLength() @@ -349,6 +341,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> { } LocalSourceStreamInfo* GetLocalStreamByIndex(int index); LocalSourceStreamInfo* GetLocalStreamById(const std::string& id); + LocalSourceStreamInfo* GetLocalStreamByDomStream( + const DOMMediaStream& stream); // Get a specific remote stream uint32_t RemoteStreamsLength() @@ -358,14 +352,16 @@ class PeerConnectionMedia : public sigslot::has_slots<> { RemoteSourceStreamInfo* GetRemoteStreamByIndex(size_t index); RemoteSourceStreamInfo* GetRemoteStreamById(const std::string& id); + RemoteSourceStreamInfo* GetRemoteStreamByDomStream( + const DOMMediaStream& stream); + bool UpdateFilterFromRemoteDescription_m( - int aMLine, + const std::string& trackId, nsAutoPtr filter); // Add a remote stream. nsresult AddRemoteStream(nsRefPtr aInfo); - nsresult AddRemoteStreamHint(int aIndex, bool aIsVideo); #ifdef MOZILLA_INTERNAL_API // In cases where the peer isn't yet identified, we disable the pipeline (not diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp.h b/media/webrtc/signaling/src/sdp/sipcc/sdp.h index 7c72456e0e9..b6919e447a8 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp.h +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp.h @@ -556,7 +556,7 @@ typedef enum sdp_srtp_crypto_suite_t_ { /* Max number of stream ids that can be grouped together */ -#define SDP_MAX_MEDIA_STREAMS 10 +#define SDP_MAX_MEDIA_STREAMS 32 #define SDP_MAGIC_NUM 0xabcdabcd diff --git a/media/webrtc/signaling/test/FakeMediaStreams.h b/media/webrtc/signaling/test/FakeMediaStreams.h index a665914ded0..ff12c7597d3 100644 --- a/media/webrtc/signaling/test/FakeMediaStreams.h +++ b/media/webrtc/signaling/test/FakeMediaStreams.h @@ -6,6 +6,8 @@ #define FAKE_MEDIA_STREAM_H_ #include +#include +#include #include "nsNetCID.h" #include "nsITimer.h" @@ -13,6 +15,7 @@ #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsISupportsImpl.h" +#include "nsServiceManagerUtils.h" // #includes from MediaStream.h #include "mozilla/Mutex.h" @@ -220,8 +223,16 @@ class Fake_MediaStreamTrack public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrack) - explicit Fake_MediaStreamTrack(bool aIsVideo) : mIsVideo (aIsVideo) {} + explicit Fake_MediaStreamTrack(bool aIsVideo) : mIsVideo (aIsVideo) + { + static size_t counter = 0; + std::ostringstream os; + os << counter++; + mID = os.str(); + } mozilla::TrackID GetTrackID() { return mIsVideo ? 1 : 0; } + std::string GetId() const { return mID; } + void AssignId(const std::string& id) { mID = id; } Fake_DOMMediaStream *GetStream() { return nullptr; } const Fake_MediaStreamTrack* AsVideoStreamTrack() const { @@ -235,6 +246,7 @@ private: ~Fake_MediaStreamTrack() {} const bool mIsVideo; + std::string mID; }; class Fake_DOMMediaStream : public nsISupports diff --git a/media/webrtc/signaling/test/mediapipeline_unittest.cpp b/media/webrtc/signaling/test/mediapipeline_unittest.cpp index 3428ddb0c4f..899b71bb9b4 100644 --- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp +++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp @@ -270,6 +270,7 @@ class TestAgentSend : public TestAgent { nullptr, test_utils->sts_target(), audio_, + "audio_track_fake_uuid", 1, false, audio_conduit_, @@ -319,7 +320,7 @@ class TestAgentReceive : public TestAgent { test_pc, nullptr, test_utils->sts_target(), - audio_->GetStream(), 1, 1, + audio_->GetStream(), "audio_track_fake_uuid", 1, 1, static_cast(audio_conduit_.get()), audio_rtp_transport_.flow_, audio_rtcp_transport_.flow_, diff --git a/media/webrtc/signaling/test/signaling_unittests.cpp b/media/webrtc/signaling/test/signaling_unittests.cpp index 71d11022d14..7b4824529f0 100644 --- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -1327,8 +1327,14 @@ class SignalingAgent { } } + // Right now we have no convenient way for this unit-test to learn the track + // ids of the tracks, so they can be queried later. We could either expose + // the JsepSessionImpl in some way, or we could parse the identifiers out of + // the SDP. For now, we just specify audio/video, since a given DOMMediaStream + // can have only one of each anyway. Once this is fixed, we will need to + // pass a real track id if we want to test that case. mozilla::RefPtr GetMediaPipeline( - bool local, size_t stream, int track) { + bool local, size_t stream, bool video) { SourceStreamInfo* streamInfo; if (local) { mozilla::SyncRunnable::DispatchToThread( @@ -1348,11 +1354,16 @@ class SignalingAgent { const auto &pipelines = streamInfo->GetPipelines(); - auto it = pipelines.find(track); - return (it == pipelines.end())? nullptr : it->second; + for (auto i = pipelines.begin(); i != pipelines.end(); ++i) { + if (i->second->IsVideo() == video) { + std::cout << "Got MediaPipeline " << i->second->trackid(); + return i->second; + } + } + return nullptr; } - void CheckMediaPipeline(int stream, int track, uint32_t flags, + void CheckMediaPipeline(int stream, bool video, uint32_t flags, VideoSessionConduit::FrameRequestType frameRequestMethod = VideoSessionConduit::FrameRequestNone) { @@ -1361,13 +1372,13 @@ class SignalingAgent { << ((flags & PIPELINE_SEND) ? "sending " : "receiving ") << ((flags & PIPELINE_VIDEO) ? "video" : "audio") << " pipeline (stream " << stream - << ", track " << track << "); expect " + << ", track " << video << "); expect " << ((flags & PIPELINE_RTCP_MUX) ? "MUX, " : "no MUX, ") << ((flags & PIPELINE_RTCP_NACK) ? "NACK." : "no NACK.") << std::endl; mozilla::RefPtr pipeline = - GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track); + GetMediaPipeline((flags & PIPELINE_LOCAL), stream, video); ASSERT_TRUE(pipeline); ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX)); // We cannot yet test send/recv with video. @@ -1938,12 +1949,12 @@ public: a2_->CloseReceiveStreams(); // Check caller video settings for remote pipeline - a1_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a1_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); // Check caller video settings for remote pipeline // (Should use pli and nack, regardless of what was in the offer) - a2_->CheckMediaPipeline(0, 1, + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_SEND | @@ -1978,12 +1989,12 @@ public: a2_->CloseReceiveStreams(); // Check callee video settings for remote pipeline - a2_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod); // Check caller video settings for remote pipeline // (Should use pli and nack, regardless of what was in the offer) - a1_->CheckMediaPipeline(0, 1, + a1_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_SEND | @@ -2559,12 +2570,12 @@ TEST_P(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 ? + a1_->CheckMediaPipeline(0, false, fRtcpMux ? PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND : PIPELINE_LOCAL | PIPELINE_SEND); // The first Remote pipeline gets stored at 0 - a2_->CheckMediaPipeline(0, 0, (fRtcpMux ? PIPELINE_RTCP_MUX : 0)); + a2_->CheckMediaPipeline(0, false, (fRtcpMux ? PIPELINE_RTCP_MUX : 0)); } TEST_P(SignalingTest, FullCallAudioOnly) @@ -3397,10 +3408,8 @@ TEST_P(SignalingTest, AudioOnlyCalleeNoRtcpMux) // 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); - - // The first Remote pipeline gets stored at 1 - a2_->CheckMediaPipeline(0, 0, 0); + a1_->CheckMediaPipeline(0, false, PIPELINE_LOCAL | PIPELINE_SEND); + a2_->CheckMediaPipeline(0, false, 0); } @@ -3537,18 +3546,18 @@ TEST_P(SignalingTest, FullCallAudioNoMuxVideoMux) // 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); + a1_->CheckMediaPipeline(0, false, PIPELINE_LOCAL | PIPELINE_SEND); // Now check video mux. - a1_->CheckMediaPipeline(0, 1, + a1_->CheckMediaPipeline(0, true, PIPELINE_LOCAL | (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_SEND | PIPELINE_VIDEO); // The first Remote pipeline gets stored at 0 - a2_->CheckMediaPipeline(0, 0, 0); + a2_->CheckMediaPipeline(0, false, 0); // Now check video mux. - a2_->CheckMediaPipeline(0, 1, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | + a2_->CheckMediaPipeline(0, true, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_VIDEO | PIPELINE_RTCP_NACK, VideoSessionConduit::FrameRequestPli); }