Skip to content

Commit

Permalink
Fixed detection of incoming RTCP packets (audio vs video) when remote…
Browse files Browse the repository at this point in the history
… SSRC is unknown (issue meetecho#258)
  • Loading branch information
meetecho committed Jun 11, 2015
1 parent 9fad3f1 commit 11a33f5
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 38 deletions.
54 changes: 48 additions & 6 deletions ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -1424,11 +1424,41 @@ void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_i
/* Easy enough */
video = (stream->stream_id == handle->video_id ? 1 : 0);
} else {
/* TODO Bundled streams, check SSRC */
guint32 rtcp_ssrc = janus_rtcp_get_sender_ssrc(buf, len);
video = (stream->video_ssrc_peer == rtcp_ssrc ? 1 : 0);
//~ JANUS_LOG(LOG_VERB, "[RTCP] Bundling: this is %s (video=%"SCNu64", audio=%"SCNu64", got %ld)\n",
//~ video ? "video" : "audio", stream->video_ssrc_peer, stream->audio_ssrc_peer, rtcp_ssrc);
/* Bundled streams, should we check the SSRCs? */
if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
/* No audio has been negotiated, definitely video */
JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is video (no audio has been negotiated)\n", handle->handle_id);
video = 1;
} else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
/* No video has been negotiated, definitely audio */
JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is audio (no video has been negotiated)\n", handle->handle_id);
video = 0;
} else {
if(stream->audio_ssrc_peer == 0 || stream->video_ssrc_peer == 0) {
/* We don't know the remote SSRC: this can happen for recvonly clients
* (see https://groups.google.com/forum/#!topic/discuss-webrtc/5yuZjV7lkNc)
* Check the local SSRC, compare it to what we have */
guint32 rtcp_ssrc = janus_rtcp_get_receiver_ssrc(buf, len);
if(rtcp_ssrc == stream->audio_ssrc) {
video = 0;
} else if(rtcp_ssrc == stream->video_ssrc) {
video = 1;
} else {
/* Mh, no SR or RR? Try checking if there's any FIR, PLI or REMB */
if(janus_rtcp_has_fir(buf, len) || janus_rtcp_has_pli(buf, len) || janus_rtcp_get_remb(buf, len)) {
video = 1;
}
}
JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is %s (local SSRC: video=%"SCNu32", audio=%"SCNu32", got %"SCNu32")\n",
handle->handle_id, video ? "video" : "audio", stream->video_ssrc, stream->audio_ssrc, rtcp_ssrc);
} else {
/* Check the remote SSRC, compare it to what we have */
guint32 rtcp_ssrc = janus_rtcp_get_sender_ssrc(buf, len);
video = (stream->video_ssrc_peer == rtcp_ssrc ? 1 : 0);
JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Incoming RTCP, bundling: this is %s (remote SSRC: video=%"SCNu32", audio=%"SCNu32", got %"SCNu32")\n",
handle->handle_id, video ? "video" : "audio", stream->video_ssrc_peer, stream->audio_ssrc_peer, rtcp_ssrc);
}
}
}
gint64 now = janus_get_monotonic_time();
GSList *nacks = janus_rtcp_get_nacks(buf, buflen);
Expand Down Expand Up @@ -1918,8 +1948,20 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);

/* Note: in case this is not an OFFER, we don't know whether DataChannels are supported on the other side or not yet */
/* Note: in case this is not an OFFER, we don't know whether any medium are supported on the other side or not yet */
if(audio) {
janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
} else {
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
}
if(video) {
janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
} else {
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
}
if(data) {
janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
} else {
Expand Down
2 changes: 2 additions & 0 deletions ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ typedef struct janus_ice_queued_packet janus_ice_queued_packet;
#define JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS (1 << 10)
#define JANUS_ICE_HANDLE_WEBRTC_PLAN_B (1 << 11)
#define JANUS_ICE_HANDLE_WEBRTC_CLEANING (1 << 12)
#define JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO (1 << 13)
#define JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO (1 << 14)


/*! \brief Janus media statistics
Expand Down
2 changes: 2 additions & 0 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -2390,6 +2390,8 @@ int janus_process_incoming_admin_request(janus_request_source *source, json_t *r
json_object_set_new(flags, "all-trickles", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)));
json_object_set_new(flags, "trickle-synced", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED)));
json_object_set_new(flags, "data-channels", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)));
json_object_set_new(flags, "has-audio", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)));
json_object_set_new(flags, "has-video", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)));
json_object_set_new(flags, "plan-b", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B)));
json_object_set_new(flags, "cleaning", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)));
json_object_set_new(info, "flags", flags);
Expand Down
43 changes: 43 additions & 0 deletions rtcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,49 @@ guint32 janus_rtcp_get_sender_ssrc(char *packet, int len) {
return 0;
}

guint32 janus_rtcp_get_receiver_ssrc(char *packet, int len) {
if(packet == NULL || len == 0)
return 0;
rtcp_header *rtcp = (rtcp_header *)packet;
if(rtcp->version != 2)
return 0;
int pno = 0, total = len;
while(rtcp) {
pno++;
switch(rtcp->type) {
case RTCP_SR: {
/* SR, sender report */
rtcp_sr *sr = (rtcp_sr*)rtcp;
if(sr->header.rc > 0) {
return ntohl(sr->rb[0].ssrc);
}
break;
}
case RTCP_RR: {
/* RR, receiver report */
rtcp_rr *rr = (rtcp_rr*)rtcp;
if(rr->header.rc > 0) {
return ntohl(rr->rb[0].ssrc);
}
break;
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0) {
break;
}
total -= length*4+4;
if(total <= 0) {
break;
}
rtcp = (rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}

int janus_rtcp_fix_ssrc(char *packet, int len, int fixssrc, uint32_t newssrcl, uint32_t newssrcr) {
if(packet == NULL || len == 0)
return -1;
Expand Down
5 changes: 5 additions & 0 deletions rtcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ typedef struct rtcp_fb
* @param[in] len The message data length in bytes
* @returns The sender SSRC, or 0 in case of error */
guint32 janus_rtcp_get_sender_ssrc(char *packet, int len);
/*! \brief Method to quickly retrieve the received SSRC (needed for demuxing RTCP in BUNDLE)
* @param[in] packet The message data
* @param[in] len The message data length in bytes
* @returns The receiver SSRC, or 0 in case of error */
guint32 janus_rtcp_get_receiver_ssrc(char *packet, int len);

/*! \brief Method to parse/validate an RTCP message
* @param[in] packet The message data
Expand Down
82 changes: 50 additions & 32 deletions sdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,48 +142,66 @@ int janus_sdp_parse(janus_ice_handle *handle, janus_sdp *sdp) {
sdp_media_t *m = remote_sdp->sdp_media;
while(m) {
/* What media type is this? */
if(m->m_type == sdp_media_audio && m->m_port > 0) {
audio++;
if(audio > 1) {
m = m->m_next;
continue;
}
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
} else if(m->m_type == sdp_media_video && m->m_port > 0) {
video++;
if(video > 1) {
m = m->m_next;
continue;
}
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
if(m->m_type == sdp_media_audio) {
if(m->m_port > 0) {
audio++;
if(audio > 1) {
m = m->m_next;
continue;
}
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
} else {
gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
/* Audio rejected? */
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
}
#ifdef HAVE_SCTP
} else if(m->m_type == sdp_media_application) {
/* Is this SCTP for DataChannels? */
if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port > 0) {
/* Yep */
data++;
if(data > 1) {
} else if(m->m_type == sdp_media_video) {
if(m->m_port > 0) {
video++;
if(video > 1) {
m = m->m_next;
continue;
}
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
} else {
gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
}
if(stream == NULL) {
JANUS_LOG(LOG_WARN, "No valid stream for data??\n");
continue;
} else {
/* Video rejected? */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video rejected by peer...\n", handle->handle_id);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
}
#ifdef HAVE_SCTP
} else if(m->m_type == sdp_media_application) {
/* Is this SCTP for DataChannels? */
if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP")) {
if(m->m_port > 0) {
/* Yep */
data++;
if(data > 1) {
m = m->m_next;
continue;
}
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
} else {
gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
}
if(stream == NULL) {
JANUS_LOG(LOG_WARN, "No valid stream for data??\n");
continue;
}
}
} else {
/* Data channels rejected? */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels rejected by peer...\n", handle->handle_id);
janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
}
#endif
} else {
Expand Down

0 comments on commit 11a33f5

Please sign in to comment.