📄 rtpdump.c
字号:
/** RTP/RTCP packet dumper (RFC 1889)* Usage:* rtpdump [address/port] > dump file** Listens on port pair specified by argument for RTP and RTCP* packets, or reads from dump file.** -F format (names can be abbreviated):* dump dump in binary format (suitable for rtpplay)* header like 'dump', but don't save audio/video payload* ascii parsed packets (default)* hex like 'ascii', but with hex dump* rtcp like 'ascii', RTCP packets only* short RTP or vat data: [-]time ts [seq]* payload only audio/video payload* * -t d: recording duration in minutes* -x n: number of payload bytes to dump per packet (hex, dump)* -f name: name of input file, using "dump" format** To record in chunks, simply build a shell script that loops,* with suitably chosen file names. Each such file will be* individually playable. (Gaps?)** Copyright (c) 1995-1997 by H. Schulzrinne (GMD Fokus, Columbia U.)* $Id$*/#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h> /* struct sockaddr_in */#include <arpa/inet.h> /* inet_ntoa() */#include <sys/time.h> /* gettimeofday() */#include <string.h>#include <stdio.h>#include <signal.h>#include <errno.h>#if !defined(nextstep)#include <values.h>#endif#include <unistd.h> /* select(), perror() */#include <stdlib.h> /* getopt(), atoi() */#include <math.h> /* fmod() */#include "rtp.h"#include "vat.h"#include "rtpdump.h"#include "ansi.h"#define VERSION "1.0"typedef u_int32 member_t;static int verbose = 0; /* decode */static char rcsid[] = "$Id: rtpdump.c,v 1.6 1995/08/30 11:19:01 hgs Exp $";typedef enum {F_invalid, F_dump, F_header, F_hex, F_rtcp, F_short, F_payload, F_ascii} t_format;/** Payload type map.*/static struct { char *enc; /* encoding name */ int rate; /* sampling rate for audio; clock rate for video */ int ch; /* audio channels; 0 for video */} pt_map[256];static void usage(char *argv0){ fprintf(stderr, "Usage: %s [-F [hex|ascii|rtcp|short|payload|dump|header] [-t minutes] [-b bytes] [multicast]/port > file\n", argv0);}static void done(int sig){ exit(0);}static double tdbl(struct timeval *a){ return a->tv_sec + a->tv_usec/1e6;}static void hex(char *buf, int len){ int i; for (i = 0; i < len; i++) { printf("%02x ", (unsigned char)buf[i]); } printf("\n");} /* hex *//** Open network sockets.*/static int open_network(char *host, int data, int sock[], struct sockaddr_in *sin){ struct ip_mreq mreq; /* multicast group */ int i; int nfds = 0; extern int hpt(char *h, struct sockaddr *sa, unsigned char *ttl); if (hpt(host, (struct sockaddr *)sin, 0) < 0) { usage(""); } /* multicast */ if (host) { mreq.imr_multiaddr = sin->sin_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); sin->sin_addr = mreq.imr_multiaddr; } /* unicast */ else { mreq.imr_multiaddr.s_addr = INADDR_ANY; sin->sin_addr.s_addr = INADDR_ANY; } /* create/bind sockets */ for (i = 0; i < 2; i++) { int one = 1; /* open data socket only if necessary */ sock[i] = -1; if (!data && i == 0) continue; sock[i] = socket(PF_INET, SOCK_DGRAM, 0); if (sock[i] < 0) { perror("socket"); exit(1); } if (sock[i] > nfds) nfds = sock[i]; if (setsockopt(sock[i], SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) perror("setsockopt: reuseaddr");#ifdef SO_REUSEPORT /* BSD 4.4 */ if (setsockopt(sock[i], SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one)) == -1) perror("setsockopt: reuseport");#endif sin->sin_port = htons(ntohs(sin->sin_port) + i); if (bind(sock[i], (struct sockaddr *)sin, sizeof(*sin)) < 0) { if (errno == EADDRNOTAVAIL) { sin->sin_addr.s_addr = INADDR_ANY; if (bind(sock[i], (struct sockaddr *)sin, sizeof(*sin)) < 0) { perror("bind"); exit(1); } } else { perror("bind"); exit(1); } } if (IN_CLASSD(ntohl(mreq.imr_multiaddr.s_addr))) { if (setsockopt(sock[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { perror("IP_ADD_MEMBERSHIP"); exit(1); } } } return nfds;} /* open_network *//** Write a header to the current output file.* The header consists of an identifying string, followed* by a binary structure.*/static void rtpdump_header(FILE *out, struct sockaddr_in *sin, struct timeval *start){ RD_hdr_t hdr; fprintf(out, "#!rtpplay%s %s/%d\n", VERSION, inet_ntoa(sin->sin_addr), sin->sin_port); hdr.start.tv_sec = htonl(start->tv_sec); hdr.start.tv_usec = htonl(start->tv_usec); hdr.source = sin->sin_addr.s_addr; hdr.port = sin->sin_port; if (fwrite((char *)&hdr, sizeof(hdr), 1, out) < 1) { perror("fwrite"); exit(1); } } /* rtpdump_header *//** Return type of packet.*/static char *parse_type(int type, char *buf){ if (type == 0) { rtp_hdr_t *r = (rtp_hdr_t *)buf; return r->version == RTP_VERSION ? "RTP" : "VATD"; } else { rtcp_t *r = (rtcp_t *)buf; return r->common.version == RTP_VERSION ? "RTCP" : "VATC"; }} /* parse_type *//** Return header length.*/static int parse_header(char *buf){ rtp_hdr_t *r = (rtp_hdr_t *)buf; int hlen = 0; if (r->version == 0) { vat_hdr_t *v = (vat_hdr_t *)buf; hlen = 8 + v->nsid * 4; } else if (r->version == RTP_VERSION) { hlen = 12 + r->cc * 4; } return hlen;} /* parse_header *//** Return header length.*/static int parse_data(char *buf, int len){ rtp_hdr_t *r = (rtp_hdr_t *)buf; int i; int hlen = 0; /* Show vat format packets. */ if (r->version == 0) { vat_hdr_t *v = (vat_hdr_t *)buf; printf("nsid=%d flags=0x%x confid=%u ts=%u\n", v->nsid, v->flags, v->confid, v->ts); hlen = 8 + v->nsid * 4; } else if (r->version == RTP_VERSION) { hlen = 12 + r->cc * 4; if (len < hlen) { printf("RTP header too short (%d bytes for %d CSRCs).\n", len, r->cc); return hlen; } printf( "v=%d p=%d x=%d cc=%d m=%d pt=%d (%s,%d,%d) seq=%u ts=%lu ssrc=0x%lx\n", r->version, r->p, r->x, r->cc, r->m, r->pt, pt_map[r->pt].enc, pt_map[r->pt].ch, pt_map[r->pt].rate, ntohs(r->seq), ntohl(r->ts), ntohl(r->ssrc)); for (i = 0; i < r->cc; i++) { printf("csrc[%d] = %0x ", i, r->csrc[i]); } } else { printf("RTP version wrong (%d).\n", r->version); } return hlen;} /* parse_data *//** Print minimal per-packet information: time, timestamp, sequence number.*/static void parse_short(struct timeval now, char *buf, int len){ rtp_hdr_t *r = (rtp_hdr_t *)buf; if (r->version == 0) { vat_hdr_t *v = (vat_hdr_t *)buf; printf("%ld.%06ld %lu\n", (v->flags ? -now.tv_sec : now.tv_sec), now.tv_usec, ntohl(v->ts)); } else if (r->version == 2) { printf("%ld.%06ld %lu %u\n", (r->m ? -now.tv_sec : now.tv_sec), now.tv_usec, ntohl(r->ts), ntohs(r->seq)); } else { printf("RTP version wrong (%d).\n", r->version); }} /* parse_short */void member_sdes(member_t m, rtcp_sdes_type_t t, char *b, int len){ static struct { rtcp_sdes_type_t t; char *name; } map[] = { {RTCP_SDES_END, "end"}, {RTCP_SDES_CNAME, "CNAME"}, {RTCP_SDES_NAME, "NAME"}, {RTCP_SDES_EMAIL, "EMAIL"}, {RTCP_SDES_PHONE, "PHONE"}, {RTCP_SDES_LOC, "LOC"}, {RTCP_SDES_TOOL, "TOOL"}, {RTCP_SDES_NOTE, "NOTE"}, {RTCP_SDES_PRIV, "PRIV"}, {11, "SOURCE"}, {0,0} }; int i; char num[10]; sprintf(num, "%d", t); for (i = 0; map[i].name; i++) { if (map[i].t == t) break; } printf("%s=\"%*.*s\" ", map[i].name ? map[i].name : num, len, len, b);} /* member_sdes *//** Parse one SDES chunk (one SRC description). Total length is 'len'.* Return new buffer position or zero if error.*/static char *rtp_read_sdes(char *b, int len){ rtcp_sdes_item_t *rsp; u_int32 src = *(u_int32 *)b; int total_len = 0; len -= 4; /* subtract SSRC from available bytes */ if (len <= 0) { return 0; } rsp = (rtcp_sdes_item_t *)(b + 4); for (; rsp->type; rsp = (rtcp_sdes_item_t *)((char *)rsp + rsp->length + 2)) { member_sdes(src, rsp->type, rsp->data, rsp->length); total_len += rsp->length + 2; } if (total_len >= len) { fprintf(stderr, "Remaining length of %d bytes for SSRC item too short (has %u bytes)\n", len, total_len); return 0; } b = (char *)rsp + 1; /* skip padding */ return b + ((4 - ((int)b & 0x3)) & 0x3);} /* rtp_read_sdes *//** Return length parsed, -1 on error.*/static int parse_control(char *buf, int len){ rtcp_t *r; /* RTCP header */ int i; r = (rtcp_t *)buf; /* Backwards compatibility: VAT header. */ if (r->common.version == 0) { struct CtrlMsgHdr *v = (struct CtrlMsgHdr *)buf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -