From d02c39d11f91270540941b70571912c59a782154 Mon Sep 17 00:00:00 2001 From: "Byron Campen [:bwc]" Date: Mon, 1 Dec 2014 21:19:57 -0800 Subject: [PATCH] Bug 1095218 - Part 1: msid support. r=mt --HG-- extra : rebase_source : 85f8a97d16496f2b00ea049ebe29d41becd15a62 --- .../test_peerConnection_peerIdentity.html | 5 - .../signaling/src/jsep/JsepSessionImpl.cpp | 181 +++++++++++++++++- .../signaling/src/jsep/JsepSessionImpl.h | 12 +- .../src/peerconnection/PeerConnectionImpl.cpp | 2 + .../webrtc/signaling/src/sdp/SdpAttribute.cpp | 12 ++ media/webrtc/signaling/src/sdp/SdpAttribute.h | 30 +++ .../signaling/src/sdp/SdpAttributeList.h | 2 +- .../src/sdp/SipccSdpAttributeList.cpp | 44 ++++- .../signaling/src/sdp/SipccSdpAttributeList.h | 6 +- media/webrtc/signaling/src/sdp/sipcc/sdp.h | 10 +- .../webrtc/signaling/src/sdp/sipcc/sdp_attr.c | 78 +++++++- .../signaling/src/sdp/sipcc/sdp_attr_access.c | 23 ++- .../webrtc/signaling/src/sdp/sipcc/sdp_main.c | 2 +- .../signaling/src/sdp/sipcc/sdp_private.h | 6 + .../signaling/test/jsep_session_unittest.cpp | 58 +++++- media/webrtc/signaling/test/sdp_unittests.cpp | 42 ++-- 16 files changed, 467 insertions(+), 46 deletions(-) diff --git a/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html index f2eb201a5bb3..580bccae5286 100644 --- a/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html +++ b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html @@ -32,15 +32,10 @@ function theTest() { }); test.setMediaConstraints([{ audio: true, - peerIdentity: id2 - }, { video: true, peerIdentity: id2 }], [{ audio: true, - fake: true, - peerIdentity: id1 - }, { video: true, fake: true, peerIdentity: id1 diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp index d1f07cd0ecc5..8ba9acdef993 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp @@ -236,6 +236,9 @@ JsepSessionImpl::AddOfferMSectionsByType(SdpMediaSection::MediaType mediatype, AddLocalSsrcs(*track->mTrack, &sdp->GetMediaSection(*track->mAssignedMLine)); + + AddLocalIds(*track->mTrack, + &sdp->GetMediaSection(*track->mAssignedMLine)); } while (offerToReceive.isSome() && added < *offerToReceive) { @@ -268,6 +271,133 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const } } +void +JsepSessionImpl::SetupMsidSemantic(const std::vector& msids, + Sdp* sdp) const +{ + if (!msids.empty()) { + UniquePtr msidSemantics( + new SdpMsidSemanticAttributeList); + msidSemantics->PushEntry("WMS", msids); + sdp->GetAttributeList().SetAttribute(msidSemantics.release()); + } +} + +nsresult +JsepSessionImpl::GetIdsFromMsid(const Sdp& sdp, + const SdpMediaSection& msection, + std::string* streamId, + std::string* trackId) +{ + if (!sdp.GetAttributeList().HasAttribute( + SdpAttribute::kMsidSemanticAttribute)) { + return NS_ERROR_NOT_AVAILABLE; + } + + auto& msidSemantics = sdp.GetAttributeList().GetMsidSemantic().mMsidSemantics; + std::vector allMsids; + nsresult rv = GetMsids(msection, &allMsids); + NS_ENSURE_SUCCESS(rv, rv); + + bool allMsidsAreWebrtc = false; + std::set webrtcMsids; + + for (auto i = msidSemantics.begin(); i != msidSemantics.end(); ++i) { + if (i->semantic == "WMS") { + for (auto j = i->msids.begin(); j != i->msids.end(); ++j) { + if (*j == "*") { + allMsidsAreWebrtc = true; + } else { + webrtcMsids.insert(*j); + } + } + break; + } + } + + bool found = false; + + for (auto i = allMsids.begin(); i != allMsids.end(); ++i) { + if (allMsidsAreWebrtc || webrtcMsids.count(i->identifier)) { + // For now, we assume that there is exactly one streamId/trackId pair + // per m-section. Later on, we'll add handling for multiple remote tracks + // per m-section. + if (!found) { + *streamId = i->identifier; + *trackId = i->appdata; + found = true; + } else if ((*streamId != i->identifier) || (*trackId != i->appdata)) { + // Bail if there are multiple stream/track ids for now + JSEP_SET_ERROR("Found multiple different webrtc msids in m-section " + << msection.GetLevel() << ". The behavior here is " + "undefined."); + return NS_ERROR_INVALID_ARG; + } + } + } + + if (!found) { + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +} + +nsresult +JsepSessionImpl::GetMsids( + const SdpMediaSection& msection, + std::vector* msids) +{ + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { + *msids = msection.GetAttributeList().GetMsid().mMsids; + } + + // Can we find some additional msids in ssrc attributes? + // (Chrome does not put plain-old msid attributes in its SDP) + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) { + auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs; + + for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) { + if (i->attribute.find("msid:") == 0) { + // Would be nice if SdpSsrcAttributeList could parse out the contained + // attribute, but at least the parse here is simple. + size_t streamIdStart = i->attribute.find_first_not_of(" \t", 5); + // We do not assume the appdata token is here, since this is not + // necessarily a webrtc msid + if (streamIdStart == std::string::npos) { + JSEP_SET_ERROR("Malformed source-level msid attribute: " + << i->attribute); + return NS_ERROR_INVALID_ARG; + } + + size_t streamIdEnd = i->attribute.find_first_of(" \t", streamIdStart); + if (streamIdEnd == std::string::npos) { + streamIdEnd = i->attribute.size(); + } + + size_t trackIdStart = + i->attribute.find_first_not_of(" \t", streamIdEnd); + if (trackIdStart == std::string::npos) { + trackIdStart = i->attribute.size(); + } + + size_t trackIdEnd = i->attribute.find_first_of(" \t", trackIdStart); + if (trackIdEnd == std::string::npos) { + trackIdEnd = i->attribute.size(); + } + + size_t streamIdSize = streamIdEnd - streamIdStart; + size_t trackIdSize = trackIdEnd - trackIdStart; + + msids->push_back({i->attribute.substr(streamIdStart, streamIdSize), + i->attribute.substr(trackIdStart, trackIdSize)}); + } + } + } + + return NS_OK; +} + nsresult JsepSessionImpl::CreateOffer(const JsepOfferOptions& options, std::string* offer) @@ -395,6 +525,24 @@ JsepSessionImpl::AddLocalSsrcs(const JsepTrack& track, } } +void +JsepSessionImpl::AddLocalIds(const JsepTrack& track, + SdpMediaSection* msection) const +{ + if (track.GetMediaType() == SdpMediaSection::kApplication) { + return; + } + + UniquePtr msids(new SdpMsidAttributeList); + if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { + msids->mMsids = msection->GetAttributeList().GetMsid().mMsids; + } + + msids->PushEntry(track.GetStreamId(), track.GetTrackId()); + + msection->GetAttributeList().SetAttribute(msids.release()); +} + JsepCodecDescription* JsepSessionImpl::FindMatchingCodec(const std::string& fmt, const SdpMediaSection& msection) const @@ -663,6 +811,8 @@ JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options, AddLocalSsrcs(*track->mTrack, msection); + AddLocalIds(*track->mTrack, msection); + localDirection = SdpDirectionAttribute::kSendonly; track->mAssignedMLine = Some(mlineIndex); found = true; @@ -1462,7 +1612,7 @@ JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp& remoteDescription) // TODO(bug 1017888): Suppress new track creation on renegotiation // of existing tracks. if (direction & SdpDirectionAttribute::kSendFlag) { - nsresult rv = CreateReceivingTrack(i, msection); + nsresult rv = CreateReceivingTrack(i, remoteDescription, msection); NS_ENSURE_SUCCESS(rv, rv); } } @@ -1521,20 +1671,31 @@ JsepSessionImpl::ValidateLocalDescription(const Sdp& description) nsresult JsepSessionImpl::CreateReceivingTrack(size_t mline, + const Sdp& sdp, const SdpMediaSection& msection) { std::string streamId; std::string trackId; - // Generate random track ids. - // TODO(bug 1095218): Pull track and stream IDs out of SDP if available. - if (!mUuidGen->Generate(&trackId)) { - JSEP_SET_ERROR("Failed to generate UUID for JsepTrack"); - return NS_ERROR_FAILURE; + nsresult rv = GetIdsFromMsid(sdp, msection, &streamId, &trackId); + + if (NS_FAILED(rv)) { + if (rv != NS_ERROR_NOT_AVAILABLE) { + // Malformed ssrc attribute, probably + return rv; + } + + streamId = mDefaultRemoteStreamId; + + // Generate random track ids. + if (!mUuidGen->Generate(&trackId)) { + JSEP_SET_ERROR("Failed to generate UUID for JsepTrack"); + return NS_ERROR_FAILURE; + } } JsepTrack* remote = new JsepTrack(msection.GetMediaType(), - mDefaultRemoteStreamId, + streamId, trackId, JsepTrack::kJsepTrackReceiving); @@ -1590,6 +1751,12 @@ JsepSessionImpl::CreateGenericSDP(UniquePtr* sdpp) iceOpts->PushEntry("trickle"); sdp->GetAttributeList().SetAttribute(iceOpts); + // This assumes content doesn't add a bunch of msid attributes with a + // different semantic in mind. + std::vector msids; + msids.push_back("*"); + SetupMsidSemantic(msids, sdp.get()); + *sdpp = Move(sdp); return NS_OK; } diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h index 166a27a665ac..8f38683eb037 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h @@ -210,6 +210,7 @@ private: void AddExtmap(SdpMediaSection* msection) const; void AddMid(const std::string& mid, SdpMediaSection* msection) const; void AddLocalSsrcs(const JsepTrack& track, SdpMediaSection* msection) const; + void AddLocalIds(const JsepTrack& track, SdpMediaSection* msection) const; JsepCodecDescription* FindMatchingCodec( const std::string& pt, const SdpMediaSection& msection) const; @@ -231,7 +232,9 @@ private: nsresult ValidateLocalDescription(const Sdp& description); nsresult SetRemoteTracksFromDescription(const Sdp& remoteDescription); // Non-const because we use our Uuid generator - nsresult CreateReceivingTrack(size_t mline, const SdpMediaSection& msection); + nsresult CreateReceivingTrack(size_t mline, + const Sdp& sdp, + const SdpMediaSection& msection); nsresult HandleNegotiatedSession(const UniquePtr& local, const UniquePtr& remote); nsresult DetermineSendingDirection(SdpDirectionAttribute::Direction offer, @@ -244,6 +247,13 @@ private: Maybe offerToReceive, Sdp* sdp); void SetupBundle(Sdp* sdp) const; + void SetupMsidSemantic(const std::vector& msids, Sdp* sdp) const; + nsresult GetIdsFromMsid(const Sdp& sdp, + const SdpMediaSection& msection, + std::string* streamId, + std::string* trackId); + nsresult GetMsids(const SdpMediaSection& msection, + std::vector* msids); nsresult CreateOfferMSection(SdpMediaSection::MediaType type, SdpDirectionAttribute::Direction direction, SdpMediaSection::Protocol proto, diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index b8112fe2f65d..1decdcb443c3 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -200,12 +200,14 @@ public: for (uint32_t i = 0; i < tracks.Length(); i++) { JSErrorResult rv; mObserver->OnAddTrack(*tracks[i], rv); + CSFLogInfo(logTag, "Calling OnAddTrack"); if (rv.Failed()) { CSFLogError(logTag, ": OnAddTrack(%d) failed! Error: %u", 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())); diff --git a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp index 2694a33f83f8..a9ec89579f42 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp +++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp @@ -164,6 +164,18 @@ SdpMsidAttributeList::Serialize(std::ostream& os) const } } +void +SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const +{ + for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) { + os << "a=" << mType << ":" << i->semantic; + for (auto j = i->msids.begin(); j != i->msids.end(); ++j) { + os << " " << *j; + } + os << CRLF; + } +} + void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const { diff --git a/media/webrtc/signaling/src/sdp/SdpAttribute.h b/media/webrtc/signaling/src/sdp/SdpAttribute.h index e74a7567267b..b6bcab77c768 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttribute.h +++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h @@ -630,6 +630,36 @@ public: std::vector mMsids; }; +/////////////////////////////////////////////////////////////////////////// +// a=msid-semantic, draft-ietf-mmusic-msid +//------------------------------------------------------------------------- +// msid-semantic-attr = "msid-semantic:" msid-semantic msid-list +// msid-semantic = token ; see RFC 4566 +// msid-list = *(" " msid-id) / " *" +class SdpMsidSemanticAttributeList : public SdpAttribute +{ +public: + SdpMsidSemanticAttributeList() : SdpAttribute(kMsidSemanticAttribute) {} + + struct MsidSemantic + { + // TODO: Once we have some more of these, we might want to make an enum + std::string semantic; + std::vector msids; + }; + + void + PushEntry(const std::string& semantic, const std::vector& msids) + { + MsidSemantic value = {semantic, msids}; + mMsidSemantics.push_back(value); + } + + virtual void Serialize(std::ostream& os) const MOZ_OVERRIDE; + + std::vector mMsidSemantics; +}; + /////////////////////////////////////////////////////////////////////////// // a=remote-candiate, RFC5245 //------------------------------------------------------------------------- diff --git a/media/webrtc/signaling/src/sdp/SdpAttributeList.h b/media/webrtc/signaling/src/sdp/SdpAttributeList.h index 4343efb9650e..2d17ab89a92a 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttributeList.h +++ b/media/webrtc/signaling/src/sdp/SdpAttributeList.h @@ -70,7 +70,7 @@ public: virtual const std::string& GetLabel() const = 0; virtual unsigned int GetMaxptime() const = 0; virtual const std::string& GetMid() const = 0; - virtual const std::string& GetMsidSemantic() const = 0; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const = 0; virtual unsigned int GetPtime() const = 0; // This is "special", because it's multiple things diff --git a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp index 3f730e12d475..91a3d2e283e9 100644 --- a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp +++ b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp @@ -108,8 +108,6 @@ SipccSdpAttributeList::LoadSimpleStrings(sdp_t* sdp, uint16_t level, errorHolder); LoadSimpleString(sdp, level, SDP_ATTR_IDENTITY, SdpAttribute::kIdentityAttribute, errorHolder); - LoadSimpleString(sdp, level, SDP_ATTR_MSID_SEMANTIC, - SdpAttribute::kMsidSemanticAttribute, errorHolder); } void @@ -547,6 +545,38 @@ SipccSdpAttributeList::LoadGroups(sdp_t* sdp, uint16_t level, return true; } +bool +SipccSdpAttributeList::LoadMsidSemantics(sdp_t* sdp, uint16_t level, + SdpErrorHolder& errorHolder) +{ + auto msidSemantics = MakeUnique(); + + for (uint16_t i = 1; i < UINT16_MAX; ++i) { + sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_MSID_SEMANTIC, i); + + if (!attr) { + break; + } + + sdp_msid_semantic_t* msid_semantic = &(attr->attr.msid_semantic); + std::vector msids; + for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!msid_semantic->msids[i]) { + break; + } + + msids.push_back(msid_semantic->msids[i]); + } + + msidSemantics->PushEntry(msid_semantic->semantic, msids); + } + + if (!msidSemantics->mMsidSemantics.empty()) { + SetAttribute(msidSemantics.release()); + } + return true; +} + void SipccSdpAttributeList::LoadFmtp(sdp_t* sdp, uint16_t level) { @@ -829,6 +859,10 @@ SipccSdpAttributeList::Load(sdp_t* sdp, uint16_t level, if (!LoadGroups(sdp, level, errorHolder)) { return false; } + + if (!LoadMsidSemantics(sdp, level, errorHolder)) { + return false; + } } else { sdp_media_e mtype = sdp_get_media_type(sdp, level); if (mtype == SDP_MEDIA_APPLICATION) { @@ -1045,14 +1079,14 @@ SipccSdpAttributeList::GetMsid() const return *static_cast(attr); } -const std::string& +const SdpMsidSemanticAttributeList& SipccSdpAttributeList::GetMsidSemantic() const { if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) { - return kEmptyString; + MOZ_CRASH(); } const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute); - return static_cast(attr)->mValue; + return *static_cast(attr); } uint32_t diff --git a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h index 52de2e991838..329213659309 100644 --- a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h +++ b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h @@ -69,7 +69,8 @@ public: virtual const std::string& GetLabel() const MOZ_OVERRIDE; virtual unsigned int GetMaxptime() const MOZ_OVERRIDE; virtual const std::string& GetMid() const MOZ_OVERRIDE; - virtual const std::string& GetMsidSemantic() const MOZ_OVERRIDE; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() + const MOZ_OVERRIDE; virtual unsigned int GetPtime() const MOZ_OVERRIDE; virtual SdpDirectionAttribute::Direction GetDirection() const MOZ_OVERRIDE; @@ -105,6 +106,9 @@ private: void LoadSetup(sdp_t* sdp, uint16_t level); void LoadSsrc(sdp_t* sdp, uint16_t level); bool LoadGroups(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); + bool LoadMsidSemantics(sdp_t* sdp, + uint16_t level, + SdpErrorHolder& errorHolder); void LoadFmtp(sdp_t* sdp, uint16_t level); void LoadMsids(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); void LoadExtmap(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp.h b/media/webrtc/signaling/src/sdp/sipcc/sdp.h index 1a60cd0cf3de..7c72456e0e93 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_GROUP_STREAM_ID 10 +#define SDP_MAX_MEDIA_STREAMS 10 #define SDP_MAGIC_NUM 0xabcdabcd @@ -838,9 +838,14 @@ typedef struct sdp_stream_data { char x_confid[SDP_MAX_STRING_LEN+1]; sdp_group_attr_e group_attr; /* FID or LS */ uint16_t num_group_id; - char * group_ids[SDP_MAX_GROUP_STREAM_ID]; + char * group_ids[SDP_MAX_MEDIA_STREAMS]; } sdp_stream_data_t; +typedef struct sdp_msid_semantic { + char semantic[SDP_MAX_STRING_LEN+1]; + char * msids[SDP_MAX_MEDIA_STREAMS]; +} sdp_msid_semantic_t; + /* * a=source-filter: * = ... @@ -1009,6 +1014,7 @@ typedef struct sdp_attr { sdp_srtp_crypto_context_t srtp_context; sdp_mptime_t mptime; sdp_stream_data_t stream_data; + sdp_msid_semantic_t msid_semantic; char unknown[SDP_MAX_STRING_LEN+1]; sdp_source_filter_t source_filter; sdp_fmtp_fb_t rtcp_fb; diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c index c4a9af180015..21a647bc8cb6 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c @@ -4101,7 +4101,7 @@ sdp_result_e sdp_parse_attr_group (sdp_t *sdp_p, sdp_attr_t *attr_p, */ attr_p->attr.stream_data.num_group_id =0; - for (i=0; iattr.msid_semantic.semantic, + sizeof(attr_p->attr.msid_semantic.semantic), + " \t", + &result); + + if (result != SDP_SUCCESS) { + sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute; " + "missing semantic", + sdp_p->debug_str); + sdp_p->conf_p->num_invalid_param++; + return SDP_INVALID_PARAMETER; + } + + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + /* msid-id can be up to 64 characters long, plus null terminator */ + char temp[65]; + ptr = sdp_getnextstrtok(ptr, temp, sizeof(temp), " \t", &result); + + if (result != SDP_SUCCESS) { + break; + } + + attr_p->attr.msid_semantic.msids[i] = cpr_strdup(temp); + } + + if ((result != SDP_SUCCESS) && (result != SDP_EMPTY_TOKEN)) { + sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute", + sdp_p->debug_str); + sdp_p->conf_p->num_invalid_param++; + return SDP_INVALID_PARAMETER; + } + + if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) { + SDP_PRINT("%s Parsed a=msid-semantic, %s", sdp_p->debug_str, + attr_p->attr.msid_semantic.semantic); + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!attr_p->attr.msid_semantic.msids[i]) { + break; + } + + SDP_PRINT("%s ... msid %s", sdp_p->debug_str, + attr_p->attr.msid_semantic.msids[i]); + } + } + + return SDP_SUCCESS; +} + + +sdp_result_e sdp_build_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + flex_string *fs) +{ + int i; + flex_string_sprintf(fs, "a=msid-semantic:%s", + attr_p->attr.msid_semantic.semantic); + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!attr_p->attr.msid_semantic.msids[i]) { + break; + } + + flex_string_sprintf(fs, " %s", + attr_p->attr.msid_semantic.msids[i]); + } + flex_string_sprintf(fs, "\r\n"); + return SDP_SUCCESS; +} + sdp_result_e sdp_parse_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr) diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c index 37dd17131d34..444d2225d4aa 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c @@ -605,6 +605,20 @@ void sdp_copy_attr_fields (sdp_attr_t *src_attr_p, sdp_attr_t *dst_attr_p) } break; + case SDP_ATTR_MSID_SEMANTIC: + sstrncpy(dst_attr_p->attr.msid_semantic.semantic, + src_attr_p->attr.msid_semantic.semantic, + SDP_MAX_STRING_LEN+1); + for (i=0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + if (!src_attr_p->attr.msid_semantic.msids[i]) { + break; + } + + dst_attr_p->attr.msid_semantic.msids[i] = + cpr_strdup(src_attr_p->attr.msid_semantic.msids[i]); + } + break; + case SDP_ATTR_SOURCE_FILTER: dst_attr_p->attr.source_filter.mode = src_attr_p->attr.source_filter.mode; @@ -1176,6 +1190,10 @@ void sdp_free_attr (sdp_attr_t *attr_p) for (i = 0; i < attr_p->attr.stream_data.num_group_id; i++) { SDP_FREE(attr_p->attr.stream_data.group_ids[i]); } + } else if (attr_p->type == SDP_ATTR_MSID_SEMANTIC) { + for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { + SDP_FREE(attr_p->attr.msid_semantic.msids[i]); + } } /* Now free the actual attribute memory. */ @@ -1658,7 +1676,6 @@ static boolean sdp_attr_is_simple_string(sdp_attr_e attr_type) { (attr_type != SDP_ATTR_X_CONFID) && (attr_type != SDP_ATTR_LABEL) && (attr_type != SDP_ATTR_IDENTITY) && - (attr_type != SDP_ATTR_MSID_SEMANTIC) && (attr_type != SDP_ATTR_ICE_OPTIONS)) { return FALSE; } @@ -10305,7 +10322,7 @@ sdp_result_e sdp_set_group_num_id (void *sdp_ptr, uint16_t level, } sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER); - } else if ((group_num_id == 0) || (group_num_id > SDP_MAX_GROUP_STREAM_ID)){ + } else if ((group_num_id == 0) || (group_num_id > SDP_MAX_MEDIA_STREAMS)){ if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { CSFLogError(logTag, "%s Number of group id value provided - %u is invalid", sdp_p->debug_str, (unsigned)group_num_id); @@ -10388,7 +10405,7 @@ sdp_result_e sdp_set_group_id (void *sdp_ptr, uint16_t level, return (SDP_INVALID_PARAMETER); } else { num_group_id = attr_p->attr.stream_data.num_group_id; - if (num_group_id == SDP_MAX_GROUP_STREAM_ID) { + if (num_group_id == SDP_MAX_MEDIA_STREAMS) { if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { CSFLogError(logTag, "%s Max number of Group Ids already defined " "for this group line %u", sdp_p->debug_str, (unsigned)level); diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c index 8c66a9f4a2e4..fedc23abe202 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c @@ -185,7 +185,7 @@ const sdp_attrarray_t sdp_attr[SDP_MAX_ATTR_TYPES] = {"msid", sizeof("msid"), sdp_parse_attr_msid, sdp_build_attr_msid}, {"msid-semantic", sizeof("msid-semantic"), - sdp_parse_attr_simple_string, sdp_build_attr_simple_string}, + sdp_parse_attr_msid_semantic, sdp_build_attr_msid_semantic}, {"bundle-only", sizeof("bundle-only"), sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag}, {"end-of-candidates", sizeof("end-of-candidates"), diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h b/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h index e44c57761f81..3029bc56eb89 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h @@ -80,6 +80,12 @@ extern sdp_result_e sdp_parse_attr_msid(sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr); extern sdp_result_e sdp_build_attr_msid(sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs); +extern sdp_result_e sdp_parse_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + const char *ptr); +extern sdp_result_e sdp_build_attr_msid_semantic(sdp_t *sdp_p, + sdp_attr_t *attr_p, + flex_string *fs); extern sdp_result_e sdp_parse_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr); extern sdp_result_e sdp_build_attr_ssrc(sdp_t *sdp_p, sdp_attr_t *attr_p, diff --git a/media/webrtc/signaling/test/jsep_session_unittest.cpp b/media/webrtc/signaling/test/jsep_session_unittest.cpp index 3625ac9876d2..67ce0b312755 100644 --- a/media/webrtc/signaling/test/jsep_session_unittest.cpp +++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp @@ -246,6 +246,20 @@ protected: if (checkFlags & CHECK_TRACKS) { // Check that the transports exist. ASSERT_EQ(types.size(), mSessionOff.GetTransportCount()); + for (size_t i = 0; i < types.size(); ++i) { + RefPtr ltrack; + ASSERT_EQ(NS_OK, mSessionOff.GetLocalTrack(i, <rack)); + ASSERT_NE("", ltrack->GetStreamId()); + ASSERT_NE("", ltrack->GetTrackId()); + if (ltrack->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += ltrack->GetStreamId(); + msidAttr += " "; + msidAttr += ltrack->GetTrackId(); + ASSERT_NE(std::string::npos, offer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } + } } } @@ -265,6 +279,16 @@ protected: RefPtr rtrack; ASSERT_EQ(NS_OK, mSessionAns.GetRemoteTrack(i, &rtrack)); ASSERT_EQ(types[i], rtrack->GetMediaType()); + ASSERT_NE("", rtrack->GetStreamId()); + ASSERT_NE("", rtrack->GetTrackId()); + if (rtrack->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += rtrack->GetStreamId(); + msidAttr += " "; + msidAttr += rtrack->GetTrackId(); + ASSERT_NE(std::string::npos, offer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } } @@ -287,6 +311,21 @@ protected: ASSERT_EQ(types[i], pair->mSending->GetMediaType()); ASSERT_TRUE(pair->mReceiving); ASSERT_EQ(types[i], pair->mReceiving->GetMediaType()); + ASSERT_NE("", pair->mSending->GetStreamId()); + ASSERT_NE("", pair->mSending->GetTrackId()); + // These might have been in the SDP, or might have been randomly + // chosen by JsepSessionImpl + ASSERT_NE("", pair->mReceiving->GetStreamId()); + ASSERT_NE("", pair->mReceiving->GetTrackId()); + + if (pair->mReceiving->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += pair->mSending->GetStreamId(); + msidAttr += " "; + msidAttr += pair->mSending->GetTrackId(); + ASSERT_NE(std::string::npos, answer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } DumpTrackPairs(mSessionOff); @@ -302,14 +341,29 @@ protected: if (checkFlags & CHECK_TRACKS) { // Verify that the right stuff is in the tracks. - ASSERT_EQ(types.size(), mSessionAns.GetNegotiatedTrackPairCount()); + ASSERT_EQ(types.size(), mSessionOff.GetNegotiatedTrackPairCount()); for (size_t i = 0; i < types.size(); ++i) { const JsepTrackPair* pair; - ASSERT_EQ(NS_OK, mSessionAns.GetNegotiatedTrackPair(i, &pair)); + ASSERT_EQ(NS_OK, mSessionOff.GetNegotiatedTrackPair(i, &pair)); ASSERT_TRUE(pair->mSending); ASSERT_EQ(types[i], pair->mSending->GetMediaType()); ASSERT_TRUE(pair->mReceiving); ASSERT_EQ(types[i], pair->mReceiving->GetMediaType()); + ASSERT_NE("", pair->mSending->GetStreamId()); + ASSERT_NE("", pair->mSending->GetTrackId()); + // These might have been in the SDP, or might have been randomly + // chosen by JsepSessionImpl + ASSERT_NE("", pair->mReceiving->GetStreamId()); + ASSERT_NE("", pair->mReceiving->GetTrackId()); + + if (pair->mReceiving->GetMediaType() != SdpMediaSection::kApplication) { + std::string msidAttr("a=msid:"); + msidAttr += pair->mReceiving->GetStreamId(); + msidAttr += " "; + msidAttr += pair->mReceiving->GetTrackId(); + ASSERT_NE(std::string::npos, answer.find(msidAttr)) + << "Did not find " << msidAttr << " in offer"; + } } } DumpTrackPairs(mSessionAns); diff --git a/media/webrtc/signaling/test/sdp_unittests.cpp b/media/webrtc/signaling/test/sdp_unittests.cpp index f30f7fd95da8..f71034a6a77f 100644 --- a/media/webrtc/signaling/test/sdp_unittests.cpp +++ b/media/webrtc/signaling/test/sdp_unittests.cpp @@ -1126,7 +1126,8 @@ const std::string kBasicAudioVideoOffer = "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF "a=ice-options:trickle foo" CRLF -"a=msid-semantic:WMS plus" CRLF +"a=msid-semantic:WMS stream streama" CRLF +"a=msid-semantic:foo stream" CRLF "a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=identity:blahblahblah foo;bar" CRLF "a=group:BUNDLE first second" CRLF @@ -1149,7 +1150,7 @@ const std::string kBasicAudioVideoOffer = "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF -"a=msid:track stream" CRLF +"a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761" CRLF @@ -1171,8 +1172,8 @@ const std::string kBasicAudioVideoOffer = "a=rtcp-fb:120 ccm fir" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF -"a=msid:tracka streama" CRLF -"a=msid:trackb streamb" CRLF +"a=msid:streama tracka" CRLF +"a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378" CRLF @@ -1426,7 +1427,7 @@ const std::string kH264AudioVideoOffer = "a=ice-ufrag:4a799b2e" CRLF "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF -"a=msid-semantic:WMS plus" CRLF +"a=msid-semantic:WMS stream streama" CRLF "a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=group:BUNDLE first second" CRLF "a=group:BUNDLE third" CRLF @@ -1448,7 +1449,7 @@ const std::string kH264AudioVideoOffer = "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF -"a=msid:track stream" CRLF +"a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761" CRLF @@ -1469,8 +1470,8 @@ const std::string kH264AudioVideoOffer = "a=recvonly" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF -"a=msid:tracka streama" CRLF -"a=msid:trackb streamb" CRLF +"a=msid:streama tracka" CRLF +"a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378" CRLF @@ -1672,22 +1673,29 @@ TEST_P(NewSdpTest, CheckMsid) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(mSdp->GetAttributeList().HasAttribute( SdpAttribute::kMsidSemanticAttribute)); - // note that we lose the extra pieces here - // it's not worth it to save them until they mean something - ASSERT_EQ("WMS", mSdp->GetAttributeList().GetMsidSemantic()); + auto semantics = mSdp->GetAttributeList().GetMsidSemantic().mMsidSemantics; + ASSERT_EQ(2U, semantics.size()); + ASSERT_EQ("WMS", semantics[0].semantic); + ASSERT_EQ(2U, semantics[0].msids.size()); + ASSERT_EQ("stream", semantics[0].msids[0]); + ASSERT_EQ("streama", semantics[0].msids[1]); + ASSERT_EQ("foo", semantics[1].semantic); + ASSERT_EQ(1U, semantics[1].msids.size()); + ASSERT_EQ("stream", semantics[1].msids[0]); + const SdpMsidAttributeList& msids1 = mSdp->GetMediaSection(0).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids1.mMsids.size()); - ASSERT_EQ("track", msids1.mMsids[0].identifier); - ASSERT_EQ("stream", msids1.mMsids[0].appdata); + ASSERT_EQ("stream", msids1.mMsids[0].identifier); + ASSERT_EQ("track", msids1.mMsids[0].appdata); const SdpMsidAttributeList& msids2 = mSdp->GetMediaSection(1).GetAttributeList().GetMsid(); ASSERT_EQ(2U, msids2.mMsids.size()); - ASSERT_EQ("tracka", msids2.mMsids[0].identifier); - ASSERT_EQ("streama", msids2.mMsids[0].appdata); - ASSERT_EQ("trackb", msids2.mMsids[1].identifier); - ASSERT_EQ("streamb", msids2.mMsids[1].appdata); + ASSERT_EQ("streama", msids2.mMsids[0].identifier); + ASSERT_EQ("tracka", msids2.mMsids[0].appdata); + ASSERT_EQ("streamb", msids2.mMsids[1].identifier); + ASSERT_EQ("trackb", msids2.mMsids[1].appdata); const SdpMsidAttributeList& msids3 = mSdp->GetMediaSection(2).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids3.mMsids.size());