rtperror UpdateMemberInfoByBYE(_RTP_CONTEXT *the_context, rtcp_packet *the_packet, struct sockaddr *fromaddr, int addrlen) { member *pkt_origin_member; u_int32 cur_ssrc; unsigned int ssrc_iter; int i; rtcp_bye_block bb; /* Copy the reason code (non null terminated) from the packet into the context variable, so it can be returned to the callback */ for(i = 0; i < the_packet->variant.bye.reason_length; i++) { the_context->byereason[i] = the_packet->variant.bye.reason[i]; } the_context->byereason[i] = '\0'; /* Update info for the various members */ for (ssrc_iter=0; ssrc_iter < RTCP_RC(*the_packet->common); ssrc_iter++){ bb = RTPGetByeBlock(the_packet, ssrc_iter); cur_ssrc = bb.ssrccsrc; pkt_origin_member = UpdateTimeOrCreateMember(the_context, cur_ssrc, fromaddr, addrlen, TRUE, RTP_MEMBER_PENDING); /* The member that is about to go BYE has been placed on the membership list if it wasn't already there, and timestamped with the current time (since we just received a BYE packet for it, the current time is appropriate). We now destroy it unless the BYE was (believed to be) due to a collision and the CNAME is known. In such a case, we leave the member on the member-list. Note that the remove will fail if the member is in the CSRC list */ if (!pkt_origin_member->colliding || pkt_origin_member->sdes_info[0] == NULL){ RemoveMember(the_context, pkt_origin_member); if (the_context->UpdateMemberCallBack != NULL){ the_context->UpdateMemberCallBack(the_context->context_num, pkt_origin_member->unique_id, RTP_FLAG_MEMBER_LEAVES, the_context->byereason); } DestroyMember(the_context, pkt_origin_member); } } return(RTP_OK); }
static int rtcp_parse(struct rtp_context *ctx, const unsigned char* data, size_t bytes) { uint32_t rtcphd; rtcp_header_t header; assert(bytes >= sizeof(rtcphd)); rtcphd = nbo_r32(data); header.v = RTCP_V(rtcphd); header.p = RTCP_P(rtcphd); header.rc = RTCP_RC(rtcphd); header.pt = RTCP_PT(rtcphd); header.length = RTCP_LEN(rtcphd); assert((header.length+1)*4 <= bytes); assert(2 == header.v); // 1. RTP version field must equal 2 (p69) // 2. The payload type filed of the first RTCP packet in a compound packet must be SR or RR (p69) // 3. padding only valid at the last packet if(1 == header.p) { assert((header.length+1)*4 + 4 <= bytes); header.length -= *(data + header.length - 1) * 4; } switch(header.pt) { case RTCP_SR: rtcp_sr_unpack(ctx, &header, data+4); break; case RTCP_RR: rtcp_rr_unpack(ctx, &header, data+4); break; case RTCP_SDES: rtcp_sdes_unpack(ctx, &header, data+4); break; case RTCP_BYE: rtcp_bye_unpack(ctx, &header, data+4); break; case RTCP_APP: rtcp_app_unpack(ctx, &header, data+4); break; default: assert(0); } return (header.length+1)*4; }
rtperror UpdateMemberInfoByRR(_RTP_CONTEXT *the_context, rtcp_packet *the_packet, struct sockaddr *fromaddr, int addrlen) { member *pkt_origin_member; rtcp_report_block the_block; struct timeval tvalptr; unsigned int ssrc_iter; gettimeofday(&tvalptr, NULL); pkt_origin_member = UpdateTimeOrCreateMember(the_context, the_packet->variant.rr.rr_fixed->ssrc, fromaddr, addrlen, TRUE, RTP_MEMBER_PENDING); pkt_origin_member->recv_rtp_count++; /* Now deal with member state changes. The member should move to pending if it has sent two RTP packets */ if(((pkt_origin_member->recv_rtp_count > 1) && (pkt_origin_member->status == RTP_MEMBER_PENDING)) || (pkt_origin_member->status == RTP_MEMBER_EXPIRED)){ ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED); } for (ssrc_iter=0; ssrc_iter < RTCP_RC(*the_packet->common); ssrc_iter++){ member *reported_sender; the_block = RTPGetReportBlock(the_packet, ssrc_iter); /* NOTE: We don't update sender times based on the report block, because 2 receivers could perpetually update each other's sender list even after the sender has left. */ /* We create a member that is on the SSRC list if it is not already part of the session */ if ((reported_sender = GetMemberFromSSRCHash(the_context, the_block.ssrc)) == NULL) { reported_sender = EstablishNewMember(the_context, the_block.ssrc, NULL, fromaddr, TRUE, RTP_MEMBER_PENDING); } UpdateSenderTime(the_context, reported_sender, tvalptr, 0); Update_RR(pkt_origin_member, reported_sender, &the_block); } return(RTP_OK); }
rtperror UpdateMemberInfoBySDES(_RTP_CONTEXT *the_context, rtcp_packet *the_packet, struct sockaddr *fromaddr, int addrlen) { member *pkt_origin_member = NULL; member *prev_member; rtcp_sdes_item sdes_item; char **member_item; char the_item[100]; u_int32 cur_ssrc, prev_ssrc; struct timeval tvalptr; unsigned int ssrc_iter; gettimeofday(&tvalptr, NULL); if (RTCP_RC(*the_packet->common)==0){ return(RTP_OK); } ssrc_iter = 0; sdes_item = InitSDESItemIter(the_packet); /* Make prev_ssrc not match initial ssrc. This is so the member will be retrieved*/ prev_ssrc = sdes_item.ssrc + 1; /* The item pointed to does not represent the end of all items in the packet until it's type has value 0 and its chunk # = that of the rc field of the sdes packet. */ while (sdes_item.type != 0 || ssrc_iter < RTCP_RC(*the_packet->common)){ /* only has stuff worth processing if the time type is not 0. */ if (sdes_item.type > 0 && sdes_item.type < _RTP_NUM_SDES_TYPES + 1){ cur_ssrc = sdes_item.ssrc; if (cur_ssrc != prev_ssrc){ /* NOTE: This will update the membership time of every member that contains info in the SDES packet */ if(ssrc_iter == 0) { pkt_origin_member = UpdateTimeOrCreateMember(the_context, cur_ssrc, fromaddr, addrlen, TRUE, RTP_MEMBER_PENDING); pkt_origin_member->recv_rtp_count++; /* Now deal with member state changes. The member should move to pending if it has sent two RTP packets */ if(((pkt_origin_member->recv_rtp_count > 1) && (pkt_origin_member->status == RTP_MEMBER_PENDING)) || (pkt_origin_member->status == RTP_MEMBER_EXPIRED)){ ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED); } } else { pkt_origin_member = UpdateTimeOrCreateMember(the_context, cur_ssrc, fromaddr, addrlen, TRUE, RTP_MEMBER_PENDING_CONTRIBUTOR); pkt_origin_member->recv_csrc_count++; /* Now deal with member state changes. The member should move to pending if it has sent two RTP packets */ if(((pkt_origin_member->recv_csrc_count > 1) && (pkt_origin_member->status == RTP_MEMBER_PENDING_CONTRIBUTOR)) || (pkt_origin_member->status == RTP_MEMBER_EXPIRED_CONTRIBUTOR)){ ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED_CONTRIBUTOR); } } } /* set member_item to point to the location where we store the SDES item for the member. */ member_item = pkt_origin_member->sdes_info + sdes_item.type - 1; if (*member_item != NULL){ /* Previous SDES info for this field existed. If the field remains the same, then do not make a change. */ if (strncmp(*member_item, sdes_item.description, sdes_item.len) != 0){ if (sdes_item.type == 1){ /* If we get here, then we found a member with the same SSRC but different CNAME. We need to search through the rest of the colliding members to see if we can find a member with the same address and CNAME -- i.e. if we've already seen and handled this collision. If not, handle the collision now. If so, just have pkt_origin_member point to the found member. */ #ifdef _RTP_DEBUG printf("Members with different CNAMEs collide\n"); #endif while (pkt_origin_member->collides_with_next != NULL){ if (strncmp(*member_item, sdes_item.description, sdes_item.len) == 0){ return(RTP_OK); } pkt_origin_member = pkt_origin_member->collides_with_next; member_item = pkt_origin_member->sdes_info + sdes_item.type - 1; } #ifdef _RTP_DEBUG printf("Handle1...\n"); #endif pkt_origin_member = HandleSSRCCollision(the_context, pkt_origin_member, fromaddr, TRUE, the_item,ssrc_iter); } else { /* We are changing an SDES item other than CNAME. Free the old one here, and put the new one in below. */ free(*member_item); *member_item = NULL; } } if (sdes_item.type == 1 && fromaddr->sa_family != _RTP_ADDRESS_NOT_YET_KNOWN) { /* Either we've found the member with this CNAME and SDES, or we've just created a new member. Check to see if the member's addr is the same as the packet's fromaddr; if not, announce an IP address change for the member. */ if (pkt_origin_member->my_addr[1].sa_family == _RTP_ADDRESS_NOT_YET_KNOWN || FromDifferentSource(fromaddr, &pkt_origin_member->my_addr[1], addrlen)) { pkt_origin_member->my_addr[1] = *fromaddr; if (the_context->ChangedMemberSockaddrCallBack != NULL && pkt_origin_member->unique_id != 0) { the_context-> ChangedMemberSockaddrCallBack(the_context->context_num, pkt_origin_member->unique_id, &pkt_origin_member->my_addr[1], TRUE); } } } } else if (sdes_item.type == 1){ /* CNAME was previously NULL for member, so we must check to see if the CNAME has already been used. If so, we need to finish up moving a member due to a collision. If not, we note that this is a new member. */ strncpy(the_item, sdes_item.description, sdes_item.len); the_item[sdes_item.len] = '\0'; prev_member = GetMemberFromCNAMEHash(the_context, the_item); if (prev_member != NULL){ /* CNAME already used. */ if (prev_member == the_context->my_memberinfo) { /* The already-used CNAME was our own! Through error or malice or mis-configuration, we've received a packet which didn't come from our IP address or have our ssrc, but which has our CNAME. Assume this was a looped back packet. */ return errordebug(RTP_PACKET_LOOPBACK, "UpdateMemberInfoByRTCP", "context %d, got a foreign packet with our CNAME", (int)the_context->context_num); } /* CNAMEs are unique. Thus, we can be sure that we have retrieved the sole member with the same CNAME, and that these 2 members should be merged. */ #ifdef _RTP_DEBUG printf ("*** Going to merge members... ***\n"); #endif pkt_origin_member = MergeMembers(the_context, prev_member, pkt_origin_member); member_item = pkt_origin_member->sdes_info + sdes_item.type - 1; } } /* Whether we just freed memory or it has never been allocated, now we add the new SDES item */ if (*member_item == NULL){ *member_item = malloc(sdes_item.len + 1); strncpy(*member_item, sdes_item.description, sdes_item.len); (*member_item)[sdes_item.len] = '\0'; if (sdes_item.type == 1){ /* Item is a CNAME. CNAME data has just come in for this member so we add the member to the CNAME hash at this point */ EnterCNAMEHashTable(the_context, pkt_origin_member); /* Change status to confirmed since we've got cname */ if((ssrc_iter == 0) && (pkt_origin_member->status != RTP_MEMBER_CONFIRMED)) { ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED); } else if(ssrc_iter != 0) { /* This is a CSRC who has a new CNAME. They're now a confirmed contributor */ ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED_CONTRIBUTOR); } } if (the_context->ChangedMemberInfoCallBack != NULL) { the_context-> ChangedMemberInfoCallBack(the_context->context_num, pkt_origin_member->unique_id, sdes_item.type, *member_item, RTP_FLAG_MEMBER_INFO_CHANGES); } } prev_ssrc = cur_ssrc; } else (ssrc_iter++); if (ssrc_iter < RTCP_RC(*the_packet->common)){ sdes_item = GetNextItem(&sdes_item); } } return(RTP_OK); }
rtperror UpdateMemberInfoBySR(_RTP_CONTEXT *the_context, rtcp_packet *the_packet, struct sockaddr *fromaddr, int addrlen) { member *pkt_origin_member; rtcp_report_block the_block; struct timeval tvalptr; unsigned int ssrc_iter; gettimeofday(&tvalptr, NULL); pkt_origin_member = UpdateTimeOrCreateMember(the_context, the_packet->variant.sr.sr_fixed->ssrc, fromaddr, addrlen, TRUE, RTP_MEMBER_PENDING); pkt_origin_member->recv_rtp_count++; /* Now deal with member state changes. The member should move to pending if it has sent two RT(c)P packets */ if(((pkt_origin_member->recv_rtp_count > 1) && (pkt_origin_member->status == RTP_MEMBER_PENDING)) || (pkt_origin_member->status == RTP_MEMBER_EXPIRED)){ ChangeMemberStatus(the_context,pkt_origin_member,RTP_MEMBER_CONFIRMED); } UpdateSenderTime(the_context, pkt_origin_member, tvalptr, 0); /* update remaining fields based on receipt of packet */ /* NTP timestamp */ pkt_origin_member->ntp_timestamp.secs = the_packet->variant.sr.sr_fixed->ntp_secs; pkt_origin_member->ntp_timestamp.frac = the_packet->variant.sr.sr_fixed->ntp_frac; /* RTP timestamp */ pkt_origin_member->rtp_timestamp = the_packet->variant.sr.sr_fixed->rtp_stamp; /* pkt and oct counts */ pkt_origin_member->pkt_count = the_packet->variant.sr.sr_fixed->pkt_count; pkt_origin_member->oct_count = the_packet->variant.sr.sr_fixed->oct_count; /* For each sender report block, update sender info as held by this member */ for (ssrc_iter=0; ssrc_iter < RTCP_RC(*the_packet->common); ssrc_iter++) { member *reported_sender; the_block = RTPGetReportBlock(the_packet, ssrc_iter); /* NOTE: We don't update sender times based on the report block, because 2 receivers could perpetually update each other's sender list even after the sender has left. */ /* We create members from the SSRC list if they are not already part of the session */ if ((reported_sender = GetMemberFromSSRCHash(the_context, the_block.ssrc)) == NULL) { reported_sender = EstablishNewMember(the_context, the_block.ssrc, NULL, fromaddr, TRUE, RTP_MEMBER_PENDING); } UpdateSenderTime(the_context, reported_sender, tvalptr, 0); Update_RR(pkt_origin_member, reported_sender, &the_block); } /* update our local info about the member's last SR send time */ pkt_origin_member->last_sr_receipt_time = tvalptr; return(RTP_OK); }