📄 rtpsend.c
字号:
/** Send RTP packet stream with configurable parameters.* Intended to test RTP features.* Reads RTP/RTCP headers from ASCII file, possibly generated by rtpdump.* Format (in any order, without white space around =, lines continued* with initial white space, comment lines start with #, strings* enclosed in quotation marks):* <time> RTP v=<version> m=<marker> pt=<payload type> ts=<time stamp>* seq=<sequence number> cc=<CSRC count> data=<hex payload>* <time> RTCP (SDES v=<version> * (src=<source> cname="..." name="...")* (src=<source> ...)* )* (RR v=<version>* )*** (c) 1995 Henning Schulzrinne, GMD Fokus*/#include <stdio.h>#include <ctype.h>#include <sys/types.h>#include <sys/time.h> /* gettimeofday() */#include <sys/socket.h> /* struct sockaddr */#include <netinet/in.h>#include <arpa/inet.h> /* inet_ntoa() */#include <time.h>#include <stdio.h> /* stderr, printf() */#include <string.h>#include <stdlib.h> /* perror(), calloc() */#include <unistd.h> /* write() */#include "notify.h" /* notify_start(), ... */#include "rtp.h" /* RTP headers */#include "multimer.h" /* timer_set() */#include "ansi.h"static char rcsid[] = "$Id$";static int verbose = 0;static FILE *in = stdin;static int sock[2]; /* output sockets *//** Node either has a parameter or a non-zero pointer to list.*/typedef struct node { struct node *next, *list; /* parameter in list, level down */ char *type; /* parameter type */ int num; /* numeric value */ char *string; /* string value */} node_t;static void usage(char *argv0){ fprintf(stderr, "Usage: %s [-l] [-f file] destination/port/ttl\n", argv0); exit(1);} /* usage *//** Convert hexadecimal numbers in 'text' to binary in 'buffer'.* Ignore embedded whitespace.* Return length in bytes.*/static int hex(char *text, char *buffer){ char *s; char byte[3]; int nibble = 0; int len = 0; byte[2] = '\0'; for (s = text; *s; s++) { if (isspace(*s)) continue; else { byte[nibble++] = *s; if (nibble == 2) { nibble = 0; buffer[len++] = strtol(byte, (char **)NULL, 16); } } } return len;} /* hex *//** Convert textual description into parse tree.* (SDES (ssrc=<ssrc> cname=<cname> ...) (ssrc=<ssrc> ...))* Return pointer to first node.*/static node_t *parse(char *text){ int string = 0; int level = 0; char tmp[1024]; int c = 0; node_t *n; node_t *first = (node_t *)NULL, *last = (node_t *)NULL; char *s; /* convert to tree */ for (s = text; *s; s++) { if (string) { tmp[c++] = *s; if (*s == '"') string = 0; } else if (*s == '(') { if (level > 0) tmp[c++] = *s; else c = 0; level++; } else if (*s == ')') { level--; if (level == 0) { n = calloc(1, sizeof(node_t)); /* append node to list */ if (!first) first = n; if (last) last->next = n; last = n; tmp[c] = 0; c = 0; /* attach list */ n->list = parse(tmp); } else { tmp[c++] = *s; } } else if (*s == '"') { tmp[c++] = *s; string = 1; } else if (level >= 1) { tmp[c++] = *s; } /* end of parameter/value pair */ else if (isspace(*s)) { char *e; tmp[c] = '\0'; if (c > 0) { c = 0; n = calloc(1, sizeof(node_t)); if (!first) first = n; if (last) last->next = n; last = n; e = strchr(tmp, '='); if (e) { char *v = e+1; *e = '\0'; if (isdigit(*v)) n->num = strtol(v, (char **)NULL, 0); else { n->string = malloc(strlen(v+1) + 1); /* strip quotation marks */ v[strlen(v)-1] = '\0'; strcpy(n->string, v+1); } } n->type = malloc(strlen(tmp) + 1); strcpy(n->type, tmp); } } /* parameters, separated by white space */ else { tmp[c++] = *s; } } return first;} /* parse *//** Parse parameter=value. Return value, word becomes parameter.* Value must be integer.*/static u_int32 parse_int(char *word){ char *s; u_int32 value; if ((s = strchr(word, '='))) { value = strtol(s+1, (char **)NULL, 0); *s = '\0'; } else { *word = '\0'; } return value;} /* parse_int *//** Free nodes recursively.*/static void node_free(node_t *list){ node_t *n, *n_next; for (n = list; n; n = n_next) { if (n->type) { free(n->type); n->type = 0; } if (n->string) { free(n->string); n->string = 0; } n->num = 0; if (n->list) node_free(n->list); n_next = n->next; free(n); }} /* node_free */static int rtcp_sdes_item(char *type, char *string, char *packet){ struct { char *name; rtcp_sdes_type_t type; } map[] = { {"end", RTCP_SDES_END}, {"cname", RTCP_SDES_CNAME}, {"name", RTCP_SDES_NAME}, {"email", RTCP_SDES_EMAIL}, {"phone", RTCP_SDES_PHONE}, {"loc", RTCP_SDES_LOC}, {"tool", RTCP_SDES_TOOL}, {"note", RTCP_SDES_NOTE}, {"priv", RTCP_SDES_PRIV}, {0,0} }; int i; rtcp_sdes_item_t *item = (rtcp_sdes_item_t *)packet; for (i = 0; map[i].name; i++) { if (strcasecmp(type, map[i].name) == 0) break; } item->type = map[i].type; item->length = strlen(string); strcpy(item->data, string); return item->length + 2;} /* rtcp_sdes_item *//** Create SDES entries for single source.* Return length.*/static int rtcp_sdes(node_t *list, char *packet){ node_t *n; int len = 0, total = 4; struct rtcp_sdes *sdes = (struct rtcp_sdes *)packet; packet += 4; /* skip SRC */ for (n = list; n; n = n->next) { if (n->type) { if (strcmp(n->type, "src") == 0) { sdes->src = n->num; } else { len = rtcp_sdes_item(n->type, n->string, packet); packet += len; total += len; } } } /* end marker */ *packet = RTCP_SDES_END; total++; /* pad length to next multiple of 32 bits */ len = (total + 3) & 0xfffc; memset(packet, 0, len - total); return len;} /* rtcp_sdes */static int rtcp_sr(node_t *n, char *packet){ int length = 0; return length;} /* rtcp_sr */static int rtcp_rr(node_t *n, char *packet){ int length = 0; return length;} /* rtcp_rr */static int rtcp_bye(node_t *n, char *packet){ int length = 0; return length;} /* rtcp_bye *//** Based on list of parameters in 'n', assemble RTCP packet.*/static int rtcp_packet(node_t *list, char *packet){ node_t *n; int len = 0, total = 4, count = 0; rtcp_t *r = (rtcp_t *)packet; int (*f)(node_t *list, char *packet); r->common.length = 0; r->common.count = 0; r->common.version = RTP_VERSION; r->common.p = 0; packet += 4; /* skip common header */ for (n = list; n; n = n->next) { if (n->type) { if (strcmp(n->type, "SDES") == 0) { f = rtcp_sdes; r->common.pt = RTCP_SDES; } else if (strcmp(n->type, "RR") == 0) { f = rtcp_rr; r->common.pt = RTCP_RR; } else if (strcmp(n->type, "SR") == 0) { f = rtcp_sr; r->common.pt = RTCP_SR; } else if (strcmp(n->type, "BYE") == 0) { f = rtcp_bye; r->common.pt = RTCP_BYE; } else if (strcmp(n->type, "pt") == 0) { r->common.pt = n->num; } else if (strcmp(n->type, "v") == 0) { r->common.version = n->num; } else if (strcmp(n->type, "p") == 0) { r->common.p = n->num; } else if (strcmp(n->type, "count") == 0) { r->common.count = n->num; } else if (strcmp(n->type, "len") == 0) { r->common.length = n->num; } } /* list: type-specific parts */ else { len = (*f)(n->list, packet); packet += len; total += len; count++; } } /* if no length or count given, fill in */ if (r->common.length == 0) { r->common.length = (total - 4) / 4; } if (r->common.count == 0) r->common.count = count; return total;} /* rtcp_packet *//** Generate RTCP packet based on textual description.*/static int rtcp(char *text, char *packet){ node_t *node_list, *n; int len; int total = 0; node_list = parse(text); for (n = node_list; n; n = n->next) { if (n->list) { len = rtcp_packet(n->list, packet); packet += len; total += len; } } node_free(node_list); return total;} /* rtcp *//** Generate RTP data packet.*/static int rtp(char *text, char *packet){ char *word; int pl = 0; /* payload length */ int wc = 0; int cc = 0; int pad = 0; rtp_hdr_t *h = (rtp_hdr_t *)packet; int length = 0; u_int32 value; /* defaults */ memset(packet, 0, sizeof(rtp_hdr_t)); h->version = RTP_VERSION; while ((word = strtok(wc ? 0 : text, " "))) { value = parse_int(word); if (strcmp(word, "ts") == 0) { h->ts = htonl(value); } else if (strcmp(word, "seq") == 0) { h->seq = htonl(value); } else if (strcmp(word, "pt") == 0) { h->pt = value; } else if (strcmp(word, "ssrc") == 0) { h->ssrc = htonl(value); } else if (strcmp(word, "p") == 0) { h->p = (value != 0); pad = value; } else if (strcmp(word, "m") == 0) { h->m = value; } else if (strcmp(word, "v") == 0) { h->version = value; } else if (strcmp(word, "cc") == 0) { h->cc = value; } else if (strncmp(word, "csrc", 4) == 0) { int k = atoi(&word[5]); h->csrc[k] = value; if (k > cc) cc = k; } /* data is in hex; words may be separated by spaces */ else if (strcmp(word, "data") == 0) { pl = hex(&word[5], packet + 12 + h->cc*4); } else if (strcmp(word, "len") == 0) { length = value; } wc++; } /* fill in default values if not set */ if (h->cc == 0) h->cc = cc; if (length == 0) length = 12 + h->cc * 4 + pl; /* insert padding */ return length;} /* rtp *//** Generate a packet based on description in 'text'. Return length.* Set generation time.*/static int generate(char *text, char *data, double *time, int *type){ int length; char type_name[100]; if (verbose) printf("%s", text); sscanf(text, "%lf %s", time, type_name); if (strcmp(type_name, "RTP") == 0) { length = rtp(strstr(text, "RTP") + 3, data); *type = 0; } else if (strcmp(type_name, "RTCP") == 0) { length = rtcp(strstr(text, "RTCP") + 4, data); *type = 1; } return length;} /* generate *//** Timer handler; sends any pending packets and parses next one.* First packet is played out immediately.*/static Notify_value send_handler(Notify_client client){ static struct { int length; double time; int type; char data[1024]; } packet = { 0, -1, 0}; FILE *in = (FILE *)client; static char line[2048]; /* last line read (may be next packet) */ char text[2048]; double this; /* time this packet is being sent */ struct timeval delta; /* next packet generation time */ char *s; /* send any pending packet */ if (packet.length && write(sock[packet.type], packet.data, packet.length) < 0) { perror("write"); } /* read line; continuation lines start with white space */ if (feof(in)) { notify_stop(); exit(0); return NOTIFY_DONE; } s = text; if (line[0]) { strcpy(text, line); s += strlen(text); } while (fgets(line, sizeof(line), in)) { if (line[0] == '#') continue; else if (s != text && !isspace(line[0])) break; else { strcpy(s, line); s += strlen(line); } } this = packet.time; packet.length = generate(text, packet.data, &packet.time, &packet.type); /* very first packet: send immediately */ if (this < 0.) this = packet.time; else if (this > packet.time) { fprintf(stderr, "Non-monotonic time %f - sent immediately.\n", packet.time); this = packet.time; } /* compute next playout time */ delta.tv_sec = packet.time - this; delta.tv_usec = ((packet.time - this) - delta.tv_sec) * 1e6; timer_set(&delta, send_handler, (Notify_client)in, 1); return NOTIFY_DONE;} /* send_handler */int main(int argc, char *argv[]){ char ttl = 16; static struct sockaddr_in sin; int i; int c; int loop = 0; /* play file indefinitely */ char *filename = 0; extern char *optarg; extern int optind; extern int hpt(char *h, struct sockaddr *sa, unsigned char *ttl); /* parse command line arguments */ while ((c = getopt(argc, argv, "f:lt:v")) != EOF) { switch(c) { case 'f': filename = optarg; break; case 'l': loop = 1; break; case 'v': verbose = 1; break; case '?': case 'h': usage(argv[0]); break; } } if (filename) { in = fopen(filename, "r"); if (!in) { perror(filename); exit(1); } } else { in = stdin; loop = 0; } if (optind < argc) { if (hpt(argv[optind], (struct sockaddr *)&sin, &ttl) < 0) usage(argv[0]); } /* create/connect sockets */ for (i = 0; i < 2; i++) { sock[i] = socket(PF_INET, SOCK_DGRAM, 0); if (sock[i] < 0) { perror("socket"); exit(1); } sin.sin_port = htons(ntohs(sin.sin_port) + i); if (connect(sock[i], (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("connect"); exit(1); } if (IN_CLASSD(sin.sin_addr.s_addr) && (setsockopt(sock[i], IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)) { perror("IP_MULTICAST_TTL"); exit(1); } } send_handler((Notify_client)in); notify_start(); return 0;} /* main */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -