The following code is a test program that initiates a single member of an RTP session that is able to send and receive RTP packets, displays all packets received, and periodically displays membership information. The member initiates its CSRC list with 3 imaginary contributing members (whose SSRCs are chosen randomly). The code is fully functional except that the encryption / decryption algorithms have been removed since the code used cannot be made publicly available.
#include <stdlib.h> #include <stdio.h> #include <sys/time.h> #include <string.h> #include <math.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/uio.h> #include "rtp_api.h" #include "rtp_crypt.h" #define DEFAULT_MULTI "224.119.10.10" #define DEFAULT_PORT 5076 #define RTPSTAMPRATE 1.0 #define MAX_SCHEDULE 100 #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif
typedef struct { int Active; struct timeval activate_time; int32 opaque; } sched_item;
sched_item sched[MAX_SCHEDULE];
void InitSched(){ int i; for (i=0; i<MAX_SCHEDULE; i++){ sched[i].Active = FALSE; } return; }
int FindInactive(){ int i; for (i=0; i < MAX_SCHEDULE;i++){ if (!sched[i].Active){ return i; } } return -1; }
int FindReady(){ int i; struct timeval curtime; gettimeofday(&curtime, NULL); for (i=0; i<MAX_SCHEDULE; i++){ if (sched[i].Active && (sched[i].activate_time.tv_sec < curtime.tv_sec || (sched[i].activate_time.tv_sec == curtime.tv_sec && sched[i].activate_time.tv_usec < curtime.tv_usec))){ sched[i].Active = FALSE; return i; } } return -1; }
void RTPSchedule(context cid, int32 opaque, struct timeval *tp){ struct timeval curtime; int i = FindInactive(); if (i < 0){ printf("error: no more room in scheduler\n"); exit(1); } sched[i].Active = TRUE; sched[i].activate_time = *tp; sched[i].opaque = opaque; gettimeofday(&curtime, NULL); printf("Scheduled for %f seconds\n", (float) (tp->tv_sec - curtime.tv_sec) + (float) (tp->tv_usec - curtime.tv_usec) / 1000000); return; }
void ReportNewMember(context cid, person id_no, unsigned int flags){ int32 new_ssrc; int err; err = RTPMemberInfoGetSSRC(cid, id_no, &new_ssrc); if (err != RTP_OK){ printf("%s\n", RTPStrError()); } printf("New Member number %ld: SSRC %x\n", id_no, (int)new_ssrc); return; }
void ReportExpMember(context cid, person id_no, unsigned int flags){ int32 new_ssrc; int err; err = RTPMemberInfoGetSSRC(cid, id_no, &new_ssrc); if (err != RTP_OK){ printf("%s\n", RTPStrError()); } printf("Exp Member number %ld: SSRC %x\n", id_no, (int)new_ssrc); return; }
void ReportNewSender(context cid, person id_no, unsigned int flags){ int32 new_ssrc; int err; err = RTPMemberInfoGetSSRC(cid, id_no, &new_ssrc); if (err != RTP_OK){ printf("%s\n", RTPStrError()); } printf("New SENDER number %ld: SSRC %x\n", id_no, (int)new_ssrc); return; }
void ReportExpSender(context cid, person id_no, unsigned int flags){ int32 new_ssrc; int err; err = RTPMemberInfoGetSSRC(cid, id_no, &new_ssrc); if (err != RTP_OK){ printf("%s\n", RTPStrError()); } printf("Exp SENDER number %ld: SSRC %x\n", id_no, (int)new_ssrc); return; }
void ReportChangedMemberInfo(context cid, person id_no, memberinfo field, char *new, char *old, unsigned int flags){ printf("Person %ld: SDES field %d changed from %s to %s\n", id_no, field, old ? old : "(NULL)", new); return; }
void ReportCollision(context cid, person id_1, person id_2, unsigned int flags){ printf("Persons %ld and %ld colliding\n", id_1, id_2); return; }
void DescribeUsage(int help){ fprintf(stderr, "Usage: rtp_test [-help] [-tMin] [-S [tsec]] [-e[1]] [-r[0]] [[addr]/[port]]\n"); if (help){ fprintf(stderr, "-help : this help message\n"); fprintf(stderr, "-tMin: run rtp_test for min minutes\n"); fprintf(stderr, "-S[dsec] : send RTP packets at rate of 10 per dsec seconds. default is 10\n"); fprintf(stderr, "-e : Turn on encryption (-e1 encrypts only SDES info) default is off\n"); fprintf(stderr, "-r : Reconsideration, -r0 turns reconsideration off. Default is on\n"); fprintf(stderr, "[addr]/[port] : address (multicast or unicast) and / or port to use.\n"); fprintf(stderr, "Default address is multicast %s, default port is %d\n", DEFAULT_MULTI, DEFAULT_PORT); } exit(2); }
void main(int argc, char **argv){ char addr[20]; int i, j, parts, ssrc_count, fromlen, count, rtpcount, runtime, encryption, reconsideration, is_sender, sendrate, port; struct timeval curtime, endtime, next_RTP_send, last_RTP_send, tinytime; person cur_mem; member_iterator the_iter; rtcp_bye_block byeblock; double rtptimechange; char usekey[9] = "DESKEY01\0"; char payload[1000], theload[10000], *pktparts[100]; rtp_packet rtppkt; rtcp_packet rtcppkt; rtcp_report_block the_block; rtcp_sdes_item cur_item; struct sockaddr fromaddr; context my_context; int32 csrc_list[10], csrcguy, cur_ssrc; int loaduse; rtperror the_err; socktype rtpsock, rtcpsock; int maxfd = 0; fd_set selection;
encryption = 2;
runtime = 10;
reconsideration = 3;
is_sender = FALSE; sendrate = 10; port = DEFAULT_PORT; sprintf(addr, "%s", DEFAULT_MULTI); for (i=1; i< argc; i++){ if (argv[i][0] == '-'){ if (strlen(argv[i]) == 1){ fprintf(stderr, "rtp_test: option must follow a '-'\n"); DescribeUsage(FALSE); } switch(argv[i][1]){ case 'S': is_sender = TRUE; if (strlen(argv[i])==2){ break; } sendrate = atoi(argv[i]+2); break; case 't': if (strlen(argv[i]) == 2){ fprintf(stderr, "rtp_test: option t requires integer\n"); DescribeUsage(FALSE); } runtime = atoi(argv[i]+2); break; case 'h': DescribeUsage(TRUE); case 'e': encryption = 0; if (strlen(argv[i]) > 2){ if (argv[i][2] == '1'){ encryption = 1; } } break; case 'r': reconsideration = 1; if (strlen(argv[i]) > 2){ if (argv[i][2] == '0'){ reconsideration = 0; } } break; default: fprintf(stderr, "rtp_test: illegal option -- %c\n", argv[i][1]); DescribeUsage(FALSE); } } else { j = 1; if (argv[i][0] != '/'){ strncpy(addr, argv[i], 20); for (j=0; j<20 && addr[j]!='\0'; j++){ if (addr[j] == '/'){ addr[j] = '\0'; j++; break; } } } if (argv[i][j] != '\0'){ port = atoi(argv[i]+j); } } } printf("Using address %s/%d\n", addr, port);
#ifdef _RTP_WATCH_ALLOCATION InitMallocs(); #endif tinytime.tv_sec = 0; tinytime.tv_usec = 100000;
InitSched(); gettimeofday(&curtime, NULL); endtime = curtime; next_RTP_send = curtime; endtime.tv_sec += 60 * runtime;
for (i=0; i<3; i++){ csrc_list[i] = (int32) curtime.tv_usec + i; }
the_err = RTPCreate(&my_context); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSetNewMemberCallBack(my_context, &ReportNewMember); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } the_err = RTPSetExpiredMemberCallBack(my_context, &ReportExpMember); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } the_err = RTPSetNewSenderCallBack(my_context, &ReportNewSender); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } the_err = RTPSetChangedMemberInfoCallBack(my_context, &ReportChangedMemberInfo); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } the_err = RTPSetExpiredSenderCallBack(my_context, &ReportExpSender); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } the_err = RTPSetCollidedMemberCallBack(my_context, &ReportCollision); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionSetAddr(my_context, htonl(inet_addr(addr))); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
#ifdef _RTP_ARCH_FreeBSD the_err = RTPSessionSetLocalAddr(my_context, ntohl(inet_addr("0.0.0.0"))); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } #endif
gethostname(payload, 1000); sprintf(theload, "I am %s:%ld",payload, curtime.tv_usec); the_err = RTPMemberInfoSetSDES(my_context, 0, 1, theload); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionSetCSRCList(my_context, csrc_list, 3); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } for (i=0; i<3; i++){
the_err = RTPSessionGetUniqueIDForCSRC(my_context, csrc_list[i], &csrcguy); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
sprintf(theload, "I am CSRC %d from %s:%ld", i, payload, curtime.tv_usec); the_err = RTPMemberInfoSetSDES(my_context, csrcguy, 1, theload); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } }
the_err = RTPSessionSetPort(my_context, port); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionSetEncryptionFuncs(my_context, &RTPInit, &RTPEncrypt, &RTPDecrypt); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionSetEncryption(my_context, encryption); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionSetReconsideration(my_context, reconsideration);
the_err = RTPSessionSetKey(my_context, (void*) usekey);
the_err = RTPSessionSetRTPStampRate(my_context, 3, RTPSTAMPRATE); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPOpenConnection(my_context); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
the_err = RTPSessionGetRTPSocket(my_context, &rtpsock); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } if (maxfd < rtpsock.sock) maxfd = rtpsock.sock; the_err = RTPSessionGetRTCPSocket(my_context, &rtcpsock); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } if (maxfd < rtcpsock.sock) maxfd = rtcpsock.sock; count = 0; rtpcount = 0;
while (curtime.tv_sec < endtime.tv_sec){
RTPMemberInfoGetSSRC(my_context, 0, &cur_ssrc); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } printf("My SSRC is %x\n", (int)cur_ssrc);
gettimeofday(&curtime, NULL); i = FindReady(); if (i != -1){ RTPExecute(my_context, sched[i].opaque); }
FD_ZERO(&selection); FD_SET(rtpsock.sock, &selection); FD_SET(rtcpsock.sock, &selection); the_err = select(maxfd + 1, &selection, NULL, NULL, &tinytime); if (the_err < 0){ printf("select error: %d\n", errno); exit(1); } if (FD_ISSET(rtpsock.sock, &selection)){
loaduse = 10000;
the_err = RTPReceive(my_context, rtpsock.sock, theload, &loaduse);
if (the_err == RTP_PACKET_LOOPBACK){ printf("Got my own RTP packet\n"); }
else if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
else {
rtppkt = RTPGetRTPPacket(theload, loaduse); rtpcount++;
printf("%d: RTP len=%d v=%d p=%d x=%d cc=%d m=%d pt=%d\n", (int)rtpcount, (int)loaduse, (int)rtppkt.RTP_header->version, (int)rtppkt.RTP_header->p, (int)rtppkt.RTP_header->x, (int)rtppkt.RTP_header->cc, (int)rtppkt.RTP_header->m, (int)rtppkt.RTP_header->pt);
memcpy(payload, rtppkt.payload, (int) rtppkt.payload_len); payload[rtppkt.payload_len] = '\0'; printf("%s\n", payload); } } if (FD_ISSET(rtcpsock.sock, &selection)){
loaduse = 10000;
the_err = RTPReceive(my_context, rtcpsock.sock, theload, &loaduse);
if (the_err == RTP_PACKET_LOOPBACK){ printf("Got my own RTCP packet\n"); }
else if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); }
else { printf("Got RTCP pkt len %d\n", (int)loaduse);
parts = RTPSplitCompoundRTCP(theload, pktparts, loaduse); for (i=0; i < parts; i++){
rtcppkt = RTPGetRTCPPacket(pktparts[i]);
switch (rtcppkt.overlay->pt){ case 200:
printf(" (SR ssrc=0x%x p=%d count=%d len=%d\n", (int)rtcppkt.overlay->rtcp_hdr_variant.sr.ssrc, (int)rtcppkt.overlay->p, (int)rtcppkt.overlay->rc, (int)rtcppkt.overlay->len); printf("ntp=%ld.%ld ts=%ld psent=%ld osent=%ld\n", (long)rtcppkt.overlay->rtcp_hdr_variant.sr.ntp_stamp_msw, (long)rtcppkt.overlay->rtcp_hdr_variant.sr.ntp_stamp_lsw, (long)rtcppkt.overlay->rtcp_hdr_variant.sr.rtp_stamp, (long)rtcppkt.overlay->rtcp_hdr_variant.sr.pkt_count, (long)rtcppkt.overlay->rtcp_hdr_variant.sr.oct_count); for (j=0; j<rtcppkt.overlay->rc;j++){ the_block = RTPGetReportBlock(&rtcppkt, j); printf(" (ssrc=%x fraction=%d lost=%ld last_seq=%ld jit=%ld lsr=%ld dlsr=%ld)\n", (int)the_block.ssrc, (int)the_block.frac_lost, (long)the_block.cum_lost, (long)the_block.highest_seqno, (long)the_block.jitter, (long)the_block.lsr, (long)the_block.dlsr); } printf(" )\n"); break; case 201:
printf(" (RR ssrc=0x%x p=%d count=%d len=%d\n", (int)rtcppkt.overlay->rtcp_hdr_variant.rr.ssrc, (int)rtcppkt.overlay->p, (int)rtcppkt.overlay->rc, (int)rtcppkt.overlay->len); for (j=0; j<rtcppkt.overlay->rc;j++){ the_block = RTPGetReportBlock(&rtcppkt, j); printf(" (ssrc=%x fraction=%d lost=%ld last_seq=%ld jit=%ld lsr=%ld dlsr=%ld)\n", (int)the_block.ssrc, (int)the_block.frac_lost, (long)the_block.cum_lost, (long)the_block.highest_seqno, (long)the_block.jitter, (long)the_block.lsr, (long)the_block.dlsr); } printf(" )\n"); break; case 202:
printf(" (SDES p=%d count=%d len=%d\n", (int)rtcppkt.overlay->p, (int)rtcppkt.overlay->rc, (int)rtcppkt.overlay->len); if (rtcppkt.overlay->rc == 0){ printf(" )\n"); break; } cur_ssrc = 0; ssrc_count = 0; cur_item = InitSDESItemIter(&rtcppkt); while (ssrc_count < rtcppkt.overlay->rc && cur_item.type != 0){ if (cur_ssrc != cur_item.ssrc){ cur_ssrc = cur_item.ssrc; printf("(src=0x%x ", (int)cur_item.ssrc); } printf("F%d=", cur_item.type); for (j=0; j<cur_item.len;j++){ printf("%c",cur_item.description[j]); } printf(" "); cur_item = GetNextItem(&cur_item); while (cur_item.type == 0 && ssrc_count < rtcppkt.overlay->rc){ ssrc_count++; if (ssrc_count < rtcppkt.overlay->rc){ cur_item = GetNextItem(&cur_item); } printf(" )\n"); } } printf(" )\n"); break; case 203:
printf(" (BYE p=%d count=%d len=%d\n", (int)rtcppkt.overlay->p, (int)rtcppkt.overlay->rc, (int)rtcppkt.overlay->len); for (ssrc_count = 0; ssrc_count < rtcppkt.overlay->rc; ssrc_count++){ byeblock = RTPGetByeBlock(&rtcppkt, ssrc_count); printf("for %x, ", (int)byeblock.ssrccsrc); } printf(" )\n"); } } }
printf("My members:\n");
RTPSessionGetMemberList(my_context, &the_iter);
while (RTPCurrentMember(my_context, &the_iter, &cur_mem) == RTP_OK){
RTPMemberInfoGetSSRC(my_context, cur_mem, &cur_ssrc); printf("%ld : ssrc %x :", cur_mem, (int) cur_ssrc);
if (RTPMemberInfoGetSDES(my_context, cur_mem, RTP_MI_CNAME, payload) != RTP_OK){ sprintf(payload, "NO-INFO"); } else if (payload==NULL){ sprintf(payload, "NULL"); } printf(": CNAME %s\n", payload);
RTPNextMember(my_context, &the_iter, &cur_mem); } } fflush(0);
if (is_sender && (next_RTP_send.tv_sec < curtime.tv_sec || (next_RTP_send.tv_sec == curtime.tv_sec && next_RTP_send.tv_usec <= curtime.tv_usec))){ count++; sprintf(theload, "This is packet %d", count); printf("Sending %s\n", theload); rtptimechange = ((double) (curtime.tv_sec - last_RTP_send.tv_sec) * 1000000 + (double) (curtime.tv_usec - last_RTP_send.tv_usec)) / RTPSTAMPRATE; the_err = RTPSend(my_context, (int32) rtptimechange, 0, 3, theload, strlen(theload)); if (the_err != RTP_OK){ printf("%s\n", RTPStrError()); } next_RTP_send.tv_usec += (long int) (((double)sendrate/10.0 - floor((double)sendrate/10.0)) * 1000000); if (next_RTP_send.tv_usec > 1000000){ next_RTP_send.tv_sec++; next_RTP_send.tv_usec -= 1000000; } next_RTP_send.tv_sec += (long int)floor((double)sendrate / 10.0); last_RTP_send = curtime; } }
the_err = RTPCloseConnection(my_context); if (the_err != RTP_OK){ printf("CloseConn failed: error %d\n", the_err); } the_err = RTPDestroy(my_context); if (the_err != RTP_OK){ printf("Error %d raised\n", the_err); }
#ifdef _RTP_WATCH_ALLOCATION CheckMallocs(); #endif exit(0); }