Bug 1095218 - Part 2: Multistream support. r=mt

--HG--
extra : rebase_source : d699a4408c351014e30be3b3dfe148bda14c643f
This commit is contained in:
Byron Campen [:bwc] 2014-12-10 11:17:09 -08:00
parent cc7a4509dc
commit 24ec1efc5e
17 changed files with 541 additions and 503 deletions

View File

@ -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<nsIUUIDGenerator> 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

View File

@ -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<DOMMediaStream> mStream;
TrackID mTrackID;
nsID mID;
nsString mID;
bool mEnded;
bool mEnabled;
};

View File

@ -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]

View File

@ -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:<stream-id> <track-id>
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));

View File

@ -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) {

View File

@ -1513,6 +1513,8 @@ JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr<Sdp>* parsedp)
return NS_ERROR_INVALID_ARG;
}
std::set<std::string> 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<Sdp>* 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);

View File

@ -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<void *>(domstream->GetStream()) << " conduit type=" <<
MOZ_MTLOG(ML_DEBUG, "Reattaching pipeline " << description_ << " to stream "
<< static_cast<void *>(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

View File

@ -77,7 +77,7 @@ class MediaPipeline : public sigslot::has_slots<> {
nsCOMPtr<nsIEventTarget> main_thread,
nsCOMPtr<nsIEventTarget> sts_thread,
MediaStream *stream,
TrackID track_id,
const std::string& track_id,
int level,
RefPtr<MediaSessionConduit> conduit,
RefPtr<TransportFlow> rtp_transport,
@ -130,9 +130,9 @@ class MediaPipeline : public sigslot::has_slots<> {
nsAutoPtr<MediaPipelineFilter> 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<MediaSessionConduit> conduit_; // Our conduit. Written on the main
// thread. Read on STS thread.
@ -352,6 +352,7 @@ public:
nsCOMPtr<nsIEventTarget> main_thread,
nsCOMPtr<nsIEventTarget> sts_thread,
DOMMediaStream *domstream,
const std::string& track_id,
int level,
bool is_video,
RefPtr<MediaSessionConduit> conduit,
@ -359,7 +360,7 @@ public:
RefPtr<TransportFlow> rtcp_transport,
nsAutoPtr<MediaPipelineFilter> 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<nsIEventTarget> main_thread,
nsCOMPtr<nsIEventTarget> sts_thread,
MediaStream *stream,
TrackID track_id,
const std::string& track_id,
int level,
RefPtr<MediaSessionConduit> conduit,
RefPtr<TransportFlow> rtp_transport,
@ -547,17 +548,23 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive {
nsCOMPtr<nsIEventTarget> main_thread,
nsCOMPtr<nsIEventTarget> 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<AudioSessionConduit> conduit,
RefPtr<TransportFlow> rtp_transport,
RefPtr<TransportFlow> rtcp_transport,
nsAutoPtr<MediaPipelineFilter> 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<nsIEventTarget> main_thread,
nsCOMPtr<nsIEventTarget> 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<VideoSessionConduit> conduit,
RefPtr<TransportFlow> rtp_transport,
RefPtr<TransportFlow> rtcp_transport,
nsAutoPtr<MediaPipelineFilter> 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 {

View File

@ -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<AudioSessionConduit*>(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<VideoSessionConduit*>(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<MediaPipeline>(pipeline));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " <<
static_cast<unsigned>(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<MediaPipelineTransmit> 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<MediaPipeline>(pipeline));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " <<
static_cast<unsigned>(rv));
return rv;
}
return NS_OK;
}

View File

@ -178,10 +178,18 @@ private:
class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
{
public:
TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints,
TracksAvailableCallback(const std::list<std::string>& audioTrackIds,
const std::list<std::string>& videoTrackIds,
const std::set<std::string>& preexistingTrackIds,
nsRefPtr<PeerConnectionObserver> 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<nsRefPtr<MediaStreamTrack>> 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<uint32_t>(rv.ErrorCode()));
CSFLogError(logTag, ": OnAddTrack(%u) failed! Error: %u",
static_cast<unsigned>(i),
static_cast<unsigned>(rv.ErrorCode()));
}
}
JSErrorResult rv;
CSFLogInfo(logTag, "Calling OnAddStream");
mObserver->OnAddStream(*aStream, rv);
if (rv.Failed()) {
CSFLogError(logTag, ": OnAddStream() failed! Error: %u", static_cast<uint32_t>(rv.ErrorCode()));
CSFLogError(logTag, ": OnAddStream() failed! Error: %u",
static_cast<unsigned>(rv.ErrorCode()));
}
if (!mAudioTrackIds.empty() || !mVideoTrackIds.empty()) {
CSFLogError(logTag, "Failed to assign %u audio and %u video tracks!",
static_cast<unsigned>(mAudioTrackIds.size()),
static_cast<unsigned>(mVideoTrackIds.size()));
}
}
void AssignNextIdToTrack(MediaStreamTrack* track)
{
std::list<std::string>* 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<PeerConnectionObserver> mObserver;
std::list<std::string> mAudioTrackIds;
std::list<std::string> mVideoTrackIds;
const std::set<std::string> 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<std::string> streamsToNotify;
// Group tracks by stream id
std::map<std::string, std::vector<RefPtr<JsepTrack>>> tracksByStreamId;
for (size_t i = 0; i < numTracks; ++i) {
RefPtr<JsepTrack> 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<RefPtr<JsepTrack>>& tracks = i->second;
nsRefPtr<RemoteSourceStreamInfo> 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<std::string> newAudioTrackIds;
std::list<std::string> newVideoTrackIds;
// TODO(bug 1017888): Fill in for renegotiation
std::set<std::string> preexistingTrackIds;
for (auto j = tracks.begin(); j != tracks.end(); ++j) {
RefPtr<JsepTrack> 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<RemoteSourceStreamInfo> 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<OwningNonNull<DOMMediaStream>>& 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<PeerConnectionObserver> 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<LocalSourceStreamInfo> 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<PeerConnectionObserver> 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()) {

View File

@ -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);

View File

@ -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<int, mozilla::RefPtr<mozilla::MediaPipeline> >::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<int, mozilla::RefPtr<mozilla::MediaPipeline> >::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<mozilla::MediaPipeline> pipeline = mPipelines[aMLine];
MOZ_ASSERT(pipeline);
if (NS_SUCCEEDED(static_cast<mozilla::MediaPipelineTransmit*>(pipeline.get())->ReplaceTrack(aNewStream, aNewTrack))) {
return NS_OK;
mozilla::RefPtr<mozilla::MediaPipeline> 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<mozilla::MediaPipelineTransmit*>(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<int, mozilla::RefPtr<mozilla::MediaPipeline> >::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<int, mozilla::RefPtr<mozilla::MediaPipeline> >::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<LocalSourceStreamInfo> 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<LocalSourceStreamInfo> 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<LocalSourceStreamInfo> 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<mozilla::MediaPipelineFilter> filter)
{
ASSERT_ON_THREAD(mMainThread);
RefPtr<mozilla::MediaPipeline> receive;
for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) {
receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(aMLine);
receive = mRemoteSourceStreams[i]->GetPipelineByTrackId_m(trackId);
}
RefPtr<mozilla::MediaPipeline> 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<int>(aMLine));
CSFLogWarn(logTag, "Could not locate track %s to update filter",
trackId.c_str());
}
return false;
}
@ -951,27 +839,6 @@ PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo)
return NS_OK;
}
nsresult
PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo)
{
if (aIndex < 0 ||
static_cast<unsigned int>(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<mozilla::MediaPipeline>& 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<mozilla::MediaPipelineTransmit> 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<mozilla::MediaPipelineReceive> 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<int, bool>::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<mozilla::WebrtcAudioConduit*>
(aIsVideo ?
mPipelines[it->first]->Conduit() :
aPipeline->Conduit());
mozilla::WebrtcVideoConduit *video_conduit = static_cast<mozilla::WebrtcVideoConduit*>
(aIsVideo ?
aPipeline->Conduit() :
mPipelines[it->first]->Conduit());
mozilla::WebrtcAudioConduit *audio_conduit =
static_cast<mozilla::WebrtcAudioConduit*>(aPipeline->IsVideo() ?
i->second->Conduit() :
aPipeline->Conduit());
mozilla::WebrtcVideoConduit *video_conduit =
static_cast<mozilla::WebrtcVideoConduit*>(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<MediaPipeline> SourceStreamInfo::GetPipelineByLevel_m(int aMLine) {
RefPtr<MediaPipeline> 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<MediaPipeline> SourceStreamInfo::GetPipelineByLevel_m(int aMLine) {
// RefPtr<MediaPipeline>, 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];
}
}

View File

@ -198,18 +198,38 @@ public:
return mMediaStream;
}
nsresult StorePipeline(
const std::string& trackId,
const mozilla::RefPtr<mozilla::MediaPipeline>& 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<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>>&
const std::map<std::string, mozilla::RefPtr<mozilla::MediaPipeline>>&
GetPipelines() const { return mPipelines; }
mozilla::RefPtr<mozilla::MediaPipeline> GetPipelineByLevel_m(int aMLine);
mozilla::RefPtr<mozilla::MediaPipeline> 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<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>> mPipelines;
nsRefPtr<DOMMediaStream> 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<std::string> mTracks;
// Indexed by track id, might contain pipelines for removed tracks
std::map<std::string, mozilla::RefPtr<mozilla::MediaPipeline>> 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<mozilla::MediaPipelineTransmit> 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<mozilla::TrackID> mAudioTracks;
nsTArray<mozilla::TrackID> mVideoTracks;
};
class RemoteSourceStreamInfo : public SourceStreamInfo {
@ -266,28 +265,17 @@ class RemoteSourceStreamInfo : public SourceStreamInfo {
RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> 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<mozilla::MediaPipelineReceive> aPipeline);
void DetachTransport_s();
void DetachMedia_m();
void SyncPipeline(RefPtr<MediaPipelineReceive> 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<int, bool> 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<mozilla::MediaPipelineFilter> filter);
// Add a remote stream.
nsresult AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> 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

View File

@ -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

View File

@ -6,6 +6,8 @@
#define FAKE_MEDIA_STREAM_H_
#include <set>
#include <string>
#include <sstream>
#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

View File

@ -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<mozilla::AudioSessionConduit *>(audio_conduit_.get()),
audio_rtp_transport_.flow_,
audio_rtcp_transport_.flow_,

View File

@ -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<mozilla::MediaPipeline> 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<mozilla::MediaPipeline> 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);
}