4 * $Id: tap_rtp.c,v 1.10 2003/04/23 08:20:06 guy Exp $
6 * RTP analysing addition for ethereal
8 * Copyright 2003, Iskratel, Ltd, Kranj
9 * By Miha Jemec <m.jemec@iskratel.si>
11 * Ethereal - Network traffic analyzer
12 * By Gerald Combs <gerald@ethereal.com>
13 * Copyright 1998 Gerald Combs
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 * This tap works as follows:
30 * When the user clicks on the RTP analisys button, we first check if it is a RTP packet.
31 * If yes we store the SSRC, ip, and port values. Then the tap is registered and the
32 * redissect_packets() routine is called. So we go through all the RTP packets and search
33 * for SSRC of reversed connection (it has inversed socket parameters). If more than one
34 * is found a window is displayed where the user can select the appropriate from the list.
35 * Rigth now we have the information about the converstion we are looking for (both SSRC).
36 * The redissect_packets() routine is called again. This time whenever a RTP packet with
37 * matching SSRC values arrives, we store all the information we need (number, sequence
38 * number, arrival time, ...) and compute the delay, jitter and wrong sequence number.
39 * We add this values to CList. If the RTP packet carries voice in g711 alaw or ulaw, we
40 * also store this voice information in a temp file. Window is displayed.
41 * Then three buttons are available: Close, Refresh and Save voice.
42 * The Refresh button calls the redissect_packets() routine again. It goes through the packets
43 * again and does all the calculation again (if capturing in real time this means that some
44 * more packets could come and can be computed in statistic). It also writes the sound
46 * The Save voice button opens the dialog where we can choose the file name, format (not yet)
47 * and direction we want to save. Currently it works only with g711 alaw and ulaw, and if the
48 * length of captured packets is equal the length of packets on wire and if there are no padding
52 * - Support for saving voice in more different formats and with more different codecs:
53 * Since this should be portable to all OS, there is only possibility to save the
54 * voice in a file and not play it directly through the sound card. There are enough
55 * players on all platforms, that are doing right this. What about the format?
56 * Currently there is only support for saving as an .au file (ulaw, 8000 Hz, 8bit)
57 * There are many players for this format on all platforms (for example Windows Media Player
58 * under Windows, command play under Linux). Support will be added for wav format and
59 * possibility to save with two channels (separate channel for each direction)
61 * - Support for more codecs. Now you can save voice only if the codec is g.711 alaw or ulaw.
63 * - right now, the reversed connection must have the same (only inversed) ip and port numbers.
64 * I think that there is no reason that in special cases the reversed connection would not use
65 * some different port or even the IP combination (please correct me if I am wrong).
66 * So this will be added soon.
68 * - some more statistics (delay and jitter distribution)
70 * - GTK2 implementation
72 * - grammar correction
74 * - some more testing (other OS)
78 * - instead of tmpnam() use of mkstemp().
79 * I tried to do it with mkstemp() but didn't now how to solve following problem:
80 * I call mkstemp() and then write in this temp file and it works fine . But if the user
81 * then hits the refresh button, this temp file should be deleted and opened again. I tried
82 * to call close() and unlink(), but when I call mkstemp() for the second time I always get
83 * an error ( -1) as return value. What is the correct order? Is it possible to call
84 * mkstemp() twice with the same template?
86 * - problem with statistics for lost (late, duplicated) packets. How to make the statistic
87 * more resistant to special (bizarre) arrival of sequence numbers
96 #ifdef HAVE_SYS_TYPES_H
97 # include <sys/types.h>
103 #include "epan/packet_info.h"
104 #include <epan/epan_dissect.h>
105 #include <epan/filesystem.h>
107 #include "../register.h"
108 #include "../packet-rtp.h"
109 #include "file_dlg.h"
110 #include "dlg_utils.h"
112 #include "simple_dialog.h"
116 #include "progress_dlg.h"
117 #include "compat_macros.h"
125 #include <io.h> /* open/close on win32 */
132 static GtkWidget *rtp_w = NULL;
133 static GtkWidget *save_voice_as_w = NULL;
134 static GtkWidget *main_vb;
135 static GtkWidget *clist;
136 static GtkWidget *clist_r;
137 static GtkWidget *max;
138 static GtkWidget *max_r;
140 static gboolean copy_file(gchar *, /*gint,*/ gint, void *);
142 static char f_tempname[100], r_tempname[100];
144 /* type of error when saving voice in a file didn't succeed */
147 TAP_RTP_WRONG_LENGTH,
149 TAP_RTP_FILE_OPEN_ERROR,
153 /* structure that holds the information about the forwarding and reversed connection */
154 /* f_* always aplies to the forward direction and r_* to the reversed */
155 typedef struct _info_stat {
157 gchar destination[16];
160 guint32 ssrc_forward;
161 guint32 ssrc_reversed;
163 gboolean search_ssrc;
165 guint reversed_ip_and_port;
166 gboolean f_first_packet;
167 gboolean r_first_packet;
172 guint32 f_delta_timestamp;
173 guint32 r_delta_timestamp;
186 guint16 f_start_seq_nr;
187 guint16 r_start_seq_nr;
188 guint16 f_stop_seq_nr;
189 guint16 r_stop_seq_nr;
202 error_type_t f_error_type;
203 error_type_t r_error_type;
209 /* when there is a [re]reading of packet's */
215 rs->f_first_packet = TRUE;
216 rs->r_first_packet = TRUE;
225 rs->f_start_seq_nr = 0;
226 rs->r_start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
227 rs->f_stop_seq_nr = 0;
228 rs->r_stop_seq_nr = 0;
235 rs->f_start_time = 0;
236 rs->r_start_time = 0;
239 /* XXX check for error at fclose? */
240 if (rs->f_fp != NULL)
242 if (rs->r_fp != NULL)
244 rs->f_fp = fopen(f_tempname, "wb");
245 if (rs->f_fp == NULL)
246 rs->f_error_type = TAP_RTP_FILE_OPEN_ERROR;
247 rs->r_fp = fopen(r_tempname, "wb");
248 if (rs->r_fp == NULL)
249 rs->r_error_type = TAP_RTP_FILE_OPEN_ERROR;
253 /* here we can redraw the output */
255 static void rtp_draw(void *prs _U_)
260 /* when we are finished with redisection, we add the label for the statistic */
261 static void draw_stat(void *prs)
264 gchar label_max[200];
265 guint32 f_expected = (rs->f_stop_seq_nr + rs->f_cycles*65536) - rs->f_start_seq_nr + 1;
266 guint32 r_expected = (rs->r_stop_seq_nr + rs->r_cycles*65536) - rs->r_start_seq_nr + 1;
267 gint32 f_lost = f_expected - rs->f_total_nr;
268 gint32 r_lost = r_expected - rs->r_total_nr;
270 g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n"
271 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
272 " Sequence error = %u",
273 rs->f_max_delay, rs->f_max_nr, rs->f_total_nr, f_expected,
274 f_lost, rs->f_sequence);
276 gtk_label_set_text(GTK_LABEL(max), label_max);
278 g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n"
279 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
280 " Sequence error = %u",
281 rs->r_max_delay, rs->r_max_nr, rs->r_total_nr, r_expected,
282 r_lost, rs->r_sequence);
284 gtk_label_set_text(GTK_LABEL(max_r), label_max);
286 /* could be done somewhere else, but can be here as well */
287 /* if this is true, then we don't have any reversed connection, so the error type
288 * will be no data. This applies only the reversed connection */
289 if (rs->reversed_ip_and_port == 0)
290 rs->r_error_type = TAP_RTP_NO_DATA;
295 /* append a line to clist */
296 /* XXX is there a nicer way to make these assignements? */
297 static void add_to_clist(gboolean forward, guint32 number, guint16 seq_num,
298 double delay, double jitter, gboolean status, gboolean marker)
303 data[0]=&field[0][0];
304 data[1]=&field[1][0];
305 data[2]=&field[2][0];
306 data[3]=&field[3][0];
307 data[4]=&field[4][0];
308 data[5]=&field[5][0];
310 g_snprintf(field[0], 20, "%u", number);
311 g_snprintf(field[1], 20, "%u", seq_num);
312 g_snprintf(field[2], 20, "%f", delay);
313 g_snprintf(field[3], 20, "%f", jitter);
314 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
315 g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr.");
317 gtk_clist_append(GTK_CLIST(forward? clist : clist_r), data);
321 /* whenever a RTP packet is seen by the tap listener */
322 /* this function works as follows:
323 * 1) packets that are not displayed are ignored
325 * 2) are we searching what could be the reversed connection (looking for reversed SSRC)
326 * if yes, do the parameters match (inversed IP and port combination from the forward one)?
327 * if yes, do we already have this SSRC stored
329 * 3) if not, is current packet matching the forward direction
330 * is it the first time we see a packet in this direction
331 * if yes, store some values, add a line to list and save the voice info
332 * in a temporary file if the codec is supported and the RTP data is ok
333 * if not, is it a packet with mark bit set (there was silence surpression)
334 * same as above, only we have to add some silence in front of the voice data
335 * if not, then this must be a normal packet
336 * store the values and voice data
337 * 4) if not, is current packet matching the reversed connection
338 * (same as for number 3)
340 static int rtp_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, void *vpri)
343 struct _rtp_info *pri=vpri;
350 /* we ignore packets that are not displayed */
351 if (pinfo->fd->flags.passed_dfilter == 0)
354 /* are we looking for the SSRC of the reversed connection? */
355 if (rs->search_ssrc != FALSE) {
356 /* XXX what should be the rules for reversed connection?
357 * 1. It should should have same inversed IP and port numbers
358 * 2. If none are found, only inversed IP's - is this possible?
359 * 3. If none are found, there isn't any reversed connection
360 * XXX is it possible that the conversation is not P2P?
361 * Curretly it works only if it matches the number 1. */
363 /* have we found inverse parameters? */
364 if ( strcmp(ip_to_str(pinfo->src.data), rs->destination) == 0 &&
365 strcmp( ip_to_str(pinfo->dst.data), rs->source) == 0 ) {
367 /* do the ports also match? */
368 if ((rs->srcport == pinfo->destport) && (rs->dstport == pinfo->srcport)) {
369 /* ok, the ip and port combination does match
370 * do we already have this ssrc stored */
371 for(i=0; i< rs->reversed_ip_and_port; i++) {
372 if (pri->info_sync_src == *(rs->ssrc_tmp+i) )
376 /* no, we found new ssrc, let's store it */
377 rs->ssrc_tmp = (guint32*)g_realloc(rs->ssrc_tmp,
378 (i+1)*sizeof(guint32));
379 *(rs->ssrc_tmp+i) = pri->info_sync_src;
380 rs->reversed_ip_and_port++;
383 /* no, only ip addresses match */
384 /* XXX not implemented yet */
393 /* ok, we are not looking for SSRC of the reversed connection */
394 /* is it the forward direction?
395 * if yes, there 3 possibilities:
396 * a) is this the first packet we got in this direction?
397 * b) or is it a packet with the mark bit set?
398 * c) if neither then it is a "normal" packet */
399 else if (rs->ssrc_forward == pri->info_sync_src) {
401 if (rs->f_first_packet != FALSE) {
402 /* we store all the values */
403 rs->f_seq_num = pri->info_seq_num;
406 rs->f_first_packet = FALSE;
407 rs->f_timestamp = pri->info_timestamp;
408 rs->f_start_seq_nr = pri->info_seq_num;
409 rs->f_stop_seq_nr = pri->info_seq_num;
411 rs->f_time = (double)pinfo->fd->rel_secs +
412 (double) pinfo->fd->rel_usecs/1000000;
413 rs->f_start_time = rs->f_time;
414 /* and add a row to clist; delay and jitter are 0 for the first packet */
415 add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, 0, 0, TRUE, FALSE);
417 /* and now save the voice info */
419 /* if we couldn't open the tmp file for writing, then we set the flag */
420 if (rs->f_fp == NULL) {
422 rs->f_error_type = TAP_RTP_FILE_OPEN_ERROR;
425 /* if the captured length and packet length aren't equal, we quit
426 * because there is some information missing */
427 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
429 rs->f_error_type = TAP_RTP_WRONG_LENGTH;
432 /* if padding bit is set, we don't do it yet */
433 if (pri->info_padding_set != FALSE) {
435 rs->f_error_type = TAP_RTP_PADDING_SET;
438 /* is it the ulaw? */
439 if (pri->info_payload_type == 0) {
440 /* we put the pointer at the beggining of the RTP data, that is
441 * at the end of the current frame minus the length of the
442 * RTP field plus 12 for the RTP header */
443 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
444 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
445 tmp = (gint16 )ulaw2linear((unsigned char)*data);
446 fwrite(&tmp, 2, 1, rs->f_fp);
453 else if (pri->info_payload_type == 8) {
454 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
455 for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
456 tmp = (gint16 )alaw2linear((unsigned char)*data);
457 fwrite(&tmp, 2, 1, rs->f_fp);
463 /* unsupported codec or other error */
466 rs->f_error_type = TAP_RTP_WRONG_CODEC;
471 /* packet with mark bit set? */
472 if (pri->info_marker_set != FALSE) {
473 n_time = (double)pinfo->fd->rel_secs +
474 (double) pinfo->fd->rel_usecs/1000000;
475 /* jitter is calculated as for RCTP - RFC 1889
476 * J = J + ( | D(i-1, i) | - J) / 16
477 * XXX the output there should be in timestamp (probably miliseconds)
478 * units expressed as an unsigned integer, so should we do it the same?
479 * (currently we use seconds)
481 * XXX Packet loss in RTCP is calculated as the difference between the
482 * number of packets expected and actually received, where for actually
483 * received the number is simply the count of packets as they arrive,
484 * including any late or duplicate packets (this means that the number
485 * can be negative). For example, if the seq numbers of the arrived
486 * packets are: 1,2,3,4,5,5,7,7,9,10 the expected number is 10 and the
487 * the number of actually captured frames is also 10. So in upper
488 * calculation there would be no losses. But there are 2 losses and
489 * 2 duplicate packets. Because this kind of statistic is rather
490 * useless (or confusing) we add the information, that there was
491 * an error with sequence number each time the sequence number was
492 * not one bigger than the previous one
495 /* jitter calculation */
496 n_jitter = rs->f_jitter + ( fabs(n_time-(rs->f_time) -
497 ((double)(pri->info_timestamp)-
498 (double)(rs->f_timestamp))/8000) - rs->f_jitter)/16;
500 /* we add the information into the clist */
501 add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->f_time),
502 n_jitter, rs->f_seq_num+1 == pri->info_seq_num?TRUE:FALSE, TRUE);
504 /* when calculating expected rtp packets the seq number can wrap around
505 * so we have to count the number of cycles
506 * f_cycles counts the wraps around in forwarding connection and
507 * f_under is flag that indicates where we are
509 * XXX how to determine number of cycles with all possible lost, late
510 * and duplicated packets without any doubt? It seems to me, that
511 * because of all possible combination of late, duplicated or lost
512 * packets, this can only be more or less good approximation
514 * There are some combinations (rare but theoretically possible),
515 * where below code won't work correctly - statistic may be wrong then.
518 /* so if the current sequence number is less than the start one
519 * we assume, that there is another cycle running */
520 if ((pri->info_seq_num < rs->f_start_seq_nr) && (rs->f_under == FALSE)){
524 /* what if the start seq nr was 0. Then the above condition will never
525 * be true, so we add another condition. XXX The problem would arise if
526 * if one of the packets with seq nr 0 or 65535 would be lost or late */
527 else if ((pri->info_seq_num == 0) && (rs->f_stop_seq_nr == 65535) &&
528 (rs->f_under == FALSE)){
532 /* the whole round is over, so reset the flag */
533 else if ((pri->info_seq_num>rs->f_start_seq_nr)&&(rs->f_under!=FALSE)){
537 /* number of times where sequence number was not ok */
538 if ( rs->f_seq_num+1 == pri->info_seq_num)
539 rs->f_seq_num = pri->info_seq_num;
540 /* XXX same problem as above */
541 else if ( (rs->f_seq_num == 65535) && (pri->info_seq_num == 0) )
542 rs->f_seq_num = pri->info_seq_num;
544 else if (rs->f_seq_num+1 < pri->info_seq_num) {
545 rs->f_seq_num = pri->info_seq_num;
548 /* late or duplicated */
549 else if (rs->f_seq_num+1 > pri->info_seq_num)
552 rs->f_stop_seq_nr = pri->info_seq_num;
554 rs->f_jitter = n_jitter;
555 rs->f_delta_timestamp = pri->info_timestamp - rs->f_timestamp;
556 rs->f_timestamp = pri->info_timestamp;
559 /* save the voice information */
560 /* if there was already an error, we quit */
561 if (rs->f_saved == FALSE)
563 /* if the captured length and packet length aren't equal, we quit */
564 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
566 rs->f_error_type = TAP_RTP_WRONG_LENGTH;
569 /* if padding bit is set, we don't do it yet */
570 if (pri->info_padding_set != FALSE) {
572 rs->f_error_type = TAP_RTP_PADDING_SET;
575 /* because the mark bit is set, we have to add some silence in front */
576 /* is it the ulaw? */
577 if (pri->info_payload_type == 0) {
578 /* we insert some silence */
579 /* XXX the amount of silence should be the difference between
580 * the last timestamp and the current one minus x in the
581 * I am not sure if x is equal the amount of information
583 for(i=0; i<(rs->f_delta_timestamp-pri->info_data_len+12); i++) {
584 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
585 fwrite(&tmp, 2, 1, rs->f_fp);
588 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
589 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
590 tmp = (gint16 )ulaw2linear((unsigned char)*data);
591 fwrite(&tmp, 2, 1, rs->f_fp);
597 else if (pri->info_payload_type == 8) {
598 for(i=0; i < (rs->f_delta_timestamp-pri->info_data_len+12); i++) {
599 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
600 fwrite(&tmp, 2, 1, rs->f_fp);
603 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
604 for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
605 tmp = (gint16 )alaw2linear((unsigned char)*data);
606 fwrite(&tmp, 2, 1, rs->f_fp);
611 /* unsupported codec or other error */
614 rs->f_error_type = TAP_RTP_WRONG_CODEC;
620 /* normal packet in forward connection */
621 n_time = (double)pinfo->fd->rel_secs +
622 (double) pinfo->fd->rel_usecs/1000000;
623 n_jitter = rs->f_jitter + ( fabs (n_time-(rs->f_time) -
624 ((double)(pri->info_timestamp)-
625 (double)(rs->f_timestamp))/8000) - rs->f_jitter)/16;
626 rs->f_delay = n_time-(rs->f_time);
627 /* the delay is bigger than previous max delay, so store the delay and nr */
628 if (rs->f_delay > rs->f_max_delay) {
629 rs->f_max_delay = rs->f_delay;
630 rs->f_max_nr = pinfo->fd->num;
632 add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->f_time),
633 n_jitter, rs->f_seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE);
635 /* count the cycles */
636 if ((pri->info_seq_num < rs->f_start_seq_nr) && (rs->f_under == FALSE)){
640 else if ((pri->info_seq_num == 0) && (rs->f_stop_seq_nr == 65535) &&
641 (rs->f_under == FALSE)){
645 /* the whole round is over, so reset the flag */
646 else if ((pri->info_seq_num>rs->f_start_seq_nr+1)&&(rs->f_under!=FALSE)){
650 /* number of times where sequence number was not ok */
651 if ( rs->f_seq_num+1 == pri->info_seq_num)
652 rs->f_seq_num = pri->info_seq_num;
653 else if ( (rs->f_seq_num == 65535) && (pri->info_seq_num == 0) )
654 rs->f_seq_num = pri->info_seq_num;
656 else if (rs->f_seq_num+1 < pri->info_seq_num) {
657 rs->f_seq_num = pri->info_seq_num;
660 /* late or duplicated */
661 else if (rs->f_seq_num+1 > pri->info_seq_num)
664 rs->f_stop_seq_nr = pri->info_seq_num;
666 rs->f_jitter = n_jitter;
667 rs->f_timestamp = pri->info_timestamp;
670 /* save the voice information */
671 /* we do it only in following cases:
672 * - the codecs we support are g.711 alaw in ulaw
673 * - the captured length must equal the packet length
674 * - XXX we don't support it if there are padding bits
676 /* if there was already an error, we quit */
677 if (rs->f_saved == FALSE)
679 /* if the captured length and packet length aren't equal, we quit */
680 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
682 rs->f_error_type = TAP_RTP_WRONG_LENGTH;
685 /* if padding bit is set, we don't do it yet */
686 if (pri->info_padding_set != FALSE) {
688 rs->f_error_type = TAP_RTP_PADDING_SET;
691 /* is it the ulaw? */
692 if (pri->info_payload_type == 0) {
693 /* cfile.pd points at the beggining of the actual packet. We have
694 * to move this pointer at the RTP data. This is the packet length,
695 * minus whole RTP data length (including the RTP header, that is
697 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
698 for(i=0; i < (pri->info_data_len - 12); i++, data++) {
699 tmp = (gint16 )ulaw2linear((unsigned char)*data);
700 fwrite(&tmp, 2, 1, rs->f_fp);
706 else if (pri->info_payload_type == 8) {
707 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
708 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
709 tmp = (gint16 )alaw2linear((unsigned char)*data);
710 fwrite(&tmp, 2, 1, rs->f_fp);
715 /* unsupported codec or other error */
718 rs->f_error_type = TAP_RTP_WRONG_CODEC;
723 /* is it the reversed direction? */
724 else if (rs->ssrc_reversed == pri->info_sync_src) {
726 if (rs->r_first_packet !=FALSE) {
727 rs->r_seq_num = pri->info_seq_num;
730 rs->r_first_packet = FALSE;
731 rs->r_timestamp = pri->info_timestamp;
732 rs->r_start_seq_nr = pri->info_seq_num;
733 rs->r_stop_seq_nr = pri->info_seq_num;
735 rs->r_time = (double)pinfo->fd->rel_secs +
736 (double) pinfo->fd->rel_usecs/1000000;
737 rs->r_start_time = rs->r_time;
738 add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num, 0, 0, TRUE, FALSE);
741 /* if we couldn't open the tmp file for writing, then we set the flag */
742 if (rs->r_fp == NULL) {
746 /* if the captured length and packet length aren't equal, we quit */
747 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
749 rs->r_error_type = TAP_RTP_WRONG_LENGTH;
752 /* if padding bit is set, we don't do it yet */
753 if (pri->info_padding_set != FALSE) {
755 rs->r_error_type = TAP_RTP_PADDING_SET;
758 /* is it the ulaw? */
759 if (pri->info_payload_type == 0) {
760 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
761 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
762 tmp = (gint16 )ulaw2linear((unsigned char)*data);
763 fwrite(&tmp, 2, 1, rs->r_fp);
770 else if (pri->info_payload_type == 8) {
771 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
772 for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
773 tmp = (gint16 )alaw2linear((unsigned char)*data);
774 fwrite(&tmp, 2, 1, rs->r_fp);
780 /* unsupported codec or other error */
783 rs->r_error_type = TAP_RTP_WRONG_CODEC;
788 /* packet with mark bit set? */
789 if (pri->info_marker_set != FALSE) {
790 n_time = (double)pinfo->fd->rel_secs +
791 (double) pinfo->fd->rel_usecs/1000000;
792 n_jitter = rs->r_jitter + ( fabs (n_time-(rs->r_time) -
793 ((double)(pri->info_timestamp)-
794 (double)(rs->r_timestamp))/8000) - rs->r_jitter)/16;
795 add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->r_time),
796 n_jitter, rs->r_seq_num+1 == pri->info_seq_num?TRUE:FALSE, TRUE);
798 /* count the cycles */
799 if ((pri->info_seq_num < rs->r_start_seq_nr) && (rs->r_under == FALSE)){
803 else if ((pri->info_seq_num == 0) && (rs->r_stop_seq_nr == 65535) &&
804 (rs->r_under == FALSE)){
808 /* the whole round is over, so reset the flag */
809 else if ((pri->info_seq_num>rs->r_start_seq_nr+1)&&(rs->r_under!=FALSE)){
813 /* number of times where sequence number was not ok */
814 if ( rs->r_seq_num+1 == pri->info_seq_num)
815 rs->r_seq_num = pri->info_seq_num;
816 else if ( (rs->r_seq_num == 65535) && (pri->info_seq_num == 0) )
817 rs->r_seq_num = pri->info_seq_num;
819 else if (rs->r_seq_num+1 < pri->info_seq_num) {
820 rs->r_seq_num = pri->info_seq_num;
823 /* late or duplicated */
824 else if (rs->r_seq_num+1 > pri->info_seq_num)
827 rs->r_stop_seq_nr = pri->info_seq_num;
829 rs->r_jitter = n_jitter;
830 rs->r_delta_timestamp = pri->info_timestamp - rs->r_timestamp;
831 rs->r_timestamp = pri->info_timestamp;
834 /* save the voice information */
835 /* if there was already an error, we quit */
836 if (rs->r_saved == FALSE)
838 /* if the captured length and packet length aren't equal, we quit */
839 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
841 rs->r_error_type = TAP_RTP_WRONG_LENGTH;
844 /* if padding bit is set, we don't do it yet */
845 if (pri->info_padding_set != FALSE) {
847 rs->r_error_type = TAP_RTP_PADDING_SET;
850 /* because the mark bit is set, we have to add some silence in front */
851 /* is it the ulaw? */
852 if (pri->info_payload_type == 0) {
853 /* we insert some silence */
854 for(i=0; i<(rs->r_delta_timestamp-pri->info_data_len+12); i++) {
855 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
856 fwrite(&tmp, 2, 1, rs->r_fp);
859 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
860 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
861 tmp = (gint16 )ulaw2linear((unsigned char)*data);
862 fwrite(&tmp, 2, 1, rs->r_fp);
868 else if (pri->info_payload_type == 8) {
869 for(i=0; i < (rs->r_delta_timestamp-pri->info_data_len+12); i++) {
870 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
871 fwrite(&tmp, 2, 1, rs->r_fp);
874 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
875 for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
876 tmp = (gint16 )alaw2linear((unsigned char)*data);
877 fwrite(&tmp, 2, 1, rs->r_fp);
882 /* unsupported codec or other error */
885 rs->r_error_type = TAP_RTP_WRONG_CODEC;
891 /* normal packet in reversed connection */
892 n_time = (double)pinfo->fd->rel_secs +
893 (double) pinfo->fd->rel_usecs/1000000;
894 n_jitter = rs->r_jitter + ( fabs (n_time-(rs->r_time) -
895 ((double)(pri->info_timestamp)-
896 (double)(rs->r_timestamp))/8000) - rs->r_jitter)/16;
897 rs->r_delay = n_time-(rs->r_time);
898 if (rs->r_delay > rs->r_max_delay) {
899 rs->r_max_delay = rs->r_delay;
900 rs->r_max_nr = pinfo->fd->num;
902 add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->r_time),
903 n_jitter, rs->r_seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE);
904 /* count the cycles */
905 if ((pri->info_seq_num < rs->r_start_seq_nr) && (rs->r_under == FALSE)){
909 else if ((pri->info_seq_num == 0) && (rs->r_stop_seq_nr == 65535) &&
910 (rs->r_under == FALSE)){
914 /* the whole round is over, so reset the flag */
915 else if ((pri->info_seq_num>rs->r_start_seq_nr+1)&&(rs->r_under!=FALSE)){
919 /* number of times where sequence number was not ok */
920 if ( rs->r_seq_num+1 == pri->info_seq_num)
921 rs->r_seq_num = pri->info_seq_num;
922 else if ( (rs->r_seq_num == 65535) && (pri->info_seq_num == 0) )
923 rs->r_seq_num = pri->info_seq_num;
925 else if (rs->r_seq_num+1 < pri->info_seq_num) {
926 rs->r_seq_num = pri->info_seq_num;
929 /* late or duplicated */
930 else if (rs->r_seq_num+1 > pri->info_seq_num)
933 rs->r_stop_seq_nr = pri->info_seq_num;
935 rs->r_jitter = n_jitter;
936 rs->r_timestamp = pri->info_timestamp;
939 /* save the voice information */
940 /* if there was already an error, we quit */
941 if (rs->r_saved == FALSE)
943 /* if the captured length and packet length aren't equal, we quit */
944 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
946 rs->r_error_type = TAP_RTP_WRONG_LENGTH;
949 /* if padding bit is set, we don't do it yet */
950 if (pri->info_padding_set != FALSE) {
952 rs->r_error_type = TAP_RTP_PADDING_SET;
955 /* is it the ulaw? */
956 if (pri->info_payload_type == 0) {
957 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
958 for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
959 tmp = (gint16 )ulaw2linear((unsigned char)*data);
960 fwrite(&tmp, 2, 1, rs->r_fp);
966 else if (pri->info_payload_type == 8) {
967 data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
968 for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
969 tmp = (gint16 )alaw2linear((unsigned char)*data);
970 fwrite(&tmp, 2, 1, rs->r_fp);
975 /* unsupported codec or other error */
978 rs->r_error_type = TAP_RTP_WRONG_CODEC;
986 /* XXX just copied from gtk/rpc_stat.c */
987 void protect_thread_critical_region(void);
988 void unprotect_thread_critical_region(void);
991 /* here we close the rtp analysis dialog window and remove the tap listener */
992 static void rtp_destroy_cb(GtkWidget *win _U_, gpointer data _U_)
994 info_stat *rs=(info_stat *)data;
996 protect_thread_critical_region();
997 remove_tap_listener(rs);
998 unprotect_thread_critical_region();
1000 /* xxx is this enough? */
1001 g_free(rs->ssrc_tmp);
1004 if (rs->f_fp != NULL)
1006 if (rs->r_fp != NULL)
1011 /* Is there a save voice window open? */
1012 if (save_voice_as_w != NULL)
1013 gtk_widget_destroy(save_voice_as_w);
1015 /* Note that we no longer have a "RTP Analyse" dialog box. */
1019 /* when the close button in rtp window was clicked */
1020 /* it seems to me that rtp_destroy_cb is automatically called, so we don't
1021 * need to do the g_free... and rtp_w = NULL ... */
1022 static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w)
1024 gtk_grab_remove(GTK_WIDGET(parent_w));
1025 gtk_widget_destroy(GTK_WIDGET(parent_w));
1028 /* we search the rtp.ssrc node here (thanks to Guy Harris - code here is magic for me */
1029 static guint32 process_node(proto_item *ptree_node, header_field_info *hfinformation)
1032 proto_item *proto_sibling_node;
1033 header_field_info *hfssrc;
1036 finfo = PITEM_FINFO(ptree_node);
1038 if (hfinformation==(finfo->hfinfo)) {
1039 hfssrc = proto_registrar_get_byname("rtp.ssrc");
1042 for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL;
1043 ptree_node=g_node_next_sibling(ptree_node)) {
1044 finfo=PITEM_FINFO(ptree_node);
1045 if (hfssrc==finfo->hfinfo) {
1046 ssrc = fvalue_get_integer(finfo->value);
1052 proto_sibling_node = g_node_next_sibling(ptree_node);
1054 if (proto_sibling_node) {
1055 ssrc = process_node(proto_sibling_node, hfinformation);
1062 /* here we search the rtp protocol */
1063 static guint32 process_tree(proto_tree *protocol_tree)
1065 proto_item *ptree_node;
1066 header_field_info *hfinformation;
1068 hfinformation = proto_registrar_get_byname("rtp");
1069 if (hfinformation == NULL)
1072 ptree_node = g_node_first_child(protocol_tree);
1076 return process_node(ptree_node, hfinformation);
1079 /* when we want to update the information */
1080 static void refresh_cb(GtkWidget *w _U_, void *pri)
1084 gtk_clist_clear(GTK_CLIST(clist));
1085 gtk_clist_clear(GTK_CLIST(clist_r));
1086 redissect_packets(&cfile);
1090 static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1092 /* Note that we no longer have a Save voice info dialog box. */
1093 save_voice_as_w = NULL;
1096 /* the user wants to save in a file */
1097 /* XXX support for different formats is currently commented out */
1098 static void save_voice_as_ok_cb(GtkWidget *ok_bt, gpointer fs)
1101 /*GtkWidget *wav, *au, *sw;*/
1102 GtkWidget *rev, *forw, *both;
1104 gint channels /*, format*/;
1106 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1108 /* Perhaps the user specified a directory instead of a file.
1109 Check whether they did. */
1110 if (test_for_directory(g_dest) == EISDIR) {
1111 /* It's a directory - set the file selection box to display it. */
1112 set_last_open_dir(g_dest);
1114 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1118 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1119 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1120 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1121 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1122 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1123 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1124 rs = (info_stat *)OBJECT_GET_DATA(ok_bt, "info_stat");
1126 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1127 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1128 * disable the ok button or disable the buttons for direction if only one is not ok. The
1129 * problem is if we open the save voice dialog and then click the refresh button and maybe
1130 * the state changes, so we can't save anymore. In this case we should be able to update
1131 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1134 /* we can not save in both dirctions */
1135 if ((rs->f_saved == FALSE) && (rs->r_saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1136 /* there are many combinations here, we just exit when first matches */
1137 if ((rs->f_error_type == TAP_RTP_WRONG_CODEC) || (rs->r_error_type == TAP_RTP_WRONG_CODEC))
1138 simple_dialog(ESD_TYPE_CRIT, NULL,
1139 "Can't save in a file: Unsupported codec!");
1140 else if ((rs->f_error_type == TAP_RTP_WRONG_LENGTH) || (rs->r_error_type == TAP_RTP_WRONG_LENGTH))
1141 simple_dialog(ESD_TYPE_CRIT, NULL,
1142 "Can't save in a file: Wrong length of captured packets!");
1143 else if ((rs->f_error_type == TAP_RTP_PADDING_SET) || (rs->r_error_type == TAP_RTP_PADDING_SET))
1144 simple_dialog(ESD_TYPE_CRIT, NULL,
1145 "Can't save in a file: RTP data with padding!");
1147 simple_dialog(ESD_TYPE_CRIT, NULL,
1148 "Can't save in a file: File I/O problem!");
1151 /* we can not save forward direction */
1152 else if ((rs->f_saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1153 (GTK_TOGGLE_BUTTON (both)->active))) {
1154 if (rs->f_error_type == TAP_RTP_WRONG_CODEC)
1155 simple_dialog(ESD_TYPE_CRIT, NULL,
1156 "Can't save forward direction in a file: Unsupported codec!");
1157 else if (rs->f_error_type == TAP_RTP_WRONG_LENGTH)
1158 simple_dialog(ESD_TYPE_CRIT, NULL,
1159 "Can't save forward direction in a file: Wrong length of captured packets!");
1160 else if (rs->f_error_type == TAP_RTP_PADDING_SET)
1161 simple_dialog(ESD_TYPE_CRIT, NULL,
1162 "Can't save forward direction in a file: RTP data with padding!");
1164 simple_dialog(ESD_TYPE_CRIT, NULL,
1165 "Can't save forward direction in a file: File I/O problem!");
1168 /* we can not save reversed direction */
1169 else if ((rs->r_saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1170 (GTK_TOGGLE_BUTTON (both)->active))) {
1171 if (rs->r_error_type == TAP_RTP_WRONG_CODEC)
1172 simple_dialog(ESD_TYPE_CRIT, NULL,
1173 "Can't save reversed direction in a file: Unsupported codec!");
1174 else if (rs->r_error_type == TAP_RTP_WRONG_LENGTH)
1175 simple_dialog(ESD_TYPE_CRIT, NULL,
1176 "Can't save reversed direction in a file: Wrong length of captured packets!");
1177 else if (rs->r_error_type == TAP_RTP_PADDING_SET)
1178 simple_dialog(ESD_TYPE_CRIT, NULL,
1179 "Can't save reversed direction in a file: RTP data with padding!");
1180 else if (rs->r_error_type == TAP_RTP_NO_DATA)
1181 simple_dialog(ESD_TYPE_CRIT, NULL,
1182 "Can't save reversed direction in a file: No RTP data!");
1184 simple_dialog(ESD_TYPE_CRIT, NULL,
1185 "Can't save reversed direction in a file: File I/O problem!");
1189 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1191 else if (GTK_TOGGLE_BUTTON (au)->active)
1193 else if (GTK_TOGGLE_BUTTON (sw)->active)
1196 if (GTK_TOGGLE_BUTTON (rev)->active)
1198 else if (GTK_TOGGLE_BUTTON (both)->active)
1203 if(!copy_file(g_dest, channels/*, format*/, rs)) {
1204 simple_dialog(ESD_TYPE_CRIT, NULL, "An error occured while saving voice in a file!");
1208 /* XXX I get GTK warning (sometimes?)!!! */
1209 gtk_widget_destroy(GTK_WIDGET(save_voice_as_w));
1212 /* when the user wants to save the voice information in a file */
1213 /* XXX support for different formats is currently commented out */
1214 static void save_voice_as_cb(GtkWidget *w _U_, gpointer data)
1216 info_stat *rs=(info_stat *)data;
1220 GtkWidget *label_format;
1221 GtkWidget *channels_label;
1222 /*GSList *format_group = NULL;*/
1223 GSList *channels_group = NULL;
1224 GtkWidget *forward_rb;
1225 GtkWidget *reversed_rb;
1227 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1230 /* if we can't save in a file: wrong codec, cut packets or other errors */
1231 /* shold the error arise here or later when you click ok button ?
1232 * if we do it here, then we must disable the refresh button, so we don't do it here */
1234 if (save_voice_as_w != NULL) {
1235 /* There's already a Save voice info dialog box; reactivate it. */
1236 reactivate_window(save_voice_as_w);
1240 save_voice_as_w = gtk_file_selection_new("Ethereal: Save Voice Data As");
1241 gtk_signal_connect(GTK_OBJECT(save_voice_as_w), "destroy",
1242 GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), NULL);
1244 /* Container for each row of widgets */
1245 vertb = gtk_vbox_new(FALSE, 0);
1246 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1247 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_voice_as_w)->action_area),
1248 vertb, FALSE, FALSE, 0);
1249 gtk_widget_show (vertb);
1251 table1 = gtk_table_new (2, 4, FALSE);
1252 gtk_widget_show (table1);
1253 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1254 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1255 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1257 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1258 gtk_widget_show (label_format);
1259 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1260 (GtkAttachOptions) (GTK_FILL),
1261 (GtkAttachOptions) (0), 0, 0);
1263 /* we support .au - ulaw*/
1264 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1265 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1266 gtk_widget_show (wav_rb);
1267 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1268 (GtkAttachOptions) (GTK_FILL),
1269 (GtkAttachOptions) (0), 0, 0);
1271 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1272 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1273 gtk_widget_show (sw_rb);
1274 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1275 (GtkAttachOptions) (GTK_FILL),
1276 (GtkAttachOptions) (0), 0, 0);
1277 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1278 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1279 gtk_widget_show (au_rb);
1280 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1281 (GtkAttachOptions) (GTK_FILL),
1282 (GtkAttachOptions) (0), 0, 0);
1285 channels_label = gtk_label_new ("Channels:");
1286 gtk_widget_show (channels_label);
1287 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1288 (GtkAttachOptions) (GTK_FILL),
1289 (GtkAttachOptions) (0), 0, 0);
1290 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1292 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1293 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1294 gtk_widget_show (forward_rb);
1295 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1296 (GtkAttachOptions) (GTK_FILL),
1297 (GtkAttachOptions) (0), 0, 0);
1299 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1300 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1301 gtk_widget_show (reversed_rb);
1302 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1303 (GtkAttachOptions) (GTK_FILL),
1304 (GtkAttachOptions) (0), 0, 0);
1306 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1307 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1308 gtk_widget_show (both_rb);
1309 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1310 (GtkAttachOptions) (GTK_FILL),
1311 (GtkAttachOptions) (0), 0, 0);
1313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1315 /* if one direction is nok we don't allow saving
1316 XXX this is not ok since the user can click the refresh button and cause changes
1317 but we can not update this window. So we move all the decision on the time the ok
1319 if (rs->f_saved == FALSE) {
1320 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1321 gtk_widget_set_sensitive(forward_rb, FALSE);
1322 gtk_widget_set_sensitive(both_rb, FALSE);
1324 else if (rs->r_saved == FALSE) {
1325 gtk_widget_set_sensitive(reversed_rb, FALSE);
1326 gtk_widget_set_sensitive(both_rb, FALSE);
1330 ok_bt = GTK_FILE_SELECTION(save_voice_as_w)->ok_button;
1331 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1332 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1333 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1334 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1335 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1336 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1337 OBJECT_SET_DATA(ok_bt, "info_stat", rs);
1339 /* Connect the cancel_button to destroy the widget */
1340 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(save_voice_as_w)->cancel_button,
1341 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1344 /* Catch the "key_press_event" signal in the window, so that we can catch
1345 the ESC key being pressed and act as if the "Cancel" button had
1347 dlg_set_cancel(save_voice_as_w, GTK_FILE_SELECTION(save_voice_as_w)->cancel_button);
1349 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
1350 GTK_SIGNAL_FUNC(save_voice_as_ok_cb), save_voice_as_w);
1352 gtk_widget_show(save_voice_as_w);
1355 /* all the graphics on the window is done here */
1356 static void add_rtp_notebook(void *pri)
1360 GtkWidget *notebook, *page, *page_r, *label, *label1, *label2, *label3;
1361 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1362 GtkWidget *box4, *voice_bt, *refresh_bt, *close_bn;
1364 gchar *titles[6] = {"Packet nr.", "Sequence", "Delay (s)", "Jitter (s)", "Marker", "Status"};
1365 gchar label_forward[150];
1366 gchar label_reverse[150];
1368 g_snprintf(label_forward, 149,
1369 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1370 rs->source, rs->srcport, rs->destination, rs->dstport, rs->ssrc_forward);
1371 g_snprintf(label_reverse, 149,
1372 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1373 rs->destination, rs->dstport, rs->source, rs->srcport, rs->ssrc_reversed);
1375 gtk_widget_destroy(main_vb);
1376 main_vb = gtk_vbox_new(FALSE, 3);
1377 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1378 gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
1379 gtk_widget_show(main_vb);
1381 /* Start a nootbook for flipping between sets of changes */
1382 notebook = gtk_notebook_new();
1383 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1384 gtk_object_set_data(GTK_OBJECT(rtp_w), "notebook", notebook);
1386 /* page for forward connection */
1387 page = gtk_vbox_new(FALSE, 5);
1388 gtk_container_set_border_width(GTK_CONTAINER(page), 20);
1390 /* scrolled window */
1391 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1392 gtk_widget_set_usize(scrolled_window, 600, 200);
1393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1394 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1396 /* direction label */
1397 label1 = gtk_label_new(label_forward);
1398 gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
1400 /* place for some statistics */
1401 max = gtk_label_new("\n\n");
1402 gtk_box_pack_end(GTK_BOX(page), max, FALSE, FALSE, 5);
1404 /* clist for the information */
1405 clist = gtk_clist_new_with_titles(6, titles);
1406 gtk_widget_show(clist);
1407 gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
1408 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1411 label = gtk_label_new(" Forward Direction ");
1412 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1414 /* column width and justification */
1415 gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80);
1416 gtk_clist_set_column_width(GTK_CLIST(clist), 1, 80);
1417 gtk_clist_set_column_width(GTK_CLIST(clist), 2, 80);
1418 gtk_clist_set_column_width(GTK_CLIST(clist), 3, 80);
1419 gtk_clist_set_column_width(GTK_CLIST(clist), 4, 40);
1420 gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER);
1421 gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER);
1422 gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER);
1423 gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER);
1424 gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER);
1425 gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_CENTER);
1427 /* same page for reversed connection */
1428 page_r = gtk_vbox_new(FALSE, 5);
1429 gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
1430 scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
1431 gtk_widget_set_usize(scrolled_window_r, 600, 200);
1432 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
1433 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1434 label3 = gtk_label_new(label_reverse);
1435 gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
1436 max_r = gtk_label_new("\n\n");
1437 gtk_box_pack_end(GTK_BOX(page_r), max_r, FALSE, FALSE, 5);
1438 clist_r = gtk_clist_new_with_titles(6, titles);
1439 gtk_widget_show(clist_r);
1440 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_r);
1441 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
1442 label2 = gtk_label_new(" Reversed Direction ");
1443 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
1445 gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
1446 gtk_clist_set_column_width(GTK_CLIST(clist_r), 1, 80);
1447 gtk_clist_set_column_width(GTK_CLIST(clist_r), 2, 80);
1448 gtk_clist_set_column_width(GTK_CLIST(clist_r), 3, 80);
1449 gtk_clist_set_column_width(GTK_CLIST(clist_r), 4, 40);
1450 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 0, GTK_JUSTIFY_CENTER);
1451 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 1, GTK_JUSTIFY_CENTER);
1452 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 2, GTK_JUSTIFY_CENTER);
1453 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 3, GTK_JUSTIFY_CENTER);
1454 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 4, GTK_JUSTIFY_CENTER);
1455 gtk_clist_set_column_justification(GTK_CLIST(clist_r), 5, GTK_JUSTIFY_CENTER);
1457 /* page for help&about or future
1458 page_help = gtk_hbox_new(FALSE, 5);
1459 label4 = gtk_label_new(" Future ");
1460 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
1461 frame = gtk_frame_new("");
1462 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
1463 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
1464 gtk_container_add(GTK_CONTAINER(frame), text);
1465 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
1466 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
1468 /* show all notebooks */
1469 gtk_widget_show_all(notebook);
1471 /* and the buttons */
1472 box4 = gtk_hbutton_box_new();
1473 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, TRUE, 0);
1474 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
1475 gtk_button_box_set_layout(GTK_BUTTON_BOX(box4), GTK_BUTTONBOX_SPREAD);
1476 gtk_widget_show(box4);
1478 voice_bt = gtk_button_new_with_label("Save voice data as...");
1479 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
1480 gtk_widget_show(voice_bt);
1481 gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked",
1482 GTK_SIGNAL_FUNC(save_voice_as_cb), rs);
1484 refresh_bt = gtk_button_new_with_label("Refresh");
1485 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
1486 gtk_widget_show(refresh_bt);
1487 gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked",
1488 GTK_SIGNAL_FUNC(refresh_cb), rs);
1490 close_bn = gtk_button_new_with_label("Close");
1491 gtk_container_add(GTK_CONTAINER(box4), close_bn);
1492 gtk_widget_show(close_bn);
1493 gtk_signal_connect(GTK_OBJECT(close_bn), "clicked",
1494 GTK_SIGNAL_FUNC(rtp_destroy), GTK_OBJECT(rtp_w));
1496 redissect_packets(&cfile);
1502 /* when we click on the selected row it copies that ssrc value into ssrc_reversed */
1503 static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column,
1504 GdkEventButton *event _U_, gpointer data)
1506 info_stat *rs=(info_stat *)data;
1509 gtk_clist_get_text(GTK_CLIST(clist_r), row, column, &text);
1510 /* XXX is this strtoul portable for guint32? */
1511 rs->ssrc_reversed = strtoul(text, (char **)NULL, 10);
1515 /* when we click apply button in ssrc reversed dialog */
1516 static void apply_selected_ssrc(GtkWidget *w _U_, gpointer data)
1518 info_stat *rs=(info_stat *)data;
1519 add_rtp_notebook(rs);
1522 /* this function goes through all the packets that have the same ip and port combination
1523 * (only inversed) as the forward direction (XXX what if the reversed direction doesn't use
1524 * the same ports???) and looks for different SSRC values. This can happen if you capture
1525 * two RTP conversations one after another from the same pair of phones (PC's).
1526 * Both have same IP's and can also have same port numbers, so they (should) differ only
1527 * in SSRC values. In such case we get a list of ssrc values and we have to choose the right
1528 * one from the list. If there is only one or none, we do it automatically */
1529 static void get_reversed_ssrc(void *prs)
1531 info_stat *ri = prs;
1532 GtkWidget *scroll_r, *clist_r, *ok_bt, *label, *label2, *label1, *main_hbnbox;
1536 switch(ri->reversed_ip_and_port)
1538 /* in case we haven't found any reversed ssrc */
1539 /* XXX in this case we could look for the inversed IP only */
1541 ri->ssrc_reversed = 0;
1542 ri->search_ssrc = FALSE;
1543 add_rtp_notebook(ri);
1546 /* in case we found exactly one matching ssrc for reversed connection */
1548 ri->ssrc_reversed = ri->ssrc_tmp[0];
1549 ri->search_ssrc = FALSE;
1550 add_rtp_notebook(ri);
1553 /* there is more then one matching ssrc, so we have to choose between them */
1555 ri->search_ssrc = FALSE;
1556 /* let's draw the window */
1557 label = gtk_label_new("Found more SSRC values for the reversed\n"
1558 "connection with following parameters:\n");
1559 g_snprintf(temp, 149, "Source %s port %u Destination %s port %u",
1560 ri->destination, ri->dstport, ri->source, ri->srcport);
1561 label2 = gtk_label_new(temp);
1562 gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE, FALSE, 0);
1563 gtk_box_pack_start(GTK_BOX(main_vb), label2, FALSE, FALSE, 0);
1564 scroll_r = gtk_scrolled_window_new(NULL, NULL);
1565 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_r),
1566 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1567 clist_r = gtk_clist_new(1);
1568 gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
1569 gtk_container_add(GTK_CONTAINER(scroll_r), clist_r);
1570 gtk_box_pack_start(GTK_BOX(main_vb), scroll_r, TRUE, TRUE, 0);
1571 label1 = gtk_label_new("Select one value and click apply");
1572 gtk_box_pack_start(GTK_BOX(main_vb), label1, FALSE, FALSE, 0);
1574 main_hbnbox = gtk_hbutton_box_new();
1575 gtk_box_pack_start(GTK_BOX(main_vb), main_hbnbox, FALSE, TRUE, 0);
1576 gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
1577 gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox),
1578 GTK_BUTTONBOX_SPREAD);
1579 gtk_widget_show(main_hbnbox);
1581 ok_bt = gtk_button_new_with_label("Apply");
1582 gtk_container_add(GTK_CONTAINER(main_hbnbox), ok_bt);
1583 gtk_signal_connect(GTK_OBJECT(clist_r), "select_row",
1584 GTK_SIGNAL_FUNC(get_selected_ssrc), ri);
1585 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
1586 GTK_SIGNAL_FUNC(apply_selected_ssrc), ri);
1588 /* add all the ssrc values in the clist */
1589 /* XXX I'm sure the tmp variable could be avoided here
1590 * i tried to assign guint32 from ri->ssrc_tmp somehow to gchar **text
1591 * but gave up. So if you can do this, just go ahead */
1592 for (i=0; i < ri->reversed_ip_and_port; i++) {
1595 g_snprintf(tmp, 20, "%u", ri->ssrc_tmp[i]);
1596 text[0] = (gchar *)&tmp;
1597 gtk_clist_append(GTK_CLIST(clist_r), text);
1600 gtk_clist_select_row(GTK_CLIST(clist_r), 0, 0);
1602 gtk_widget_show(label);
1603 gtk_widget_show(label1);
1604 gtk_widget_show(label2);
1605 gtk_widget_show(ok_bt);
1606 gtk_widget_show(clist_r);
1607 gtk_widget_show(scroll_r);
1612 /* XXX only handles RTP over IPv4, should add IPv6 support */
1613 /* when the user clicks the RTP dialog button */
1614 static void rtp_analyse_cb(GtkWidget *w _U_, gpointer data _U_)
1617 gchar filter_text[256];
1620 epan_dissect_t *edt;
1622 gboolean frame_matched;
1624 GString *error_string;
1626 /* There's already a "Display Options" dialog box; reactivate it. */
1627 if (rtp_w != NULL) {
1628 reactivate_window(rtp_w);
1632 /* Try to compile the filter. */
1633 strcpy(filter_text,"rtp && ip");
1634 if (!dfilter_compile(filter_text, &sfcode)) {
1635 simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
1638 /* we load the current file into cf variable */
1640 fdata = cf->current_frame;
1642 /* we are on the selected frame now */
1644 return; /* if we exit here it's an error */
1646 /* XXX instead of looking for RTP protocol like this, we could do the process_node() staff */
1647 /* dissect the current frame */
1648 wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err);
1649 edt = epan_dissect_new(TRUE, FALSE);
1650 epan_dissect_prime_dfilter(edt, sfcode);
1651 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
1652 frame_matched = dfilter_apply_edt(sfcode, edt);
1654 /* if it is not an rtp frame, exit */
1655 frame_matched = dfilter_apply_edt(sfcode, edt);
1656 if (frame_matched != 1) {
1657 epan_dissect_free(edt);
1658 simple_dialog(ESD_TYPE_CRIT, NULL, "You didn't choose a RTP packet!");
1662 /* in rs we put all the info */
1663 rs=g_malloc(sizeof(info_stat));
1665 /* ok, it is a RTP frame, so let's get the ip and port values */
1666 rs->srcport = edt->pi.srcport;
1667 rs->dstport = edt->pi.destport;
1668 strncpy(rs->source, ip_to_str(edt->pi.src.data), 16);
1669 strncpy(rs->destination, ip_to_str(edt->pi.dst.data), 16);
1671 /* now we need the SSRC value of the current frame */
1672 rs->ssrc_forward = process_tree(edt->tree);
1673 if (rs->ssrc_forward == 0) {
1674 simple_dialog(ESD_TYPE_CRIT, NULL, "SSRC value couldn't be found!");
1678 /* now we have all the information about the forwarding connection
1679 * we need to go through all the packets and search for reversed connection
1681 rs->search_ssrc = TRUE;
1682 rs->ssrc_reversed = 0;
1683 rs->reversed_ip = 0;
1684 rs->reversed_ip_and_port = 0;
1685 rs->ssrc_tmp = NULL;
1687 sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ) || ( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ))",
1688 ip_to_str(edt->pi.src.data),
1690 ip_to_str(edt->pi.dst.data),
1692 ip_to_str(edt->pi.dst.data),
1694 ip_to_str(edt->pi.src.data),
1697 /* XXX compiler warning:passing arg 5 of `register_tap_listener' from incompatible pointer type */
1698 error_string = register_tap_listener("rtp", rs, filter_text, rtp_reset, rtp_packet, rtp_draw);
1699 if (error_string != NULL) {
1700 simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
1701 /* XXX is this enough or do I have to free anything else? */
1702 g_string_free(error_string, TRUE);
1707 /* let's draw the window */
1708 rtp_w = dlg_window_new("Ethereal: RTP Analyse");
1709 gtk_window_set_position (GTK_WINDOW (rtp_w), GTK_WIN_POS_CENTER);
1710 gtk_signal_connect(GTK_OBJECT(rtp_w), "destroy",
1711 GTK_SIGNAL_FUNC(rtp_destroy_cb), rs);
1713 /* Container for each row of widgets */
1714 main_vb = gtk_vbox_new(FALSE, 3);
1715 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1716 gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
1717 gtk_widget_show(main_vb);
1719 /* file names for storing sound data */
1725 redissect_packets(cf);
1727 /* so how many reversed connection we have ? */
1728 get_reversed_ssrc(rs);
1730 /* and finally display this window */
1731 gtk_widget_show(rtp_w);
1735 rtp_analyse_init(char *dummy _U_)
1737 rtp_analyse_cb(NULL, NULL);
1741 register_tap_listener_gtkrtp(void)
1743 register_ethereal_tap("rtp", rtp_analyse_init);
1747 register_tap_menu_gtkrtp(void)
1749 register_tap_menu_item("RTP Analysis...", rtp_analyse_cb);
1753 /* here we save it into a file that user specified */
1754 /* XXX what about endians here? could go something wrong? */
1755 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ void *data)
1757 info_stat *rs=(info_stat *)data;
1758 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1762 guint32 f_write_silence = 0;
1763 guint32 r_write_silence = 0;
1765 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1766 gboolean stop_flag = FALSE;
1768 forw_fd = open(f_tempname, O_RDONLY | O_BINARY);
1771 rev_fd = open(r_tempname, O_RDONLY | O_BINARY);
1777 /* open file for saving */
1778 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1785 progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag);
1787 /* First we write the .au header. XXX Hope this is endian independant */
1788 /* the magic word 0x2e736e64 == .snd */
1789 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1790 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1791 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1792 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1793 /* header offset == 24 bytes */
1794 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1795 write(to_fd, pd, 1);
1796 write(to_fd, pd, 1);
1797 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1798 /* total length, it is permited to set this to 0xffffffff */
1799 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1800 write(to_fd, pd, 1);
1801 write(to_fd, pd, 1);
1802 write(to_fd, pd, 1);
1803 /* encoding format == 8 bit ulaw */
1804 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1805 write(to_fd, pd, 1);
1806 write(to_fd, pd, 1);
1807 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1808 /* sample rate == 8000 Hz */
1809 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1810 write(to_fd, pd, 1);
1811 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1812 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1814 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1815 write(to_fd, pd, 1);
1816 write(to_fd, pd, 1);
1817 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1820 /* only forward direction */
1822 progbar_count = rs->f_count;
1823 progbar_quantum = rs->f_count/100;
1824 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1827 if((count > progbar_nextstep) && (count <= progbar_count)) {
1828 update_progress_dlg(progbar,
1829 (gfloat) count/progbar_count, "Saving");
1830 progbar_nextstep = progbar_nextstep + progbar_quantum;
1833 *pd = (unsigned char)linear2ulaw(f_pd);
1834 fwritten = write(to_fd, pd, 1);
1835 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1839 destroy_progress_dlg(progbar);
1845 /* only reversed direction */
1847 progbar_count = rs->r_count;
1848 progbar_quantum = rs->r_count/100;
1849 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1852 if((count > progbar_nextstep) && (count <= progbar_count)) {
1853 update_progress_dlg(progbar,
1854 (gfloat) count/progbar_count, "Saving");
1855 progbar_nextstep = progbar_nextstep + progbar_quantum;
1858 *pd = (unsigned char)linear2ulaw(r_pd);
1859 rwritten = write(to_fd, pd, 1);
1860 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1864 destroy_progress_dlg(progbar);
1870 /* both directions */
1872 (rs->f_count > rs->r_count) ? (progbar_count = rs->f_count) :
1873 (progbar_count = rs->r_count);
1874 progbar_quantum = progbar_count/100;
1875 /* since conversation in one way can start later than in the other one,
1876 * we have to write some silence information for one channel */
1877 if (rs->f_start_time > rs->r_start_time) {
1878 f_write_silence = (rs->f_start_time-rs->r_start_time)*8000;
1880 else if (rs->f_start_time < rs->r_start_time) {
1881 r_write_silence = (rs->r_start_time-rs->f_start_time)*8000;
1886 if((count > progbar_nextstep) && (count <= progbar_count)) {
1887 update_progress_dlg(progbar,
1888 (gfloat) count/progbar_count, "Saving");
1889 progbar_nextstep = progbar_nextstep + progbar_quantum;
1892 if(f_write_silence > 0) {
1893 rread = read(rev_fd, &r_pd, 2);
1898 else if(r_write_silence > 0) {
1899 fread = read(forw_fd, &f_pd, 2);
1905 fread = read(forw_fd, &f_pd, 2);
1906 rread = read(rev_fd, &r_pd, 2);
1908 if ((rread == 0) && (fread == 0))
1910 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1911 rwritten = write(to_fd, pd, 1);
1912 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1916 destroy_progress_dlg(progbar);
1922 destroy_progress_dlg(progbar);