Bug 863306: Propagate RTCP_MUX Status to pipeline via VCM. r=abr

This commit is contained in:
Suhas Nandakumar 2013-06-27 18:08:20 -07:00
parent 5a2c890c2d
commit 21880a4f7a
9 changed files with 238 additions and 41 deletions

View File

@ -1405,12 +1405,14 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id,
return VCM_ERROR;
}
mozilla::RefPtr<TransportFlow> rtcp_flow =
vcmCreateTransportFlow(pc.impl(), level, true,
fingerprint_alg, fingerprint);
if (!rtcp_flow) {
CSFLogError( logTag, "Could not create RTCP flow");
return VCM_ERROR;
mozilla::RefPtr<TransportFlow> rtcp_flow = nullptr;
if(!attrs->rtcp_mux) {
rtcp_flow = vcmCreateTransportFlow(pc.impl(), level, true,
fingerprint_alg, fingerprint);
if (!rtcp_flow) {
CSFLogError( logTag, "Could not create RTCP flow");
return VCM_ERROR;
}
}
if (CC_IS_AUDIO(mcap_id)) {
@ -2046,12 +2048,14 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
CSFLogError( logTag, "Could not create RTP flow");
return VCM_ERROR;
}
mozilla::RefPtr<TransportFlow> rtcp_flow =
vcmCreateTransportFlow(pc.impl(), level, true,
fingerprint_alg, fingerprint);
if (!rtcp_flow) {
mozilla::RefPtr<TransportFlow> rtcp_flow = nullptr;
if(!attrs->rtcp_mux) {
rtcp_flow = vcmCreateTransportFlow(pc.impl(), level, true,
fingerprint_alg, fingerprint);
if (!rtcp_flow) {
CSFLogError( logTag, "Could not create RTCP flow");
return VCM_ERROR;
}
}
if (CC_IS_AUDIO(mcap_id)) {

View File

@ -122,6 +122,10 @@ class MediaPipeline : public sigslot::has_slots<> {
virtual Direction direction() const { return direction_; }
bool IsDoingRtcpMux() const {
return (rtp_transport_ == rtcp_transport_);
}
int rtp_packets_sent() const { return rtp_packets_sent_; }
int rtcp_packets_sent() const { return rtcp_packets_sent_; }
int rtp_packets_received() const { return rtp_packets_received_; }

View File

@ -443,6 +443,20 @@ PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
}
// This method exists for the unittests.
// It allows visibility into the pipelines and flows.
// It returns NULL if no pipeline exists for this track number.
mozilla::RefPtr<mozilla::MediaPipeline>
SourceStreamInfo::GetPipeline(int aTrack) {
std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
mPipelines.find(aTrack);
if (it == mPipelines.end()) {
return NULL;
}
return it->second;
}
void
LocalSourceStreamInfo::StorePipeline(int aTrack,

View File

@ -162,17 +162,42 @@ class Fake_VideoGenerator {
#endif
// TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
// bug 837539.
class LocalSourceStreamInfo {
class SourceStreamInfo {
public:
typedef mozilla::DOMMediaStream DOMMediaStream;
LocalSourceStreamInfo(DOMMediaStream* aMediaStream, PeerConnectionMedia *aParent)
: mMediaStream(aMediaStream), mParent(aParent) {
MOZ_ASSERT(aMediaStream);
SourceStreamInfo(DOMMediaStream* aMediaStream,
PeerConnectionMedia *aParent)
: mMediaStream(aMediaStream),
mParent(aParent) {
MOZ_ASSERT(mMediaStream);
}
SourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
PeerConnectionMedia *aParent)
: mMediaStream(aMediaStream),
mParent(aParent) {
MOZ_ASSERT(mMediaStream);
}
mozilla::RefPtr<mozilla::MediaPipeline> GetPipeline(int aTrack);
protected:
std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
nsRefPtr<DOMMediaStream> mMediaStream;
PeerConnectionMedia *mParent;
};
// TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
// bug 837539.
class LocalSourceStreamInfo : public SourceStreamInfo {
public:
typedef mozilla::DOMMediaStream DOMMediaStream;
LocalSourceStreamInfo(DOMMediaStream *aMediaStream,
PeerConnectionMedia *aParent)
: SourceStreamInfo(aMediaStream, aParent) {}
~LocalSourceStreamInfo() {
mMediaStream = NULL;
}
@ -191,25 +216,18 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo)
private:
std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
nsRefPtr<DOMMediaStream> mMediaStream;
nsTArray<mozilla::TrackID> mAudioTracks;
nsTArray<mozilla::TrackID> mVideoTracks;
PeerConnectionMedia *mParent;
};
class RemoteSourceStreamInfo {
class RemoteSourceStreamInfo : public SourceStreamInfo {
public:
typedef mozilla::DOMMediaStream DOMMediaStream;
RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
PeerConnectionMedia *aParent)
: mTrackTypeHints(0),
mMediaStream(aMediaStream),
mPipelines(),
mParent(aParent) {
MOZ_ASSERT(mMediaStream);
}
RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
PeerConnectionMedia *aParent)
: SourceStreamInfo(aMediaStream, aParent),
mTrackTypeHints(0) {}
DOMMediaStream* GetMediaStream() {
return mMediaStream;
@ -225,10 +243,7 @@ RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
public:
DOMMediaStream::TrackTypeHints mTrackTypeHints;
private:
nsRefPtr<DOMMediaStream> mMediaStream;
std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
std::map<int, bool> mTypes;
PeerConnectionMedia *mParent;
};
class PeerConnectionMedia : public sigslot::has_slots<> {

View File

@ -4734,12 +4734,14 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial
/*
* Negotiate rtcp-mux
*/
if(SDP_MEDIA_APPLICATION != media_type) {
sdp_res = sdp_attr_get_rtcp_mux_attribute(sdp_p->dest_sdp, i,
0, SDP_ATTR_RTCP_MUX,
1, &rtcp_mux);
sdp_res = sdp_attr_get_rtcp_mux_attribute (sdp_p->dest_sdp, i,
0, SDP_ATTR_RTCP_MUX, 1, &rtcp_mux);
if (SDP_SUCCESS == sdp_res) {
media->rtcp_mux = TRUE;
if (SDP_SUCCESS == sdp_res) {
media->rtcp_mux = TRUE;
}
}
if (!unsupported_line) {
@ -4753,8 +4755,10 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t *fcb_p, cc_sdp_t *sdp_p, boolean initial
sdp_p->src_sdp, media->candidatesp[j]);
}
/* Set RTCPMux if we have it turned on in our config
and the other side requests it */
config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
if (rtcpmux) {
if (rtcpmux && media->rtcp_mux) {
gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, media->level,
sdp_p->src_sdp, TRUE);
}
@ -5275,7 +5279,7 @@ gsmsdp_add_media_line (fsmdef_dcb_t *dcb_p, const cc_media_cap_t *media_cap,
}
config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
if (rtcpmux) {
if (SDP_MEDIA_APPLICATION != media_cap->type && rtcpmux) {
gsmsdp_set_rtcp_mux_attribute (SDP_ATTR_RTCP_MUX, level, dcb_p->sdp->src_sdp, TRUE);
}

View File

@ -984,6 +984,7 @@ lsm_rx_start (lsm_lcb_t *lcb, const char *fname, fsmdef_media_t *media)
media->src_port = open_rcv.port;
}
attrs.rtcp_mux = media->rtcp_mux;
if ( media->cap_index == CC_VIDEO_1 ) {
attrs.video.opaque = media->video;
} else {
@ -1220,6 +1221,8 @@ lsm_tx_start (lsm_lcb_t *lcb, const char *fname, fsmdef_media_t *media)
media->xmit_chan = TRUE;
attrs.mute = FALSE;
attrs.rtcp_mux = media->rtcp_mux;
if ( CC_IS_VIDEO(media->cap_index)) {
attrs.video.opaque = media->video;
if (lcb->vid_mute) {

View File

@ -179,7 +179,7 @@ static const int gDscpCallControl = 1;
static const int gSpeakerEnabled = 1;
static const char gExternalNumberMask[] = "";
static const char gVersion[] = "0.1";
static const boolean gRTCPMUX = FALSE;
static const boolean gRTCPMUX = TRUE;
static boolean gRTPSAVPF = TRUE; /* TRUE = RTP/SAVPF , FALSE = RTP/SAVP */
static const boolean gMAXAVBITRATE = FALSE; /* Following six are OPUS fmtp options */
static const boolean gMAXCODEDAUDIOBW = FALSE;

View File

@ -331,6 +331,7 @@ typedef struct vcm_audioAttrs_t_ {
typedef struct vcm_attrs_t_ {
cc_boolean mute;
cc_boolean is_video;
cc_boolean rtcp_mux;
vcm_audioAttrs_t audio; /**< audio line attribs */
vcm_videoAttrs_t video; /**< Video Atrribs */
} vcm_mediaAttrs_t;

View File

@ -137,6 +137,15 @@ enum offerAnswerFlags
ANSWER_AV = ANSWER_AUDIO | ANSWER_VIDEO
};
enum mediaPipelineFlags
{
PIPELINE_LOCAL = (1<<0),
PIPELINE_RTCP_MUX = (1<<1),
PIPELINE_SEND = (1<<2),
PIPELINE_VIDEO = (1<<3)
};
static bool SetupGlobalThread() {
if (!gThread) {
nsIThread *thread;
@ -455,7 +464,14 @@ class ParsedSDP {
Parse();
}
void DeleteLine(std::string objType)
{
ReplaceLine(objType, "");
}
// Replaces the first instance of objType in the SDP with
// a new string.
// If content is an empty string then the line will be removed
void ReplaceLine(std::string objType, std::string content)
{
std::multimap<std::string, SdpLine>::iterator it;
@ -464,6 +480,9 @@ class ParsedSDP {
SdpLine sdp_line_pair = (*it).second;
int line_no = sdp_line_pair.first;
sdp_map_.erase(it);
if(content.empty()) {
return;
}
std::string value = content.substr(objType.length());
sdp_map_.insert(std::pair<std::string, SdpLine>(objType,
std::make_pair(line_no,value)));
@ -752,7 +771,7 @@ void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer,
DONT_CHECK_VIDEO|
DONT_CHECK_DATA,
sipcc::PeerConnectionImpl::SignalingState endState =
sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer) {
sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer) {
uint32_t aHintContents = 0;
if (offerAnswerFlags & ANSWER_AUDIO) {
@ -935,6 +954,42 @@ void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer,
}
}
mozilla::RefPtr<mozilla::MediaPipeline> GetMediaPipeline(
bool local, int stream, int track) {
sipcc::SourceStreamInfo *streamInfo;
if (local) {
streamInfo = pc->media()->GetLocalStream(stream);
} else {
streamInfo = pc->media()->GetRemoteStream(stream);
}
if (!streamInfo) {
return nullptr;
}
return streamInfo->GetPipeline(track);
}
void CheckMediaPipeline(int stream, int track, uint32_t flags) {
mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track);
ASSERT_TRUE(pipeline);
ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX));
// We cannot yet test send/recv with video.
if (!(flags & PIPELINE_VIDEO)) {
if (flags & PIPELINE_SEND) {
ASSERT_GE(pipeline->rtp_packets_sent(), 40);
ASSERT_GE(pipeline->rtcp_packets_received(), 1);
} else {
ASSERT_GE(pipeline->rtp_packets_received(), 40);
ASSERT_GE(pipeline->rtcp_packets_sent(), 1);
}
}
}
public:
mozilla::RefPtr<sipcc::PeerConnectionImpl> pc;
nsRefPtr<TestObserver> pObserver;
@ -1624,6 +1679,15 @@ TEST_F(SignalingTest, FullCall)
//ASSERT_GE(a2_.GetPacketsSent(0), 40);
//ASSERT_GE(a1_.GetPacketsReceived(0), 40);
ASSERT_GE(a2_.GetPacketsReceived(0), 40);
// 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_RTCP_MUX | PIPELINE_SEND);
// The first Remote pipeline gets stored at 1
a2_.CheckMediaPipeline(0, 1, PIPELINE_RTCP_MUX);
}
TEST_F(SignalingTest, FullCallAudioOnly)
@ -2284,11 +2348,99 @@ TEST_F(SignalingTest, missingUfrag)
a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
a1_.SetLocal(TestObserver::OFFER, offer, true);
// We now detect the missing ICE parameters at SetRemoteDescription
a2_.SetRemote(TestObserver::OFFER, offer, true,
a2_.SetRemote(TestObserver::OFFER, offer, true,
sipcc::PeerConnectionImpl::kSignalingStable);
ASSERT_TRUE(a2_.pObserver->state == TestObserver::stateError);
}
TEST_F(SignalingTest, AudioOnlyCalleeNoRtcpMux)
{
sipcc::MediaConstraints constraints;
a1_.CreateOffer(constraints, OFFER_AUDIO, SHOULD_SENDRECV_AUDIO);
a1_.SetLocal(TestObserver::OFFER, a1_.offer(), false);
ParsedSDP sdpWrapper(a1_.offer());
sdpWrapper.DeleteLine("a=rtcp-mux");
std::cout << "Modified SDP " << std::endl
<< indent(sdpWrapper.getSdp()) << std::endl;
a2_.SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false);
a2_.CreateAnswer(constraints, sdpWrapper.getSdp(),
OFFER_AUDIO | ANSWER_AUDIO);
a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), false);
a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), false);
// Answer should not have a=rtcp-mux
ASSERT_EQ(a2_.getLocalDescription().find("\r\na=rtcp-mux"),
std::string::npos);
ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
PR_Sleep(kDefaultTimeout * 2); // Wait for some data to get written
a1_.CloseSendStreams();
a2_.CloseReceiveStreams();
ASSERT_GE(a1_.GetPacketsSent(0), 40);
ASSERT_GE(a2_.GetPacketsReceived(0), 40);
// 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, 1, 0);
}
TEST_F(SignalingTest, FullCallAudioNoMuxVideoMux)
{
sipcc::MediaConstraints constraints;
a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
a1_.SetLocal(TestObserver::OFFER, a1_.offer(), false);
ParsedSDP sdpWrapper(a1_.offer());
sdpWrapper.DeleteLine("a=rtcp-mux");
std::cout << "Modified SDP " << std::endl
<< indent(sdpWrapper.getSdp()) << std::endl;
a2_.SetRemote(TestObserver::OFFER, sdpWrapper.getSdp(), false);
a2_.CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV);
a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), false);
a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), false);
// Answer should have only one a=rtcp-mux line
size_t match = a2_.getLocalDescription().find("\r\na=rtcp-mux");
ASSERT_NE(match, std::string::npos);
match = a2_.getLocalDescription().find("\r\na=rtcp-mux", match + 1);
ASSERT_EQ(match, std::string::npos);
ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
PR_Sleep(kDefaultTimeout * 2); // Wait for some data to get written
a1_.CloseSendStreams();
a2_.CloseReceiveStreams();
ASSERT_GE(a1_.GetPacketsSent(0), 40);
ASSERT_GE(a2_.GetPacketsReceived(0), 40);
// 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);
// Now check video mux.
a1_.CheckMediaPipeline(0, 1,
PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND | PIPELINE_VIDEO);
// The first Remote pipeline gets stored at 1
a2_.CheckMediaPipeline(0, 1, 0);
// Now check video mux.
a2_.CheckMediaPipeline(0, 2, PIPELINE_RTCP_MUX | PIPELINE_VIDEO);
}
} // End namespace test.
bool is_color_terminal(const char *terminal) {