blob: 3eb16034577126fc92e3b8a3939a2e64188353b0 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id: pcaputil.c 4537 2013-06-19 06:47:43Z riza $ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjlib.h>
21#include <pjlib-util.h>
22#include <pjmedia.h>
23#include <pjmedia-codec.h>
24
25static const char *USAGE =
26"pcaputil [options] INPUT OUTPUT\n"
27"\n"
28" Convert captured RTP packets in PCAP file to WAV file or play it\n"
29" to audio device.\n"
30"\n"
31" INPUT is the PCAP file name/path.\n"
32" OUTPUT is the WAV file name/path to store the output, or set to \"-\",\n"
33" to play the output to audio device. The program will decode\n"
34" the RTP contents using codec that is available in PJMEDIA,\n"
35" and optionally decrypt the content using the SRTP crypto and\n"
36" keys below.\n"
37"\n"
38"Options to filter packets from PCAP file:\n"
39"(you can always select the relevant packets from Wireshark of course!)\n"
40" --src-ip=IP Only include packets from this source address\n"
41" --dst-ip=IP Only include packets destined to this address\n"
42" --src-port=port Only include packets from this source port number\n"
43" --dst-port=port Only include packets destined to this port number\n"
44"\n"
45"Options for RTP packet processing:\n"
46""
47" --codec=codec_id The codec ID formatted \"name/clock-rate/channel-count\"\n"
48" must be specified for codec with dynamic PT,\n"
49" e.g: \"Speex/8000\"\n"
50" --srtp-crypto=TAG, -c Set crypto to be used to decrypt SRTP packets. Valid\n"
51" tags are: \n"
52" AES_CM_128_HMAC_SHA1_80 \n"
53" AES_CM_128_HMAC_SHA1_32\n"
54" --srtp-key=KEY, -k Set the base64 key to decrypt SRTP packets.\n"
55"\n"
56"Options for playing to audio device:\n"
57""
58" --play-dev-id=dev_id Audio device ID for playback.\n"
59"\n"
60" Example:\n"
61" pcaputil file.pcap output.wav\n"
62" pcaputil -c AES_CM_128_HMAC_SHA1_80 \\\n"
63" -k VLDONbsbGl2Puqy+0PV7w/uGfpSPKFevDpxGsxN3 \\\n"
64" file.pcap output.wav\n"
65"\n"
66;
67
68static struct app
69{
70 pj_caching_pool cp;
71 pj_pool_t *pool;
72 pjmedia_endpt *mept;
73 pj_pcap_file *pcap;
74 pjmedia_port *wav;
75 pjmedia_codec *codec;
76 pjmedia_aud_stream *aud_strm;
77 unsigned pt;
78 pjmedia_transport *srtp;
79 pjmedia_rtp_session rtp_sess;
80 pj_bool_t rtp_sess_init;
81} app;
82
83
84static void cleanup()
85{
86 if (app.srtp) pjmedia_transport_close(app.srtp);
87 if (app.wav) {
88 pj_ssize_t pos = pjmedia_wav_writer_port_get_pos(app.wav);
89 if (pos >= 0) {
90 unsigned msec;
91 msec = (unsigned)pos / 2 * 1000 / PJMEDIA_PIA_SRATE(&app.wav->info);
92 printf("Written: %dm:%02ds.%03d\n",
93 msec / 1000 / 60,
94 (msec / 1000) % 60,
95 msec % 1000);
96 }
97 pjmedia_port_destroy(app.wav);
98 }
99 if (app.pcap) pj_pcap_close(app.pcap);
100 if (app.codec) {
101 pjmedia_codec_mgr *cmgr;
102 pjmedia_codec_close(app.codec);
103 cmgr = pjmedia_endpt_get_codec_mgr(app.mept);
104 pjmedia_codec_mgr_dealloc_codec(cmgr, app.codec);
105 }
106 if (app.aud_strm) {
107 pjmedia_aud_stream_stop(app.aud_strm);
108 pjmedia_aud_stream_destroy(app.aud_strm);
109 }
110 if (app.mept) pjmedia_endpt_destroy(app.mept);
111 if (app.pool) pj_pool_release(app.pool);
112 pj_caching_pool_destroy(&app.cp);
113 pj_shutdown();
114}
115
116static void err_exit(const char *title, pj_status_t status)
117{
118 if (status != PJ_SUCCESS) {
119 char errmsg[PJ_ERR_MSG_SIZE];
120 pj_strerror(status, errmsg, sizeof(errmsg));
121 printf("Error: %s: %s\n", title, errmsg);
122 } else {
123 printf("Error: %s\n", title);
124 }
125 cleanup();
126 exit(1);
127}
128
129#define T(op) do { \
130 status = op; \
131 if (status != PJ_SUCCESS) \
132 err_exit(#op, status); \
133 } while (0)
134
135
136static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize,
137 pjmedia_rtp_hdr **rtp,
138 pj_uint8_t **payload,
139 unsigned *payload_size,
140 pj_bool_t check_pt)
141{
142 pj_status_t status;
143
144 /* Init RTP session */
145 if (!app.rtp_sess_init) {
146 T(pjmedia_rtp_session_init(&app.rtp_sess, 0, 0));
147 app.rtp_sess_init = PJ_TRUE;
148 }
149
150 /* Loop reading until we have a good RTP packet */
151 for (;;) {
152 pj_size_t sz = bufsize;
153 const pjmedia_rtp_hdr *r;
154 const void *p;
155 pjmedia_rtp_status seq_st;
156
157 status = pj_pcap_read_udp(app.pcap, NULL, buf, &sz);
158 if (status != PJ_SUCCESS)
159 err_exit("Error reading PCAP file", status);
160
161 /* Decode RTP packet to make sure that this is an RTP packet.
162 * We will decode it again to get the payload after we do
163 * SRTP decoding
164 */
165 status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r,
166 &p, payload_size);
167 if (status != PJ_SUCCESS) {
168 char errmsg[PJ_ERR_MSG_SIZE];
169 pj_strerror(status, errmsg, sizeof(errmsg));
170 printf("Not RTP packet, skipping packet: %s\n", errmsg);
171 continue;
172 }
173
174 /* Decrypt SRTP */
175#if PJMEDIA_HAS_SRTP
176 if (app.srtp) {
177 int len = (int)sz;
178 status = pjmedia_transport_srtp_decrypt_pkt(app.srtp, PJ_TRUE,
179 buf, &len);
180 if (status != PJ_SUCCESS) {
181 char errmsg[PJ_ERR_MSG_SIZE];
182 pj_strerror(status, errmsg, sizeof(errmsg));
183 printf("SRTP packet decryption failed, skipping packet: %s\n",
184 errmsg);
185 continue;
186 }
187 sz = len;
188
189 /* Decode RTP packet again */
190 status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, (int)sz, &r,
191 &p, payload_size);
192 if (status != PJ_SUCCESS) {
193 char errmsg[PJ_ERR_MSG_SIZE];
194 pj_strerror(status, errmsg, sizeof(errmsg));
195 printf("Not RTP packet, skipping packet: %s\n", errmsg);
196 continue;
197 }
198 }
199#endif
200
201 /* Update RTP session */
202 pjmedia_rtp_session_update2(&app.rtp_sess, r, &seq_st, PJ_FALSE);
203
204 /* Skip out-of-order packet */
205 if (seq_st.diff == 0) {
206 printf("Skipping out of order packet\n");
207 continue;
208 }
209
210 /* Skip if payload type is different */
211 if (check_pt && r->pt != app.pt) {
212 printf("Skipping RTP packet with bad payload type\n");
213 continue;
214 }
215
216 /* Skip bad packet */
217 if (seq_st.status.flag.bad) {
218 printf("Skipping bad RTP\n");
219 continue;
220 }
221
222
223 *rtp = (pjmedia_rtp_hdr*)r;
224 *payload = (pj_uint8_t*)p;
225
226 /* We have good packet */
227 break;
228 }
229}
230
231pjmedia_frame play_frm;
232static pj_bool_t play_frm_copied, play_frm_ready;
233
234static pj_status_t wait_play(pjmedia_frame *f)
235{
236 play_frm_copied = PJ_FALSE;
237 play_frm = *f;
238 play_frm_ready = PJ_TRUE;
239 while (!play_frm_copied) {
240 pj_thread_sleep(1);
241 }
242 play_frm_ready = PJ_FALSE;
243
244 return PJ_SUCCESS;
245}
246
247static pj_status_t play_cb(void *user_data, pjmedia_frame *f)
248{
249 PJ_UNUSED_ARG(user_data);
250
251 if (!play_frm_ready) {
252 PJ_LOG(3, ("play_cb()", "Warning! Play frame not ready"));
253 return PJ_SUCCESS;
254 }
255
256 pj_memcpy(f->buf, play_frm.buf, play_frm.size);
257 f->size = play_frm.size;
258
259 play_frm_copied = PJ_TRUE;
260 return PJ_SUCCESS;
261}
262
263static void pcap2wav(const pj_str_t *codec,
264 const pj_str_t *wav_filename,
265 pjmedia_aud_dev_index dev_id,
266 const pj_str_t *srtp_crypto,
267 const pj_str_t *srtp_key)
268{
269 const pj_str_t WAV = {".wav", 4};
270 struct pkt
271 {
272 pj_uint8_t buffer[320];
273 pjmedia_rtp_hdr *rtp;
274 pj_uint8_t *payload;
275 unsigned payload_len;
276 } pkt0;
277 pjmedia_codec_mgr *cmgr;
278 const pjmedia_codec_info *ci;
279 pjmedia_codec_param param;
280 unsigned samples_per_frame;
281 pj_status_t status;
282
283 /* Initialize all codecs */
284 T( pjmedia_codec_register_audio_codecs(app.mept, NULL) );
285
286 /* Create SRTP transport is needed */
287#if PJMEDIA_HAS_SRTP
288 if (srtp_crypto->slen) {
289 pjmedia_srtp_crypto crypto;
290
291 pj_bzero(&crypto, sizeof(crypto));
292 crypto.key = *srtp_key;
293 crypto.name = *srtp_crypto;
294 T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) );
295 T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) );
296 }
297#else
298 PJ_UNUSED_ARG(srtp_crypto);
299 PJ_UNUSED_ARG(srtp_key);
300#endif
301
302 /* Read first packet */
303 read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp,
304 &pkt0.payload, &pkt0.payload_len, PJ_FALSE);
305
306 cmgr = pjmedia_endpt_get_codec_mgr(app.mept);
307
308 /* Get codec info and param for the specified payload type */
309 app.pt = pkt0.rtp->pt;
310 if (app.pt >=0 && app.pt < 96) {
311 T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) );
312 } else {
313 unsigned cnt = 2;
314 const pjmedia_codec_info *info[2];
315 T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt,
316 info, NULL) );
317 if (cnt != 1)
318 err_exit("Codec ID must be specified and unique!", 0);
319
320 ci = info[0];
321 }
322 T( pjmedia_codec_mgr_get_default_param(cmgr, ci, &param) );
323
324 /* Alloc and init codec */
325 T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) );
326 T( pjmedia_codec_init(app.codec, app.pool) );
327 T( pjmedia_codec_open(app.codec, &param) );
328
329 /* Init audio device or WAV file */
330 samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000;
331 if (pj_strcmp2(wav_filename, "-") == 0) {
332 pjmedia_aud_param aud_param;
333
334 /* Open audio device */
335 T( pjmedia_aud_dev_default_param(dev_id, &aud_param) );
336 aud_param.dir = PJMEDIA_DIR_PLAYBACK;
337 aud_param.channel_count = ci->channel_cnt;
338 aud_param.clock_rate = ci->clock_rate;
339 aud_param.samples_per_frame = samples_per_frame;
340 T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb,
341 NULL, &app.aud_strm) );
342 T( pjmedia_aud_stream_start(app.aud_strm) );
343 } else if (pj_stristr(wav_filename, &WAV)) {
344 /* Open WAV file */
345 T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr,
346 ci->clock_rate, ci->channel_cnt,
347 samples_per_frame,
348 param.info.pcm_bits_per_sample, 0, 0,
349 &app.wav) );
350 } else {
351 err_exit("invalid output file", PJ_EINVAL);
352 }
353
354 /* Loop reading PCAP and writing WAV file */
355 for (;;) {
356 struct pkt pkt1;
357 pj_timestamp ts;
358 pjmedia_frame frames[16], pcm_frame;
359 short pcm[320];
360 unsigned i, frame_cnt;
361 long samples_cnt, ts_gap;
362
363 pj_assert(sizeof(pcm) >= samples_per_frame);
364
365 /* Parse first packet */
366 ts.u64 = 0;
367 frame_cnt = PJ_ARRAY_SIZE(frames);
368 T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len,
369 &ts, &frame_cnt, frames) );
370
371 /* Decode and write to WAV file */
372 samples_cnt = 0;
373 for (i=0; i<frame_cnt; ++i) {
374 pjmedia_frame pcm_frame;
375
376 pcm_frame.buf = pcm;
377 pcm_frame.size = samples_per_frame * 2;
378
379 T( pjmedia_codec_decode(app.codec, &frames[i],
380 (unsigned)pcm_frame.size, &pcm_frame) );
381 if (app.wav) {
382 T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
383 }
384 if (app.aud_strm) {
385 T( wait_play(&pcm_frame) );
386 }
387 samples_cnt += samples_per_frame;
388 }
389
390 /* Read next packet */
391 read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp,
392 &pkt1.payload, &pkt1.payload_len, PJ_TRUE);
393
394 /* Fill in the gap (if any) between pkt0 and pkt1 */
395 ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) -
396 samples_cnt;
397 while (ts_gap >= (long)samples_per_frame) {
398
399 pcm_frame.buf = pcm;
400 pcm_frame.size = samples_per_frame * 2;
401
402 if (app.codec->op->recover) {
403 T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size,
404 &pcm_frame) );
405 } else {
406 pj_bzero(pcm_frame.buf, pcm_frame.size);
407 }
408
409 if (app.wav) {
410 T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
411 }
412 if (app.aud_strm) {
413 T( wait_play(&pcm_frame) );
414 }
415 ts_gap -= samples_per_frame;
416 }
417
418 /* Next */
419 pkt0 = pkt1;
420 pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer;
421 pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer);
422 }
423}
424
425
426int main(int argc, char *argv[])
427{
428 pj_str_t input, output, srtp_crypto, srtp_key, codec;
429 pjmedia_aud_dev_index dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
430 pj_pcap_filter filter;
431 pj_status_t status;
432
433 enum {
434 OPT_SRC_IP = 1, OPT_DST_IP, OPT_SRC_PORT, OPT_DST_PORT,
435 OPT_CODEC, OPT_PLAY_DEV_ID
436 };
437 struct pj_getopt_option long_options[] = {
438 { "srtp-crypto", 1, 0, 'c' },
439 { "srtp-key", 1, 0, 'k' },
440 { "src-ip", 1, 0, OPT_SRC_IP },
441 { "dst-ip", 1, 0, OPT_DST_IP },
442 { "src-port", 1, 0, OPT_SRC_PORT },
443 { "dst-port", 1, 0, OPT_DST_PORT },
444 { "codec", 1, 0, OPT_CODEC },
445 { "play-dev-id", 1, 0, OPT_PLAY_DEV_ID },
446 { NULL, 0, 0, 0}
447 };
448 int c;
449 int option_index;
450 char key_bin[32];
451
452 srtp_crypto.slen = srtp_key.slen = 0;
453 codec.slen = 0;
454
455 pj_pcap_filter_default(&filter);
456 filter.link = PJ_PCAP_LINK_TYPE_ETH;
457 filter.proto = PJ_PCAP_PROTO_TYPE_UDP;
458
459 /* Parse arguments */
460 pj_optind = 0;
461 while((c=pj_getopt_long(argc,argv, "c:k:", long_options, &option_index))!=-1) {
462 switch (c) {
463 case 'c':
464 srtp_crypto = pj_str(pj_optarg);
465 break;
466 case 'k':
467 {
468 int key_len = sizeof(key_bin);
469 srtp_key = pj_str(pj_optarg);
470 if (pj_base64_decode(&srtp_key, (pj_uint8_t*)key_bin, &key_len)) {
471 puts("Error: invalid key");
472 return 1;
473 }
474 srtp_key.ptr = key_bin;
475 srtp_key.slen = key_len;
476 }
477 break;
478 case OPT_SRC_IP:
479 {
480 pj_str_t t = pj_str(pj_optarg);
481 pj_in_addr a = pj_inet_addr(&t);
482 filter.ip_src = a.s_addr;
483 }
484 break;
485 case OPT_DST_IP:
486 {
487 pj_str_t t = pj_str(pj_optarg);
488 pj_in_addr a = pj_inet_addr(&t);
489 filter.ip_dst = a.s_addr;
490 }
491 break;
492 case OPT_SRC_PORT:
493 filter.src_port = pj_htons((pj_uint16_t)atoi(pj_optarg));
494 break;
495 case OPT_DST_PORT:
496 filter.dst_port = pj_htons((pj_uint16_t)atoi(pj_optarg));
497 break;
498 case OPT_CODEC:
499 codec = pj_str(pj_optarg);
500 break;
501 case OPT_PLAY_DEV_ID:
502 dev_id = atoi(pj_optarg);
503 break;
504 default:
505 puts("Error: invalid option");
506 return 1;
507 }
508 }
509
510 if (pj_optind != argc - 2) {
511 puts(USAGE);
512 return 1;
513 }
514
515 if (!(srtp_crypto.slen) != !(srtp_key.slen)) {
516 puts("Error: both SRTP crypto and key must be specified");
517 puts(USAGE);
518 return 1;
519 }
520
521 input = pj_str(argv[pj_optind]);
522 output = pj_str(argv[pj_optind+1]);
523
524 T( pj_init() );
525
526 pj_caching_pool_init(&app.cp, NULL, 0);
527 app.pool = pj_pool_create(&app.cp.factory, "pcaputil", 1000, 1000, NULL);
528
529 T( pjlib_util_init() );
530 T( pjmedia_endpt_create(&app.cp.factory, NULL, 0, &app.mept) );
531
532 T( pj_pcap_open(app.pool, input.ptr, &app.pcap) );
533 T( pj_pcap_set_filter(app.pcap, &filter) );
534
535 pcap2wav(&codec, &output, dev_id, &srtp_crypto, &srtp_key);
536
537 cleanup();
538 return 0;
539}
540