blob: 83a7ead3e93687f47c5cc2f3a4a4147588d71750 [file] [log] [blame]
Benny Prijono0f856722008-02-01 14:59:19 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
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 2 of the License, or
8 * (at your option) any later version.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjlib.h>
20#include <pjlib-util.h>
21#include <pjmedia.h>
22#include <pjmedia-codec.h>
23
24static const char *USAGE =
25"pcaputil [options] INPUT OUTPUT\n"
26"\n"
27" Convert captured RTP packets in PCAP file to WAV or stream it\n"
28" to remote destination.\n"
29"\n"
30"INPUT is the PCAP file name/path\n"
31"\n"
32"Options to filter packets from PCAP file:\n"
33" --src-ip=IP Only include packets from this source address\n"
34" --dst-ip=IP Only include packets destined to this address\n"
35" --src-port=port Only include packets from this source port number\n"
36" --dst-port=port Only include packets destined to this port number\n"
37"\n"
38"Options for saving to WAV file:\n"
39""
40" OUTPUT is WAV file: Set output to WAV file. The program will decode the\n"
41" RTP contents to the specified WAV file using codec\n"
42" that is available in PJMEDIA, and optionally decrypt\n"
43" the content using the SRTP crypto and keys below.\n"
44" --srtp-crypto=TAG, -c Set crypto to be used to decrypt SRTP packets. Valid\n"
45" tags are: \n"
46" AES_CM_128_HMAC_SHA1_80 \n"
47" AES_CM_128_HMAC_SHA1_32\n"
48" --srtp-key=KEY, -k Set the base64 key to decrypt SRTP packets.\n"
49"\n"
50" Example:\n"
51" pcaputil file.pcap output.wav\n"
52" pcaputil -c AES_CM_128_HMAC_SHA1_80 \\\n"
53" -k VLDONbsbGl2Puqy+0PV7w/uGfpSPKFevDpxGsxN3 \\\n"
54" file.pcap output.wav\n"
55;
56
57static pj_caching_pool cp;
58static pj_pool_t *pool;
59static pjmedia_endpt *mept;
60static pj_pcap_file *pcap;
61static pjmedia_port *wav;
62static pjmedia_transport *srtp;
63
64static void err_exit(const char *title, pj_status_t status)
65{
66 if (status != PJ_SUCCESS) {
67 char errmsg[PJ_ERR_MSG_SIZE];
68 pj_strerror(status, errmsg, sizeof(errmsg));
69 printf("Error: %s: %s\n", title, errmsg);
70 } else {
71 printf("Error: %s\n", title);
72 }
73
74 if (srtp) pjmedia_transport_close(srtp);
75 if (wav) pjmedia_port_destroy(wav);
76 if (pcap) pj_pcap_close(pcap);
77 if (mept) pjmedia_endpt_destroy(mept);
78 if (pool) pj_pool_release(pool);
79 pj_caching_pool_destroy(&cp);
80 pj_shutdown();
81
82 exit(1);
83}
84
85#define T(op) do { \
86 status = op; \
87 if (status != PJ_SUCCESS) \
88 err_exit(#op, status); \
89 } while (0)
90
91
92static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize,
93 pjmedia_rtp_hdr **rtp,
94 pj_uint8_t **payload,
95 unsigned *payload_size)
96{
97 pj_size_t sz = bufsize;
98 pj_status_t status;
99
100 status = pj_pcap_read_udp(pcap, buf, &sz);
101 if (status != PJ_SUCCESS)
102 err_exit("Error reading PCAP file", status);
103
104 if (sz < sizeof(pjmedia_rtp_hdr) + 10) {
105 err_exit("Invalid RTP packet", PJ_SUCCESS);
106 }
107
108 /* Decrypt SRTP */
109 if (srtp) {
110 int len = sz;
111 T(pjmedia_transport_srtp_decrypt_pkt(srtp, PJ_TRUE, buf, &len));
112 sz = len;
113 }
114
115 *rtp = (pjmedia_rtp_hdr*)buf;
116 *payload = (pj_uint8_t*) (buf + sizeof(pjmedia_rtp_hdr));
117 *payload_size = sz - sizeof(pjmedia_rtp_hdr);
118}
119
120static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
121 const pj_str_t *srtp_key)
122{
123 struct pkt
124 {
125 pj_uint8_t buffer[320];
126 pjmedia_rtp_hdr *rtp;
127 pj_uint8_t *payload;
128 unsigned payload_len;
129 } pkt0;
130 pjmedia_codec_mgr *cmgr;
131 pjmedia_codec_info *ci;
132 pjmedia_codec_param param;
133 pjmedia_codec *codec;
134 unsigned samples_per_frame;
135 pj_status_t status;
136
137 /* Initialize all codecs */
138#if PJMEDIA_HAS_SPEEX_CODEC
139 T( pjmedia_codec_speex_init(mept, 0, 10, 10) );
140#endif /* PJMEDIA_HAS_SPEEX_CODEC */
141
142#if PJMEDIA_HAS_ILBC_CODEC
143 T( pjmedia_codec_ilbc_init(mept, 30) );
144#endif /* PJMEDIA_HAS_ILBC_CODEC */
145
146#if PJMEDIA_HAS_GSM_CODEC
147 T( pjmedia_codec_gsm_init(mept) );
148#endif /* PJMEDIA_HAS_GSM_CODEC */
149
150#if PJMEDIA_HAS_G711_CODEC
151 T( pjmedia_codec_g711_init(mept) );
152#endif /* PJMEDIA_HAS_G711_CODEC */
153
154#if PJMEDIA_HAS_L16_CODEC
155 T( pjmedia_codec_l16_init(mept, 0) );
156#endif /* PJMEDIA_HAS_L16_CODEC */
157
158 /* Create SRTP transport is needed */
159 if (srtp_crypto->slen) {
160 pjmedia_srtp_crypto crypto;
161
162 pj_bzero(&crypto, sizeof(crypto));
163 crypto.key = *srtp_key;
164 crypto.name = *srtp_crypto;
165 T( pjmedia_transport_srtp_create(mept, NULL, NULL, &srtp) );
166 T( pjmedia_transport_srtp_start(srtp, &crypto, &crypto) );
167 }
168
169 /* Read first packet */
170 read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp,
171 &pkt0.payload, &pkt0.payload_len);
172
173 cmgr = pjmedia_endpt_get_codec_mgr(mept);
174
175 /* Get codec info and param for the specified payload type */
176 T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) );
177 T( pjmedia_codec_mgr_get_default_param(cmgr, ci, &param) );
178
179 /* Alloc and init codec */
180 T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &codec) );
181 T( codec->op->init(codec, pool) );
182 T( codec->op->open(codec, &param) );
183
184 /* Open WAV file */
185 samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000;
186 T( pjmedia_wav_writer_port_create(pool, wav_filename,
187 ci->clock_rate, ci->channel_cnt,
188 samples_per_frame,
189 param.info.pcm_bits_per_sample, 0, 0,
190 &wav) );
191
192 /* Loop reading PCAP and writing WAV file */
193 for (;;) {
194 struct pkt pkt1;
195 pj_timestamp ts;
196 pjmedia_frame frames[16], pcm_frame;
197 short pcm[320];
198 unsigned i, frame_cnt;
199 long samples_cnt, ts_gap;
200
201 pj_assert(sizeof(pcm) >= samples_per_frame);
202
203 /* Parse first packet */
204 ts.u64 = 0;
205 frame_cnt = PJ_ARRAY_SIZE(frames);
206 T( codec->op->parse(codec, pkt0.payload, pkt0.payload_len, &ts,
207 &frame_cnt, frames) );
208
209 /* Decode and write to WAV file */
210 samples_cnt = 0;
211 for (i=0; i<frame_cnt; ++i) {
212 pjmedia_frame pcm_frame;
213
214 pcm_frame.buf = pcm;
215 pcm_frame.size = samples_per_frame * 2;
216
217 T( codec->op->decode(codec, &frames[i], pcm_frame.size, &pcm_frame) );
218 T( pjmedia_port_put_frame(wav, &pcm_frame) );
219 samples_cnt += samples_per_frame;
220 }
221
222 /* Read next packet */
223 read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp,
224 &pkt1.payload, &pkt1.payload_len);
225
226 /* Fill in the gap (if any) between pkt0 and pkt1 */
227 ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) -
228 samples_cnt;
229 while (ts_gap >= (long)samples_per_frame) {
230
231 pcm_frame.buf = pcm;
232 pcm_frame.size = samples_per_frame * 2;
233
234 if (codec->op->recover) {
235 T( codec->op->recover(codec, pcm_frame.size, &pcm_frame) );
236 } else {
237 pj_bzero(pcm_frame.buf, pcm_frame.size);
238 }
239
240 T( pjmedia_port_put_frame(wav, &pcm_frame) );
241 ts_gap -= samples_per_frame;
242 }
243
244 /* Next */
245 pkt0 = pkt1;
246 pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer;
247 pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer);
248 }
249}
250
251
252int main(int argc, char *argv[])
253{
254 pj_str_t input, output, wav, srtp_crypto, srtp_key;
255 pj_pcap_filter filter;
256 pj_status_t status;
257
258 enum { OPT_SRC_IP = 1, OPT_DST_IP, OPT_SRC_PORT, OPT_DST_PORT };
259 struct pj_getopt_option long_options[] = {
260 { "srtp-crypto", 1, 0, 'c' },
261 { "srtp-key", 1, 0, 'k' },
262 { "src-ip", 1, 0, OPT_SRC_IP },
263 { "dst-ip", 1, 0, OPT_DST_IP },
264 { "src-port", 1, 0, OPT_SRC_PORT },
265 { "dst-port", 1, 0, OPT_DST_PORT },
266 { NULL, 0, 0, 0}
267 };
268 int c;
269 int option_index;
270 char key_bin[32];
271
272 srtp_crypto.slen = srtp_key.slen = 0;
273
274 pj_pcap_filter_default(&filter);
275 filter.link = PJ_PCAP_LINK_TYPE_ETH;
276 filter.proto = PJ_PCAP_PROTO_TYPE_UDP;
277
278 /* Parse arguments */
279 pj_optind = 0;
280 while((c=pj_getopt_long(argc,argv, "c:k:", long_options, &option_index))!=-1) {
281 switch (c) {
282 case 'c':
283 srtp_crypto = pj_str(pj_optarg);
284 break;
285 case 'k':
286 {
287 int key_len = sizeof(key_bin);
288 srtp_key = pj_str(pj_optarg);
289 if (pj_base64_decode(&srtp_key, (pj_uint8_t*)key_bin, &key_len)) {
290 puts("Error: invalid key");
291 return 1;
292 }
293 srtp_key.ptr = key_bin;
294 srtp_key.slen = key_len;
295 }
296 break;
297 case OPT_SRC_IP:
298 {
299 pj_str_t t = pj_str(pj_optarg);
300 pj_in_addr a = pj_inet_addr(&t);
301 filter.ip_src = a.s_addr;
302 }
303 break;
304 case OPT_DST_IP:
305 {
306 pj_str_t t = pj_str(pj_optarg);
307 pj_in_addr a = pj_inet_addr(&t);
308 filter.ip_dst = a.s_addr;
309 }
310 break;
311 case OPT_SRC_PORT:
312 filter.src_port = pj_htons((pj_uint16_t)atoi(pj_optarg));
313 break;
314 case OPT_DST_PORT:
315 filter.dst_port = pj_htons((pj_uint16_t)atoi(pj_optarg));
316 break;
317 default:
318 puts("Error: invalid option");
319 return 1;
320 }
321 }
322
323 if (pj_optind != argc - 2) {
324 puts(USAGE);
325 return 1;
326 }
327
328 if (!(srtp_crypto.slen) != !(srtp_key.slen)) {
329 puts("Error: both SRTP crypto and key must be specified");
330 puts(USAGE);
331 return 1;
332 }
333
334 input = pj_str(argv[pj_optind]);
335 output = pj_str(argv[pj_optind+1]);
336 wav = pj_str(".wav");
337
338 T( pj_init() );
339
340 pj_caching_pool_init(&cp, NULL, 0);
341 pool = pj_pool_create(&cp.factory, "pcaputil", 1000, 1000, NULL);
342
343 T( pjlib_util_init() );
344 T( pjmedia_endpt_create(&cp.factory, NULL, 0, &mept) );
345
346 T( pj_pcap_open(pool, input.ptr, &pcap) );
347 T( pj_pcap_set_filter(pcap, &filter) );
348
349 if (pj_stristr(&output, &wav)) {
350 pcap2wav(output.ptr, &srtp_crypto, &srtp_key);
351 } else {
352 err_exit("invalid output file", PJ_EINVAL);
353 }
354
355 pjmedia_endpt_destroy(mept);
356 pj_pool_release(pool);
357 pj_caching_pool_destroy(&cp);
358 pj_shutdown();
359 return 0;
360}
361