2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /** @file clients/rtpmon.c
21 * This progam monitors the rate at which data arrives by RTP and
22 * constantly display it. It is intended for debugging only.
24 * TODO de-dupe with playrtp.
29 #include <sys/socket.h>
30 #include <sys/types.h>
32 #include <netinet/in.h>
46 #include "configuration.h"
49 /** @brief Record of one packet */
51 /** @brief When packet arrived */
54 /** @brief Serial number of first sample */
58 /** @brief Bytes per frame */
59 static unsigned bpf
= 4;
61 /** @brief Frame serial number */
62 static uint32_t serial
;
64 /** @brief Size of ring buffer */
65 #define RINGSIZE 131072
67 /** @brief Ring buffer */
68 static struct entry ring
[RINGSIZE
];
70 /** @brief Where new packets join the ring */
71 static unsigned ringtail
;
73 static const struct option options
[] = {
74 { "help", no_argument
, 0, 'h' },
75 { "version", no_argument
, 0, 'V' },
76 { "bpf", required_argument
, 0, 'b' },
80 static void help(void) {
82 " rtpmon [OPTIONS] [ADDRESS] PORT\n"
84 " --bpf, -b Bytes/frame (default 4)\n"
85 " --help, -h Display usage message\n"
86 " --version, -V Display version number\n"
92 /** @brief Compute the rate by sampling at two points in the ring buffer */
93 static double rate(unsigned earlier
, unsigned later
) {
94 const uint32_t frames
= ring
[later
].serial
- ring
[earlier
].serial
;
95 const int64_t us
= tvsub_us(ring
[later
].when
, ring
[earlier
].when
);
98 return 1000000.0 * frames
/ us
;
103 /** @brief Called to say we received some bytes
104 * @param when When we received them
105 * @param n How many frames of audio data we received
107 static void frames(const struct timeval
*when
, size_t n
) {
108 const time_t prev
= ring
[(ringtail
- 1) % RINGSIZE
].when
.tv_sec
;
110 ring
[ringtail
].when
= *when
;
111 ring
[ringtail
].serial
= serial
;
113 ringtail
= (ringtail
+ 1) % RINGSIZE
;
114 // Report once a second
115 if(prev
!= when
->tv_sec
) {
116 if(printf("%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n",
117 rate((ringtail
- RINGSIZE
/ 128) % RINGSIZE
,
118 (ringtail
- 1) % RINGSIZE
),
119 rate((ringtail
- RINGSIZE
/ 64) % RINGSIZE
,
120 (ringtail
- 1) % RINGSIZE
),
121 rate((ringtail
- RINGSIZE
/ 32) % RINGSIZE
,
122 (ringtail
- 1) % RINGSIZE
),
123 rate((ringtail
- RINGSIZE
/ 16) % RINGSIZE
,
124 (ringtail
- 1) % RINGSIZE
),
125 rate((ringtail
- RINGSIZE
/ 8) % RINGSIZE
,
126 (ringtail
- 1) % RINGSIZE
),
127 rate((ringtail
- RINGSIZE
/ 4) % RINGSIZE
,
128 (ringtail
- 1) % RINGSIZE
),
129 rate((ringtail
- RINGSIZE
/ 2) % RINGSIZE
,
130 (ringtail
- 1) % RINGSIZE
)) < 0
131 || fflush(stdout
) < 0)
132 disorder_fatal(errno
, "stdout");
136 int main(int argc
, char **argv
) {
138 struct addrinfo
*res
;
139 struct stringlist sl
;
141 struct ipv6_mreq mreq6
;
147 struct sockaddr_in in
;
148 struct sockaddr_in6 in6
;
150 union any_sockaddr mgroup
;
152 static const struct addrinfo prefs
= {
153 .ai_flags
= AI_PASSIVE
,
154 .ai_family
= PF_INET
,
155 .ai_socktype
= SOCK_DGRAM
,
156 .ai_protocol
= IPPROTO_UDP
158 static const int one
= 1;
161 if(!setlocale(LC_CTYPE
, ""))
162 disorder_fatal(errno
, "error calling setlocale");
163 while((n
= getopt_long(argc
, argv
, "hVb:", options
, 0)) >= 0) {
166 case 'V': version("rtpmon");
167 case 'b': bpf
= atoi(optarg
); break;
168 default: disorder_fatal(0, "invalid option");
176 /* Use command-line ADDRESS+PORT or just PORT */
181 disorder_fatal(0, "usage: rtpmon [OPTIONS] [ADDRESS] PORT");
183 if(!(res
= get_address(&sl
, &prefs
, &sockname
)))
185 /* Create the socket */
186 if((rtpfd
= socket(res
->ai_family
,
188 res
->ai_protocol
)) < 0)
189 disorder_fatal(errno
, "error creating socket");
190 /* Allow multiple listeners */
191 xsetsockopt(rtpfd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof one
);
192 is_multicast
= multicast(res
->ai_addr
);
193 /* The multicast and unicast/broadcast cases are different enough that they
194 * are totally split. Trying to find commonality between them causes more
195 * trouble that it's worth. */
197 /* Stash the multicast group address */
198 memcpy(&mgroup
, res
->ai_addr
, res
->ai_addrlen
);
199 switch(res
->ai_addr
->sa_family
) {
201 mgroup
.in
.sin_port
= 0;
204 mgroup
.in6
.sin6_port
= 0;
207 disorder_fatal(0, "unsupported family %d", (int)res
->ai_addr
->sa_family
);
209 /* Bind to to the multicast group address */
210 if(bind(rtpfd
, res
->ai_addr
, res
->ai_addrlen
) < 0)
211 disorder_fatal(errno
, "error binding socket to %s",
212 format_sockaddr(res
->ai_addr
));
213 /* Add multicast group membership */
214 switch(mgroup
.sa
.sa_family
) {
216 mreq
.imr_multiaddr
= mgroup
.in
.sin_addr
;
217 mreq
.imr_interface
.s_addr
= 0; /* use primary interface */
218 if(setsockopt(rtpfd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
219 &mreq
, sizeof mreq
) < 0)
220 disorder_fatal(errno
, "error calling setsockopt IP_ADD_MEMBERSHIP");
223 mreq6
.ipv6mr_multiaddr
= mgroup
.in6
.sin6_addr
;
224 memset(&mreq6
.ipv6mr_interface
, 0, sizeof mreq6
.ipv6mr_interface
);
225 if(setsockopt(rtpfd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
226 &mreq6
, sizeof mreq6
) < 0)
227 disorder_fatal(errno
, "error calling setsockopt IPV6_JOIN_GROUP");
230 disorder_fatal(0, "unsupported address family %d", res
->ai_family
);
232 /* Report what we did */
233 disorder_info("listening on %s multicast group %s",
234 format_sockaddr(res
->ai_addr
), format_sockaddr(&mgroup
.sa
));
237 switch(res
->ai_addr
->sa_family
) {
239 struct sockaddr_in
*in
= (struct sockaddr_in
*)res
->ai_addr
;
241 memset(&in
->sin_addr
, 0, sizeof (struct in_addr
));
242 if(bind(rtpfd
, res
->ai_addr
, res
->ai_addrlen
) < 0)
243 disorder_fatal(errno
, "error binding socket to 0.0.0.0 port %d",
244 ntohs(in
->sin_port
));
248 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)res
->ai_addr
;
250 memset(&in6
->sin6_addr
, 0, sizeof (struct in6_addr
));
254 disorder_fatal(0, "unsupported family %d", (int)res
->ai_addr
->sa_family
);
256 if(bind(rtpfd
, res
->ai_addr
, res
->ai_addrlen
) < 0)
257 disorder_fatal(errno
, "error binding socket to %s",
258 format_sockaddr(res
->ai_addr
));
259 /* Report what we did */
260 disorder_info("listening on %s", format_sockaddr(res
->ai_addr
));
263 struct rtp_header header
;
268 iov
[0].iov_base
= &header
;
269 iov
[0].iov_len
= sizeof header
;
270 iov
[1].iov_base
= buffer
;
271 iov
[1].iov_len
= sizeof buffer
;
272 n
= readv(rtpfd
, iov
, 2);
273 gettimeofday(&when
, 0);
279 disorder_fatal(errno
, "error reading from socket");
282 if((size_t)n
<= sizeof (struct rtp_header
)) {
283 disorder_info("ignored a short packet");
286 frames(&when
, (n
- sizeof header
) / bpf
);