Ethereal-dev: Re: [Ethereal-dev] Ethereal addition for analysing RTP data

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: "Ronnie Sahlberg" <ronnie_sahlberg@xxxxxxxxxxxxxx>
Date: Thu, 6 Mar 2003 05:13:46 +1100
Very very cool,
This would be very nice to have in 0.9.10


I think you generated the diff in the reverse order.
the diff looks like it wants to remove the tap from packet-rtp.c,
that is no problem, patch is bright enough to detect this and do the right
thing
anyway.

But there seems to be one patch missing,
where is the _rtp_info struct defined?
is it in a missing patch to packet-rtp.h ?


As for your question in tap-rtp.c
The tap listeners are called after each individual packet is dissected.
The next packet will not be dissected until all previous *_packet() tap
listeners
have been called and have completed.
In other words, the dissection of packets and calling of *_packet() is
single threaded.
This means that there will not be any corruption if *_packet() is not fast
enough, however,
since a slow(==takes long to finish) *_packet() function will prevent
ethereal from dissecting the next
packet it is good to try to make the *_packet() function as fast as
possible.


best regards
    ronnie sahlberg

----- Original Message -----
From: "Miha Jemec"
Sent: Thursday, March 06, 2003 2:18 AM
Subject: [Ethereal-dev] Ethereal addition for analysing RTP data


> Hi!
>
> After quite some time here is the rewritten version of RTP analysing
> tool. It uses now the tap system (I had to change the packet-rtp.c and
> packet-rtp.h) and with the intention of being OS independent it doesn't
> make possible to play the voice directly through the sound card but has
> the possibility to save it in a file for later listening. How it works
> is described in code itself (I tried to put many comments there). There
> are still some open questions or problems (look for XXX). I was able to
> test it only under Intel machine with Linux OS, so test on other OS
> would be welcome.
>
> Attached files are:
>
> rtp.jpg - screenshot
> tap_rtp.c - actual code, that goes into gtk/ directory
> g711.c - routines for converting alaw or ulaw to linear, goes into gtk/
> as well
>
> I also modified some other files (ethereal 0.9.8):
>
> in gtk/menu.c one more line under "Tools":
>
> ITEM_FACTORY_ENTRY("/Tools/_RTP analysis...", NULL, rtp_analyse_cb, 0,
> NULL, NULL),
>
>
> in packet-rtp.h added following structure at the end:
>
> struct _rtp_info {
>           gboolean    info_padding_set;
>           gboolean    info_marker_set;
>           unsigned int info_payload_type;
>           unsigned int info_padding_count;
>           guint16     info_seq_num;
>           guint32     info_timestamp;
>           guint32     info_sync_src;
>           guint       info_data_len;
> };
>
>
> in packet-rtp.c: diff is attached
>
>
> Because I'm not very comfortable with automake , autoconf, ... I just
> added some lines by hand to compile:
>
> In etereal_tap_register two more lines:
>
>   { extern void register_tap_listener_gtkrtp (void);
>       register_tap_listener_gtkrtp ();}
>
> In gtk/Makefile in line am__objects_1 =
>   added  tap_rtp.$(OBJEXT)
>
>
> I would be glad to get any comments.
> Regards,
> Miha
>
>
>


----------------------------------------------------------------------------
----


> /*
>  * tap_rtp.c
>  *
>  * RTP analysing addition  for ethereal
>  *
>  * Copyright 2003, Iskratel, Ltd, Kranj
>  * By Miha Jemec <m.jemec@xxxxxxxxxxx>
>  *
>  * Ethereal - Network traffic analyzer
>  * By Gerald Combs <gerald@xxxxxxxxxxxx>
>  * Copyright 1998 Gerald Combs
>  *
>  * This program is free software; you can redistribute it and/or
>  * modify it under the terms of the GNU General Public License
>  * as published by the Free Software Foundation; either version 2
>  * of the License, or (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
USA.
>  *
>  * This tap works as follows:
>  * When the user clicks on the RTP analisys button, we first check if it
is a RTP packet.
>  * If yes we store the SSRC, ip, and port values. Then the tap is
registered and the
>  * redissect_packets() routine is called. So we go through all the RTP
packets and search
>  * for SSRC of reversed connection (it has inversed socket parameters). If
more than one
>  * is found a window is displayed where the user can select the
appropriate from the list.
>  * Rigth now we have the information about the converstion we are looking
for (both SSRC).
>  * The redissect_packets() routine is called again. This time whenever a
RTP packet with
>  * matching SSRC values arrives, we store all the information we need
(number, sequence
>  * number, arrival time, ...) and compute the delay, jitter and wrong
sequence number.
>  * We add this values to CList. If the RTP packet carries voice in g711
alaw or ulaw, we
>  * also store this voice information in a temp file. Window is displayed.
>  * Then three buttons are available: Close, Refresh and Save voice.
>  * The Refresh button calls the redissect_packets() routine again. It goes
through the packets
>  * again and does all the calculation again (if capturing in real time
this means that some
>  * more packets could come and can be computed in statistic). It also
writes the sound
>  * data again.
>  * The Save voice button opens the dialog where we can choose the file
name, format (not yet)
>  * and direction we want to save. Currently it works only with g711 alaw
and ulaw, and if the
>  * length of captured packets is equal the length of packets on wire and
if there are no padding
>  * bits.
>  *
>  * To do:
>  * - Support for saving voice in more different formats and with more
different codecs:
>  *   Since this should be portable to all OS, there is only possibility to
save the
>  *   voice in a file and not play it directly through the sound card.
There are enough
>  *   players on all platforms, that are doing right this. What about the
format?
>  *   Currently there is only support for saving as an .au file (ulaw, 8000
Hz, 8bit)
>  *   There are many players for this format on all platforms (for example
Windows Media Player
>  *   under Windows, command play under Linux). Support will be added for
wav format and
>  *   possibility to save with two channels (separate channel for each
direction)
>  *
>  * - Support for more codecs. Now you can save voice only if the codec is
g.711 alaw or ulaw.
>  *
>  * - right now, the reversed connection must have the same (only inversed)
ip and port numbers.
>  *   I think that there is no reason that in special cases the reversed
connection would not use
>  *   some different port or even the IP combination (please correct me if
I am wrong).
>  *   So this will be added soon.
>  *
>  * - some more statistics (delay and jitter distribution)
>  *
>  * - GTK2 implementation
>  *
>  * - grammar correction
>  *
>  * - some more testing (other OS)
>  *
>  * XXX Problems:
>  *
>  * - how to use snprintf (or g_snprintf) with guint16, guint32 ? If I put
%lu for guint32
>  *   then compiler makes a warning but it works. If I put %d for guint32,
>  *   then compiler doesn't warns, but then it doesn't work
>  *
>  * - instead of tmpnam() use of mkstemp().
>  *   I tried to do it with mkstemp() but didn't now how to solve following
problem:
>  *   I call mkstemp() and then write in this temp file and it works fine .
But if the user
>  *   then hits the refresh button, this temp file should be deleted and
opened again. I tried
>  *   to call close() and unlink(), but when I call mkstemp() for the
second time I always get
>  *   an error ( -1) as return value. What is the correct order? Is it
possible to call
>  *   mkstemp() twice with the same template?
>  *
>  * - since I don't know exactly how the tap events are scheduled , is it
possible that "next"
>  *   matching rtp frame will corrupt the information in struct info_stat
(is it possible
>  *   that rtp_packet is not "fast" enough?). I tried with 166Mhz procesor
and 30 Mb of captured
>  *   file and worked fine
>  *
>  * - problem with statistics for lost (late, duplicated) packets. How to
make the statistic
>  *   more resistant to special (bizarre) arrival of sequence numbers
>  */
>
> #ifdef HAVE_CONFIG_H
> # include "config.h"
> #endif
>
> #include <stdio.h>
>
> #ifdef HAVE_SYS_TYPES_H
> # include <sys/types.h>
> #endif
>
> #include "globals.h"
> #include <string.h>
> #include "epan/packet_info.h"
> #include <epan/epan_dissect.h>
> #include <epan/filesystem.h>
> #include "../tap.h"
> #include "../register.h"
> #include "../packet-rtp.h"
> #include <gtk/gtk.h>
> #include "file_dlg.h"
> #include "dlg_utils.h"
> #include "ui_util.h"
> #include "simple_dialog.h"
> #include "main.h"
> #include <math.h>
> #include "progress_dlg.h"
> #include "compat_macros.h"
> #include "g711.c"
> #include <unistd.h>
> #include <fcntl.h>
>
> static GtkWidget *rtp_w = NULL;
> static GtkWidget *voice_w = NULL;
> static GtkWidget *save_w = NULL;
> static GtkWidget *main_vb;
> static GtkWidget *clist;
> static GtkWidget *clist_r;
> static GtkWidget *max;
> static GtkWidget *max_r;
>
> static gboolean copy_file(gchar *, /*gint,*/ gint, void *);
>
> char f_tempname[100], r_tempname[100];
>
> /* type of error when saving voice in a file didn't succeed */
> typedef enum {
> WRONG_CODEC,
> WRONG_LENGTH,
> PADDING_SET,
> FILE_OPEN_ERROR,
> NO_DATA
> } error_type_t;
>
> /* structure that holds the information about the forwarding and reversed
connection */
> /* f_* always aplies to the forward direction and r_* to the reversed */
> typedef struct _info_stat {
> gchar source[16];
> gchar destination[16];
> guint16 srcport;
> guint16 dstport;
> guint32 ssrc_forward;
> guint32 ssrc_reversed;
> guint32 *ssrc_tmp;
> gboolean search_ssrc;
> guint reversed_ip;
> guint reversed_ip_and_port;
> gboolean f_first_packet;
> gboolean r_first_packet;
> guint16 f_seq_num;
> guint16 r_seq_num;
> guint32 f_timestamp;
> guint32 r_timestamp;
> guint32 f_delta_timestamp;
> guint32 r_delta_timestamp;
> double f_delay;
> double r_delay;
> double f_jitter;
> double r_jitter;
> double f_time;
> double r_time;
> double f_start_time;
> double r_start_time;
> double f_max_delay;
> double r_max_delay;
> guint32 f_max_nr;
> guint32 r_max_nr;
> guint16 f_start_seq_nr;
> guint16 r_start_seq_nr;
> guint16 f_stop_seq_nr;
> guint16 r_stop_seq_nr;
> guint32 f_total_nr;
> guint32 r_total_nr;
> guint32 f_sequence;
> guint32 r_sequence;
> gint f_cycles;
> gint r_cycles;
> gboolean f_under;
> gboolean r_under;
> FILE *f_fp;
> FILE *r_fp;
> gboolean f_saved;
> gboolean r_saved;
> error_type_t f_error_type;
> error_type_t r_error_type;
> guint32 f_count;
> guint32 r_count;
> } info_stat;
>
>
> /* when there is a [re]reading of packet's */
> static void
> rtp_reset(void *prs)
> {
>   info_stat *rs=prs;
>
>   rs->f_first_packet = TRUE;
>   rs->r_first_packet = TRUE;
>   rs->f_max_delay = 0;
>   rs->r_max_delay = 0;
>   rs->f_max_nr = 0;
>   rs->r_max_nr = 0;
>   rs->f_total_nr = 0;
>   rs->r_total_nr = 0;
>   rs->f_sequence = 0;
>   rs->r_sequence = 0;
>   rs->f_start_seq_nr = 0;
>   rs->r_start_seq_nr = 1; /* 1 is ok (for statistics in reversed
direction) */
>   rs->f_stop_seq_nr = 0;
>   rs->r_stop_seq_nr = 0;
>   rs->f_cycles = 0;
>   rs->r_cycles = 0;
>   rs->f_under = FALSE;
>   rs->r_under = FALSE;
>   rs->f_saved = FALSE;
>   rs->r_saved = FALSE;
>   rs->f_start_time = 0;
>   rs->r_start_time = 0;
>   rs->f_count = 0;
>   rs->r_count = 0;
>   /* XXX check for error at fclose? */
>   if (rs->f_fp != NULL)
> fclose(rs->f_fp);
>   if (rs->r_fp != NULL)
> fclose(rs->r_fp);
>   rs->f_fp = fopen(f_tempname, "w");
>   if (rs->f_fp == NULL)
> rs->f_error_type = FILE_OPEN_ERROR;
>   rs->r_fp = fopen(r_tempname, "w");
>   if (rs->r_fp == NULL)
> rs->r_error_type = FILE_OPEN_ERROR;
>   return;
> }
>
> /* here we can redraw the output */
> /* not used yet */
> static void rtp_draw(void *prs _U_)
> {
> return;
> }
>
> /* when we are finished with redisection, we add the label for the
statistic */
> static void draw_stat(void *prs)
> {
> info_stat *rs=prs;
> gchar label_max[200];
> guint32 f_expected = (rs->f_stop_seq_nr + rs->f_cycles*65536) -
rs->f_start_seq_nr + 1;
> guint32 r_expected = (rs->r_stop_seq_nr + rs->r_cycles*65536) -
rs->r_start_seq_nr + 1;
> gint32 f_lost = f_expected - rs->f_total_nr;
> gint32 r_lost = r_expected - rs->r_total_nr;
>
> g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %lu \n\n"
> "Total RTP packets = %lu   (expected %lu)   Lost RTP packets = %ld"
> "   Sequence error = %lu",
> rs->f_max_delay, rs->f_max_nr, rs->f_total_nr, f_expected,
> f_lost, rs->f_sequence);
>
> gtk_label_set_text(GTK_LABEL(max), label_max);
>
> g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %lu \n\n"
> "Total RTP packets = %lu   (expected %lu)   Lost RTP packets = %ld"
> "   Sequence error = %lu",
> rs->r_max_delay, rs->r_max_nr, rs->r_total_nr, r_expected,
> r_lost, rs->r_sequence);
>
> gtk_label_set_text(GTK_LABEL(max_r), label_max);
>
> /* could be done somewhere else, but can be here as well */
> /* if this is true, then we don't have any reversed connection, so the
error type
> * will be no data. This applies only the reversed connection */
> if (rs->reversed_ip_and_port == 0)
> rs->r_error_type = NO_DATA;
>
> return ;
> }
>
> /* append a line to clist */
> /* XXX is there a nicer way to make these assignements? */
> static void add_to_clist(gboolean forward, guint32 number, guint16
seq_num,
> double delay, double jitter, gboolean status, gboolean marker)
> {
> gchar *data[6];
> gchar field[6][30];
>
> data[0]=&field[0][0];
> data[1]=&field[1][0];
> data[2]=&field[2][0];
> data[3]=&field[3][0];
> data[4]=&field[4][0];
> data[5]=&field[5][0];
>
> g_snprintf(field[0], 20, "%lu", number);
> g_snprintf(field[1], 20, "%lu", seq_num);
> g_snprintf(field[2], 20, "%f", delay);
> g_snprintf(field[3], 20, "%f", jitter);
> g_snprintf(field[4], 20, "%s", marker? "SET" : "");
> g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr.");
>
> gtk_clist_append(GTK_CLIST(forward? clist : clist_r), data);
>
> }
>
> /* whenever a RTP packet is seen by the tap listener */
> /* this function works as follows:
>  * 1) packets that are not displayed are ignored
>  * return
>  * 2) are we searching what could be the reversed connection (looking for
reversed SSRC)
>  * if yes, do the parameters match (inversed IP and port combination from
the forward one)?
>  * if yes, do we already have this SSRC stored
>  * if not store it
>  * 3) if not, is current packet matching the forward direction
>  * is it the first time we see a packet in this direction
>  * if yes, store some values, add a line to list and save the voice info
>  * in a temporary file if the codec is supported and the RTP data is ok
>  * if not, is it a packet with mark bit set (there was silence
surpression)
>  * same as above, only we have to add some silence in front of the voice
data
>  * if not, then this must be a normal packet
>  * store the values and voice data
>  * 4) if not, is current packet matching the reversed connection
>  * (same as for number 3)
>  */
> static int rtp_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt
_U_, struct _rtp_info *pri)
> {
> info_stat *rs=prs;
> guint i;
> double n_time;
> double n_jitter;
> guint8 *data;
> gint16 tmp;
>
> /* we ignore packets that are not displayed */
> if (pinfo->fd->flags.passed_dfilter == 0)
> return 0;
>
> /* are we looking for the SSRC of the reversed connection? */
> if (rs->search_ssrc != FALSE) {
> /* XXX what should be the rules for reversed connection?
> * 1. It should should have same inversed IP and port numbers
> * 2. If none are found, only inversed IP's - is this possible?
> * 3. If none are found, there isn't any reversed connection
> * XXX is it possible that the conversation is not P2P?
> * Curretly it works only if it matches the number 1. */
>
> /* have we found inverse parameters? */
> if ( strcmp(ip_to_str(pinfo->src.data), rs->destination) == 0  &&
> strcmp( ip_to_str(pinfo->dst.data), rs->source) == 0 ) {
>
> /* do the ports also match? */
> if ((rs->srcport == pinfo->destport) && (rs->dstport == pinfo->srcport)) {
> /* ok, the ip and port combination does match
> * do we already have this ssrc stored */
> for(i=0; i< rs->reversed_ip_and_port; i++) {
> if (pri->info_sync_src == *(rs->ssrc_tmp+i) )
> return 0;
> }
>
> /* no, we found new ssrc, let's store it */
> rs->ssrc_tmp = (guint32*)g_realloc(rs->ssrc_tmp,
> (i+1)*sizeof(guint32));
> *(rs->ssrc_tmp+i) = pri->info_sync_src;
> rs->reversed_ip_and_port++;
> return 0;
> }
> /* no, only ip addresses match */
> /* XXX not implemented yet */
> else {
> rs->reversed_ip++;
> return 0;
> }
>
> }
> }
>
> /* ok, we are not looking for SSRC of the reversed connection */
> /* is it the forward direction?
> * if yes, there 3 possibilities:
> * a) is this the first packet we got in this direction?
> * b) or is it a packet with the mark bit set?
> * c) if neither then it is a "normal" packet */
> else if (rs->ssrc_forward == pri->info_sync_src) {
> /* first packet? */
> if (rs->f_first_packet != FALSE) {
> /* we store all the values */
> rs->f_seq_num = pri->info_seq_num;
> rs->f_delay = 0;
> rs->f_jitter = 0;
> rs->f_first_packet = FALSE;
> rs->f_timestamp = pri->info_timestamp;
> rs->f_start_seq_nr = pri->info_seq_num;
> rs->f_stop_seq_nr = pri->info_seq_num;
> rs->f_total_nr++;
> rs->f_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> rs->f_start_time = rs->f_time;
> /* and add a row to clist; delay and jitter are 0 for the first packet */
> add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, 0, 0, TRUE, FALSE);
>
> /* and now save the voice info */
>
> /* if we couldn't open the tmp file for writing, then we set the flag */
> if (rs->f_fp == NULL) {
> rs->f_saved = FALSE;
> rs->f_error_type = FILE_OPEN_ERROR;
> return 0;
> }
> /* if the captured length and packet length aren't equal, we quit
> * because there is some information missing */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->f_saved = FALSE;
> rs->f_error_type = PADDING_SET;
> return 0;
> }
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> /* we put the pointer at the beggining of the RTP data, that is
> * at the end of the current frame minus the length of the
> * RTP field plus 12 for the RTP header */
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> rs->f_saved = TRUE;
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> rs->f_saved = TRUE;
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_CODEC;
> return 0;
> }
> }
>
> /* packet with mark bit set? */
> if (pri->info_marker_set != FALSE) {
> n_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> /* jitter is calculated as for RCTP - RFC 1889
> * J = J + ( | D(i-1, i) | - J) / 16
> * XXX the output there should be in timestamp (probably miliseconds)
> * units expressed as an unsigned integer, so should we do it the same?
> * (currently we use seconds)
> *
> * XXX Packet loss in RTCP is calculated as the difference between the
> * number of packets expected and actually received, where for actually
> * received the number is simply the count of packets as they arrive,
> * including any late or duplicate packets (this means that the number
> * can be negative). For example, if the seq numbers of the arrived
> * packets are: 1,2,3,4,5,5,7,7,9,10 the expected number is 10 and the
> * the number of actually captured frames is also 10. So in upper
> * calculation there would be no losses. But there are 2 losses and
> * 2 duplicate packets. Because this kind of statistic is rather
> * useless (or confusing) we add the information, that there was
> * an error with sequence number each time the sequence number was
> * not one bigger than the previous one
> */
>
> /* jitter calculation */
> n_jitter = rs->f_jitter + ( fabs(n_time-(rs->f_time) -
> ((double)(pri->info_timestamp)-
> (double)(rs->f_timestamp))/8000) - rs->f_jitter)/16;
>
> /* we add the information into the clist */
> add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->f_time),
> n_jitter, rs->f_seq_num+1 == pri->info_seq_num?TRUE:FALSE, TRUE);
>
> /* when calculating expected rtp packets the seq number can wrap around
> * so we have to count the number of cycles
> * f_cycles counts the wraps around in forwarding connection and
> * f_under is flag that indicates where we are
> *
> * XXX how to determine number of cycles with all possible lost, late
> * and duplicated packets without any doubt? It seems to me, that
> * because of all possible combination of late, duplicated or lost
> * packets, this can only be more or less good approximation
> *
> * There are some combinations (rare but theoretically possible),
> * where below code won't work correctly - statistic may be wrong then.
> */
>
> /* so if the current sequence number is less than the start one
> * we assume, that there is another cycle running */
> if ((pri->info_seq_num < rs->f_start_seq_nr) && (rs->f_under == FALSE)){
> rs->f_cycles++;
> rs->f_under = TRUE;
> }
> /* what if the start seq nr was 0. Then the above condition will never
> * be true, so we add another condition. XXX The problem would arise if
> * if one of the packets with seq nr 0 or 65535 would be lost or late */
> else if ((pri->info_seq_num == 0) && (rs->f_stop_seq_nr == 65535) &&
> (rs->f_under == FALSE)){
> rs->f_cycles++;
> rs->f_under = TRUE;
> }
> /* the whole round is over, so reset the flag */
> else if ((pri->info_seq_num>rs->f_start_seq_nr)&&(rs->f_under!=FALSE)){
> rs->f_under = FALSE;
> }
>
> /* number of times where sequence number was not ok */
> if ( rs->f_seq_num+1 == pri->info_seq_num)
> rs->f_seq_num = pri->info_seq_num;
> /* XXX same problem as above */
> else if ( (rs->f_seq_num == 65535) && (pri->info_seq_num == 0) )
> rs->f_seq_num = pri->info_seq_num;
> /* lost packets */
> else if (rs->f_seq_num+1 < pri->info_seq_num) {
> rs->f_seq_num = pri->info_seq_num;
> rs->f_sequence++;
> }
> /* late or duplicated */
> else if (rs->f_seq_num+1 > pri->info_seq_num)
> rs->f_sequence++;
>
> rs->f_stop_seq_nr = pri->info_seq_num;
> rs->f_time = n_time;
> rs->f_jitter = n_jitter;
> rs->f_delta_timestamp = pri->info_timestamp - rs->f_timestamp;
> rs->f_timestamp = pri->info_timestamp;
> rs->f_total_nr++;
>
> /* save the voice information */
> /* if there was already an error, we quit */
> if (rs->f_saved == FALSE)
> return 0;
> /* if the captured length and packet length aren't equal, we quit */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->f_saved = FALSE;
> rs->f_error_type = PADDING_SET;
> return 0;
> }
> /* because the mark bit is set, we have to add some silence in front */
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> /* we insert some silence */
> /* XXX the amount of silence should be the difference between
> * the last timestamp and the current one minus x in the
> * I am not sure if x is equal the amount of information
> * current packet? */
> for(i=0; i<(rs->f_delta_timestamp-pri->info_data_len+12); i++) {
> tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> for(i=0; i < (rs->f_delta_timestamp-pri->info_data_len+12); i++) {
> tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_CODEC;
> return 0;
> }
> return 0;
> }
>
> /* normal packet in forward connection */
> n_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> n_jitter = rs->f_jitter + ( fabs (n_time-(rs->f_time) -
> ((double)(pri->info_timestamp)-
> (double)(rs->f_timestamp))/8000) - rs->f_jitter)/16;
> rs->f_delay =  n_time-(rs->f_time);
> /* the delay is bigger than previous max delay, so store the delay and nr
*/
> if (rs->f_delay > rs->f_max_delay) {
> rs->f_max_delay = rs->f_delay;
> rs->f_max_nr = pinfo->fd->num;
> }
> add_to_clist(TRUE, pinfo->fd->num, pri->info_seq_num, n_time-(rs->f_time),
> n_jitter, rs->f_seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE);
>
> /* count the cycles */
> if ((pri->info_seq_num < rs->f_start_seq_nr) && (rs->f_under == FALSE)){
> rs->f_cycles++;
> rs->f_under = TRUE;
> }
> else if ((pri->info_seq_num == 0) && (rs->f_stop_seq_nr == 65535) &&
> (rs->f_under == FALSE)){
> rs->f_cycles++;
> rs->f_under = TRUE;
> }
> /* the whole round is over, so reset the flag */
> else if ((pri->info_seq_num>rs->f_start_seq_nr+1)&&(rs->f_under!=FALSE)){
> rs->f_under = FALSE;
> }
>
> /* number of times where sequence number was not ok */
> if ( rs->f_seq_num+1 == pri->info_seq_num)
> rs->f_seq_num = pri->info_seq_num;
> else if ( (rs->f_seq_num == 65535) && (pri->info_seq_num == 0) )
> rs->f_seq_num = pri->info_seq_num;
> /* lost packets */
> else if (rs->f_seq_num+1 < pri->info_seq_num) {
> rs->f_seq_num = pri->info_seq_num;
> rs->f_sequence++;
> }
> /* late or duplicated */
> else if (rs->f_seq_num+1 > pri->info_seq_num)
> rs->f_sequence++;
>
> rs->f_stop_seq_nr = pri->info_seq_num;
> rs->f_time = n_time;
> rs->f_jitter = n_jitter;
> rs->f_timestamp = pri->info_timestamp;
> rs->f_total_nr++;
>
> /* save the voice information */
> /* we do it only in following cases:
> * - the codecs we support are g.711 alaw in ulaw
> * - the captured length must equal the packet length
> * - XXX we don't support it if there are padding bits
> */
> /* if there was already an error, we quit */
> if (rs->f_saved == FALSE)
> return 0;
> /* if the captured length and packet length aren't equal, we quit */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->f_saved = FALSE;
> rs->f_error_type = PADDING_SET;
> return 0;
> }
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> /* cfile.pd points at the beggining of the actual packet. We have
> * to move this pointer at the RTP data. This is the packet length,
> * minus whole RTP data length (including the RTP header, that is
> * why we add 12) */
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len - 12); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->f_fp);
> rs->f_count++;
> }
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->f_saved = FALSE;
> rs->f_error_type = WRONG_CODEC;
> return 0;
> }
> }
>
> /* is it the reversed direction? */
> else if (rs->ssrc_reversed == pri->info_sync_src) {
> /* first packet? */
> if (rs->r_first_packet !=FALSE) {
> rs->r_seq_num = pri->info_seq_num;
> rs->r_delay = 0;
> rs->r_jitter = 0;
> rs->r_first_packet = FALSE;
> rs->r_timestamp = pri->info_timestamp;
> rs->r_start_seq_nr = pri->info_seq_num;
> rs->r_stop_seq_nr = pri->info_seq_num;
> rs->r_total_nr++;
> rs->r_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> rs->r_start_time = rs->r_time;
> add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num, 0, 0, TRUE, FALSE);
>
> /* save it */
> /* if we couldn't open the tmp file for writing, then we set the flag */
> if (rs->r_fp == NULL) {
> rs->r_saved = FALSE;
> return 0;
> }
> /* if the captured length and packet length aren't equal, we quit */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->r_saved = FALSE;
> rs->r_error_type = PADDING_SET;
> return 0;
> }
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> rs->r_saved = TRUE;
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> rs->r_saved = TRUE;
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_CODEC;
> return 0;
> }
> }
>
> /* packet with mark bit set? */
> if (pri->info_marker_set != FALSE) {
> n_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> n_jitter = rs->r_jitter + ( fabs (n_time-(rs->r_time) -
> ((double)(pri->info_timestamp)-
> (double)(rs->r_timestamp))/8000) - rs->r_jitter)/16;
> add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num,
n_time-(rs->r_time),
> n_jitter, rs->r_seq_num+1 == pri->info_seq_num?TRUE:FALSE, TRUE);
>
> /* count the cycles */
> if ((pri->info_seq_num < rs->r_start_seq_nr) && (rs->r_under == FALSE)){
> rs->r_cycles++;
> rs->r_under = TRUE;
> }
> else if ((pri->info_seq_num == 0) && (rs->r_stop_seq_nr == 65535) &&
> (rs->r_under == FALSE)){
> rs->r_cycles++;
> rs->r_under = TRUE;
> }
> /* the whole round is over, so reset the flag */
> else if ((pri->info_seq_num>rs->r_start_seq_nr+1)&&(rs->r_under!=FALSE)){
> rs->r_under = FALSE;
> }
>
> /* number of times where sequence number was not ok */
> if ( rs->r_seq_num+1 == pri->info_seq_num)
> rs->r_seq_num = pri->info_seq_num;
> else if ( (rs->r_seq_num == 65535) && (pri->info_seq_num == 0) )
> rs->r_seq_num = pri->info_seq_num;
> /* lost packets */
> else if (rs->r_seq_num+1 < pri->info_seq_num) {
> rs->r_seq_num = pri->info_seq_num;
> rs->r_sequence++;
> }
> /* late or duplicated */
> else if (rs->r_seq_num+1 > pri->info_seq_num)
> rs->r_sequence++;
>
> rs->r_stop_seq_nr = pri->info_seq_num;
> rs->r_time = n_time;
> rs->r_jitter = n_jitter;
> rs->r_delta_timestamp = pri->info_timestamp - rs->r_timestamp;
> rs->r_timestamp = pri->info_timestamp;
> rs->r_total_nr++;
>
> /* save the voice information */
> /* if there was already an error, we quit */
> if (rs->r_saved == FALSE)
> return 0;
> /* if the captured length and packet length aren't equal, we quit */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->r_saved = FALSE;
> rs->r_error_type = PADDING_SET;
> return 0;
> }
> /* because the mark bit is set, we have to add some silence in front */
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> /* we insert some silence */
> for(i=0; i<(rs->r_delta_timestamp-pri->info_data_len+12); i++) {
> tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> for(i=0; i < (rs->r_delta_timestamp-pri->info_data_len+12); i++) {
> tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_CODEC;
> return 0;
> }
> return 0;
> }
>
> /* normal packet in reversed connection */
> n_time = (double)pinfo->fd->rel_secs +
> (double) pinfo->fd->rel_usecs/1000000;
> n_jitter = rs->r_jitter + ( fabs (n_time-(rs->r_time) -
> ((double)(pri->info_timestamp)-
> (double)(rs->r_timestamp))/8000) - rs->r_jitter)/16;
> rs->r_delay =  n_time-(rs->r_time);
> if (rs->r_delay > rs->r_max_delay) {
> rs->r_max_delay = rs->r_delay;
> rs->r_max_nr = pinfo->fd->num;
> }
> add_to_clist(FALSE, pinfo->fd->num, pri->info_seq_num,
n_time-(rs->r_time),
> n_jitter, rs->r_seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE);
> /* count the cycles */
> if ((pri->info_seq_num < rs->r_start_seq_nr) && (rs->r_under == FALSE)){
> rs->r_cycles++;
> rs->r_under = TRUE;
> }
> else if ((pri->info_seq_num == 0) && (rs->r_stop_seq_nr == 65535) &&
> (rs->r_under == FALSE)){
> rs->r_cycles++;
> rs->r_under = TRUE;
> }
> /* the whole round is over, so reset the flag */
> else if ((pri->info_seq_num>rs->r_start_seq_nr+1)&&(rs->r_under!=FALSE)){
> rs->r_under = FALSE;
> }
>
> /* number of times where sequence number was not ok */
> if ( rs->r_seq_num+1 == pri->info_seq_num)
> rs->r_seq_num = pri->info_seq_num;
> else if ( (rs->r_seq_num == 65535) && (pri->info_seq_num == 0) )
> rs->r_seq_num = pri->info_seq_num;
> /* lost packets */
> else if (rs->r_seq_num+1 < pri->info_seq_num) {
> rs->r_seq_num = pri->info_seq_num;
> rs->r_sequence++;
> }
> /* late or duplicated */
> else if (rs->r_seq_num+1 > pri->info_seq_num)
> rs->r_sequence++;
>
> rs->r_stop_seq_nr = pri->info_seq_num;
> rs->r_time = n_time;
> rs->r_jitter = n_jitter;
> rs->r_timestamp = pri->info_timestamp;
> rs->r_total_nr++;
>
> /* save the voice information */
> /* if there was already an error, we quit */
> if (rs->r_saved == FALSE)
> return 0;
> /* if the captured length and packet length aren't equal, we quit */
> if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_LENGTH;
> return 0;
> }
> /* if padding bit is set, we don't do it yet */
> if (pri->info_padding_set != FALSE) {
> rs->r_saved = FALSE;
> rs->r_error_type = PADDING_SET;
> return 0;
> }
> /* is it the ulaw? */
> if (pri->info_payload_type == 0) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len-12 ); i++, data++) {
> tmp = (gint16 )ulaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> return 0;
> }
> /* alaw? */
> else if (pri->info_payload_type == 8) {
> data = cfile.pd + (pinfo->fd->pkt_len - pri->info_data_len + 12);
> for(i=0; i < (pri->info_data_len -12 ); i++, data++) {
> tmp = (gint16 )alaw2linear((unsigned char)*data);
> fwrite(&tmp, 2, 1, rs->r_fp);
> rs->r_count++;
> }
> return 0;
> }
> /* unsupported codec or other error */
> else {
> rs->r_saved = FALSE;
> rs->r_error_type = WRONG_CODEC;
> return 0;
> }
> }
>
> return 0;
> }
>
> /* XXX just copied from gtk/rpc_stat.c */
> void protect_thread_critical_region(void);
> void unprotect_thread_critical_region(void);
>
>
> /* here we close the rtp analysis dialog window and remove the tap
listener */
> static void rtp_destroy_cb(GtkWidget *win _U_, gpointer data _U_)
> {
>   info_stat *rs=(info_stat *)data;
>
>   protect_thread_critical_region();
>   remove_tap_listener(rs);
>   unprotect_thread_critical_region();
>
>   /* xxx is this enough? */
>   g_free(rs->ssrc_tmp);
>   g_free(rs);
>
>   if (rs->f_fp != NULL)
> fclose(rs->f_fp);
>   if (rs->r_fp != NULL)
> fclose(rs->r_fp);
>   remove(f_tempname);
>   remove(r_tempname);
>
>   /* is there a save window open */
>   if (save_w != NULL)
> gtk_widget_destroy(save_w);
>
>   /* Is there a save voice window open? */
>   if (voice_w != NULL)
> gtk_widget_destroy(voice_w);
>
>   /* Note that we no longer have a "RTP Analyse" dialog box. */
>   rtp_w = NULL;
> }
>
> /* when the close button in rtp window was clicked */
> /* it seems to me that rtp_destroy_cb is automatically called, so we don't
>  * need to do the g_free... and rtp_w = NULL ... */
> static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w)
> {
>     gtk_grab_remove(GTK_WIDGET(parent_w));
>     gtk_widget_destroy(GTK_WIDGET(parent_w));
> }
>
> /* we search the rtp.ssrc node here (thanks to Guy Harris - code here is
magic for me */
> static guint32 process_node(proto_item *ptree_node, header_field_info
*hfinformation)
> {
>   field_info            *finfo;
>   proto_item            *proto_sibling_node;
>   header_field_info     *hfssrc;
>   guint32 ssrc;
>
>   finfo = PITEM_FINFO(ptree_node);
>
>   if (hfinformation==(finfo->hfinfo)) {
> hfssrc = proto_registrar_get_byname("rtp.ssrc");
> if (hfssrc == NULL)
> return 0;
> for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL;
> ptree_node=g_node_next_sibling(ptree_node)) {
> finfo=PITEM_FINFO(ptree_node);
> if (hfssrc==finfo->hfinfo) {
> ssrc = fvalue_get_integer(finfo->value);
> return ssrc;
> }
> }
>   }
>
>   proto_sibling_node = g_node_next_sibling(ptree_node);
>
>   if (proto_sibling_node) {
> ssrc = process_node(proto_sibling_node, hfinformation);
> return ssrc;
>   }
>   else
> return 0;
> }
>
> /* here we search the rtp protocol */
> static guint32 process_tree(proto_tree *protocol_tree)
> {
>   proto_item      *ptree_node;
>   header_field_info     *hfinformation;
>
>   hfinformation = proto_registrar_get_byname("rtp");
>   if (hfinformation == NULL)
> return 0;
>
>   ptree_node = g_node_first_child(protocol_tree);
>   if (!ptree_node)
> return 0;
>
>   return process_node(ptree_node, hfinformation);
> }
>
> /* when we want to update the information */
> static void refresh_cb(GtkWidget *w _U_, void *pri)
> {
>   info_stat *rs=pri;
>
>   gtk_clist_clear(GTK_CLIST(clist));
>   gtk_clist_clear(GTK_CLIST(clist_r));
>   redissect_packets(&cfile);
>   draw_stat(rs);
> }
>
> /* when the user clicks the close button */
> static void voice_close_cb(GtkWidget *close_bt _U_, gpointer parent_w _U_)
> {
>   gtk_widget_destroy(GTK_WIDGET(parent_w));
> }
>
> static void voice_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
> {
>   if (save_w != NULL)
>   gtk_widget_destroy(GTK_WIDGET(save_w));
>
>   /* Note that we no longer have a Save voice info dialog box. */
>   voice_w = NULL;
> }
>
>
> /* the user wants to save in a file */
> /* XXX support for different formats is currently commented out */
> static void ok_button_cb(GtkWidget *ok_bt, gpointer data)
> {
>   info_stat *rs=(info_stat *)data;
>   GtkWidget *entry, *rev, *forw, *both;
>   /*GtkWidget *wav, *au, *sw;*/
>   gchar *g_dest;
>   gint channels /*, format*/;
>
>   entry = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "file_entry");
>   /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
>   au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
>   sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
>   rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
>   forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
>   both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
>   g_dest = gtk_entry_get_text(GTK_ENTRY(entry));
>
>   /* XXX user clicks the ok button, but we know we can't save the voice
info because f.e.
>    * we don't support that codec. So we pop up a warning. Maybe it would
be better to
>    * disable the ok button or disable the buttons for direction if only
one is not ok. The
>    * problem is if we open the save voice dialog and then click the
refresh button and maybe
>    * the state changes, so we can't save anymore. In this case we should
be able to update
>    * the buttons. For now it is easier if we put the warning when the ok
button is pressed.
>    */
>
>   /* we can not save in both dirctions */
>   if ((rs->f_saved == FALSE) && (rs->r_saved == FALSE) &&
(GTK_TOGGLE_BUTTON (both)->active)) {
> /* there are many combinations here, we just exit when first matches */
> if ((rs->f_error_type == WRONG_CODEC) || (rs->r_error_type ==
WRONG_CODEC))
> simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save in a file: Unsupported codec!");
> else if ((rs->f_error_type == WRONG_LENGTH) || (rs->r_error_type ==
WRONG_LENGTH))
> simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save in a file: Wrong length of captured packets!");
> else if ((rs->f_error_type == PADDING_SET) || (rs->r_error_type ==
PADDING_SET))
> simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save in a file: RTP data with padding!");
> else
> simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save in a file: File I/O problem!");
> return;
>   }
>   /* we can not save forward direction */
>   else if ((rs->f_saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active)
||
> (GTK_TOGGLE_BUTTON (both)->active))) {
> if (rs->f_error_type == WRONG_CODEC)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save forward direction in a file: Unsupported codec!");
>         else if (rs->f_error_type == WRONG_LENGTH)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save forward direction in a file: Wrong length of
captured packets!");
>         else if (rs->f_error_type == PADDING_SET)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save forward direction in a file: RTP data with padding!");
>         else
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
> "Can't save forward direction in a file: File I/O problem!");
> return;
>   }
>   /* we can not save reversed direction */
>   else if ((rs->r_saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
> (GTK_TOGGLE_BUTTON (both)->active))) {
> if (rs->r_error_type == WRONG_CODEC)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save reversed direction in a file: Unsupported
codec!");
>         else if (rs->r_error_type == WRONG_LENGTH)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save reversed direction in a file: Wrong length of
captured packets!");
>         else if (rs->r_error_type == PADDING_SET)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save reversed direction in a file: RTP data with
padding!");
>         else if (rs->r_error_type == NO_DATA)
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save reversed direction in a file: No RTP data!");
> else
>                 simple_dialog(ESD_TYPE_CRIT, NULL,
>                 "Can't save reversed direction in a file: File I/O
problem!");
>         return;
>   }
>
>   /* if the file text entry is empty */
>   if (!g_dest[0]) {
> simple_dialog(ESD_TYPE_CRIT, NULL, "Saving to file, but no file
specified.");
> return;
>   }
>
>   /*if (GTK_TOGGLE_BUTTON (wav)->active)
> format = 1;
>   else if (GTK_TOGGLE_BUTTON (au)->active)
> format = 2;
>   else if (GTK_TOGGLE_BUTTON (sw)->active)
> format = 3;*/
>
>   if (GTK_TOGGLE_BUTTON (rev)->active)
> channels = 2;
>   else if (GTK_TOGGLE_BUTTON (both)->active)
> channels = 3;
>   else
> channels = 1;
>
>   if(!copy_file(g_dest, channels/*, format*/, rs)) {
> simple_dialog(ESD_TYPE_CRIT, NULL, "An error occured while saving voice in
a file!");
> return;
>   }
>
>   if (save_w != NULL)
>   gtk_widget_destroy(GTK_WIDGET(save_w));
>
>   /* XXX I get GTK warning (sometimes?)!!! */
>   gtk_widget_destroy(GTK_WIDGET(voice_w));
> }
>
> static void save_ok_button_cb(GtkWidget *w _U_, gpointer data)
> {
>   gchar     *f_name;
>
>   f_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION
(data)));
>
>   /* Perhaps the user specified a directory instead of a file.
>      Check whether they did. */
>   if (test_for_directory(f_name) == EISDIR) {
> /* It's a directory - set the file selection box to display it. */
> set_last_open_dir(f_name);
> g_free(f_name);
> gtk_file_selection_set_filename(GTK_FILE_SELECTION(data), last_open_dir);
> return;
>   }
>
>   gtk_entry_set_text(GTK_ENTRY(OBJECT_GET_DATA(data, "file_entry")),
f_name);
>   gtk_widget_destroy(GTK_WIDGET(data));
>
>   g_free(f_name);
> }
>
> static void save_cancel_button_cb(GtkWidget *w _U_, gpointer data)
> {
>   gtk_widget_destroy(GTK_WIDGET(data));
> }
>
> static void save_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
> {
>   /* Note that we no longer have a Save voice info dialog box. */
>   save_w = NULL;
> }
>
> /* file selection window */
> static void file_selection_cb(GtkWidget *w _U_, gpointer file_entry)
> {
>  if (save_w != NULL) {
> /* Yes.  Just re-activate that dialog box. */
> reactivate_window(save_w);
> return;
>   }
>
>   save_w = gtk_file_selection_new ("Ethereal: Save to File");
>   gtk_signal_connect(GTK_OBJECT(save_w), "destroy",
GTK_SIGNAL_FUNC(save_destroy_cb), NULL);
>
>   OBJECT_SET_DATA(save_w, "file_entry", file_entry);
>
>
gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(save_w)->cancel_button),
"clicked",
> GTK_SIGNAL_FUNC(save_cancel_button_cb), GTK_OBJECT(save_w));
>
>   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(save_w)->ok_button),
"clicked",
> GTK_SIGNAL_FUNC(save_ok_button_cb), GTK_OBJECT(save_w));
>
>   gtk_widget_show(save_w);
> }
>
> /* when the user wants to save the voice information in a file */
> /* XXX support for different formats is currently commented out */
> static void voice_cb(GtkWidget *w _U_, gpointer data)
> {
>   info_stat *rs=(info_stat *)data;
>
>   GtkWidget *vertb;
>   GtkWidget *table1;
>   GtkWidget *label_format;
>   GtkWidget *channels_label;
>   /*GSList *format_group = NULL;*/
>   GSList *channels_group = NULL;
>   GtkWidget *forward_rb;
>   GtkWidget *reversed_rb;
>   GtkWidget *both_rb;
>   /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
>   GtkWidget *hbox3;
>   GtkWidget *file_button;
>   GtkWidget *file_entry;
>   GtkWidget *hbox4;
>   GtkWidget *ok_bt;
>   GtkWidget *cancel_bt;
>
>   /* if we can't save in a file: wrong codec, cut packets or other errors
*/
>   /* shold the error arise here or later when you click ok button ?
>    * if we do it here, then we must disable the refresh button, so we
don't do it here */
>
>   if (voice_w != NULL) {
> /* There's already a Save voice info dialog box; reactivate it. */
> reactivate_window(voice_w);
> return;
>   }
>
>   voice_w = dlg_window_new("Ethereal: Save voice data");
>   gtk_signal_connect(GTK_OBJECT(voice_w), "destroy",
>        GTK_SIGNAL_FUNC(voice_destroy_cb), NULL);
>
>   /* Container for each row of widgets */
>   vertb = gtk_vbox_new(FALSE, 0);
>   gtk_container_border_width(GTK_CONTAINER(vertb), 5);
>   gtk_container_add(GTK_CONTAINER(voice_w), vertb);
>   gtk_widget_show (vertb);
>
>   table1 = gtk_table_new (2, 4, FALSE);
>   gtk_widget_show (table1);
>   gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
>   gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
>   gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
>
>   label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono)
");
>   gtk_widget_show (label_format);
>   gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
>                     (GtkAttachOptions) (GTK_FILL),
>                     (GtkAttachOptions) (0), 0, 0);
>
>   /* we support .au - ulaw*/
> /*  wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
>   format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
>   gtk_widget_show (wav_rb);
>   gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
>                     (GtkAttachOptions) (GTK_FILL),
>                     (GtkAttachOptions) (0), 0, 0);
>
>   sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit
");
>   format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
>   gtk_widget_show (sw_rb);
>   gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
>                     (GtkAttachOptions) (GTK_FILL),
>                     (GtkAttachOptions) (0), 0, 0);
>   au_rb = gtk_radio_button_new_with_label (format_group, ".au");
>   format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
>   gtk_widget_show (au_rb);
>   gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
>                     (GtkAttachOptions) (GTK_FILL),
>                     (GtkAttachOptions) (0), 0, 0);
>  */
>
>   channels_label = gtk_label_new ("Channels:");
>   gtk_widget_show (channels_label);
>   gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
> (GtkAttachOptions) (GTK_FILL),
> (GtkAttachOptions) (0), 0, 0);
>   gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
>
>   forward_rb = gtk_radio_button_new_with_label (channels_group, "forward
");
>   channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
>   gtk_widget_show (forward_rb);
>   gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
> (GtkAttachOptions) (GTK_FILL),
> (GtkAttachOptions) (0), 0, 0);
>
>   reversed_rb = gtk_radio_button_new_with_label (channels_group,
"reversed");
>   channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON
(reversed_rb));
>   gtk_widget_show (reversed_rb);
>   gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
> (GtkAttachOptions) (GTK_FILL),
> (GtkAttachOptions) (0), 0, 0);
>
>   both_rb = gtk_radio_button_new_with_label (channels_group, "both");
>   channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
>   gtk_widget_show (both_rb);
>   gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
> (GtkAttachOptions) (GTK_FILL),
> (GtkAttachOptions) (0), 0, 0);
>
>   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
>
>   /* if one direction is nok we don't allow saving
>   XXX this is not ok since the user can click the refresh button and cause
changes
>   but we can not update this window. So we move all the decision on the
time the ok
>   button is clicked
>   if (rs->f_saved == FALSE) {
> gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
> gtk_widget_set_sensitive(forward_rb, FALSE);
> gtk_widget_set_sensitive(both_rb, FALSE);
>   }
>   else if (rs->r_saved == FALSE) {
> gtk_widget_set_sensitive(reversed_rb, FALSE);
> gtk_widget_set_sensitive(both_rb, FALSE);
>   }
>   */
>   hbox3 = gtk_hbox_new (FALSE, 0);
>   gtk_widget_show (hbox3);
>   gtk_box_pack_start (GTK_BOX (vertb), hbox3, FALSE, FALSE, 0);
>   gtk_container_set_border_width (GTK_CONTAINER (hbox3), 10);
>
>   file_button = gtk_button_new_with_label ("File:");
>   gtk_widget_show (file_button);
>   gtk_box_pack_start (GTK_BOX (hbox3), file_button, FALSE, FALSE, 0);
>
>   file_entry = gtk_entry_new ();
>   gtk_widget_show (file_entry);
>   gtk_box_pack_start (GTK_BOX (hbox3), file_entry, TRUE, TRUE, 10);
>
>   hbox4 = gtk_hbox_new (FALSE, 0);
>   gtk_widget_show (hbox4);
>   gtk_box_pack_start (GTK_BOX (vertb), hbox4, FALSE, FALSE, 0);
>   gtk_container_set_border_width (GTK_CONTAINER (hbox4), 10);
>
>   ok_bt = gtk_button_new_with_label ("OK");
>   gtk_widget_show (ok_bt);
>   gtk_box_pack_start (GTK_BOX (hbox4), ok_bt, TRUE, FALSE, 0);
>   gtk_widget_set_usize (ok_bt, 60, -2);
>   OBJECT_SET_DATA(ok_bt, "file_entry", file_entry);
>   /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
>   OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
>   OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
>   OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
>   OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
>   OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
>
>   cancel_bt = gtk_button_new_with_label (" Cancel ");
>   gtk_widget_show (cancel_bt);
>   gtk_box_pack_start (GTK_BOX (hbox4), cancel_bt, TRUE, FALSE, 0);
>   gtk_widget_set_usize (cancel_bt, 60, -2);
>
>   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
> GTK_SIGNAL_FUNC(voice_close_cb), GTK_OBJECT(voice_w));
>
>   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
> GTK_SIGNAL_FUNC(ok_button_cb), rs);
>
>   gtk_signal_connect(GTK_OBJECT(file_button), "clicked",
> GTK_SIGNAL_FUNC(file_selection_cb), file_entry);
>
>   gtk_widget_show(voice_w);
> }
>
> /* all the graphics on the window is done here */
> static void add_rtp_notebook(void *pri)
> {
>   info_stat *rs=pri;
>
>   GtkWidget *notebook, *page, *page_r, *label, *label1, *label2, *label3;
>   GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text,
*label4, *page_help*/;
>   GtkWidget *box4, *voice_bt, *refresh_bt, *close_bn;
>
>   gchar *titles[6] =  {"Packet nr.", "Sequence",  "Delay (s)", "Jitter
(s)", "Marker", "Status"};
>   gchar label_forward[150];
>   gchar label_reverse[150];
>
>   /* XXX is it ok to use %lu for guint32? The compiler is not satisfied,
but it works
>    * with %d the compiler is satisfied, but it doesn't work */
>   g_snprintf(label_forward, 149,
> "Analysing connection from  %s port %lu  to  %s port %lu   SSRC = %lu\n",
> rs->source, rs->srcport, rs->destination, rs->dstport, rs->ssrc_forward);
>   g_snprintf(label_reverse, 149,
> "Analysing connection from  %s port %lu  to  %s port %lu   SSRC = %lu\n",
> rs->destination, rs->dstport, rs->source, rs->srcport, rs->ssrc_reversed);
>
>   gtk_widget_destroy(main_vb);
>   main_vb = gtk_vbox_new(FALSE, 3);
>   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
>   gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
>   gtk_widget_show(main_vb);
>
>   /* Start a nootbook for flipping between sets of changes */
>   notebook = gtk_notebook_new();
>   gtk_container_add(GTK_CONTAINER(main_vb), notebook);
>   gtk_object_set_data(GTK_OBJECT(rtp_w), "notebook", notebook);
>
>   /* page for forward connection */
>   page = gtk_vbox_new(FALSE, 5);
>   gtk_container_set_border_width(GTK_CONTAINER(page), 20);
>
>   /* scrolled window */
>   scrolled_window = gtk_scrolled_window_new(NULL, NULL);
>   gtk_widget_set_usize(scrolled_window, 600, 200);
>   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
> GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
>
>   /* direction label */
>   label1 = gtk_label_new(label_forward);
>   gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
>
>   /* place for some statistics */
>   max = gtk_label_new("\n\n");
>   gtk_box_pack_end(GTK_BOX(page), max, FALSE, FALSE, 5);
>
>   /* clist for the information */
>   clist = gtk_clist_new_with_titles(6, titles);
>   gtk_widget_show(clist);
>   gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
>   gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
>
>   /* and the label */
>   label = gtk_label_new("     Forward Direction     ");
>   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
>
>   /* column width and justification */
>   gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist), 1, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist), 2, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist), 3, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist), 4, 40);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 0,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 1,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 2,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 3,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 4,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist), 5,
GTK_JUSTIFY_CENTER);
>
>   /* same page for reversed connection */
>   page_r = gtk_vbox_new(FALSE, 5);
>   gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
>   scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
>   gtk_widget_set_usize(scrolled_window_r, 600, 200);
>   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
> GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
>   label3 = gtk_label_new(label_reverse);
>   gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
>   max_r = gtk_label_new("\n\n");
>   gtk_box_pack_end(GTK_BOX(page_r), max_r, FALSE, FALSE, 5);
>   clist_r = gtk_clist_new_with_titles(6, titles);
>   gtk_widget_show(clist_r);
>   gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_r);
>   gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
>   label2 = gtk_label_new("     Reversed Direction     ");
>   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
>
>   gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist_r), 1, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist_r), 2, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist_r), 3, 80);
>   gtk_clist_set_column_width(GTK_CLIST(clist_r), 4, 40);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 0,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 1,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 2,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 3,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 4,
GTK_JUSTIFY_CENTER);
>   gtk_clist_set_column_justification(GTK_CLIST(clist_r), 5,
GTK_JUSTIFY_CENTER);
>
>   /* page for help&about or future
>   page_help = gtk_hbox_new(FALSE, 5);
>   label4 = gtk_label_new("     Future    ");
>   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
>   frame = gtk_frame_new("");
>   text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter
distribution,...");
>   gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
>   gtk_container_add(GTK_CONTAINER(frame), text);
>   gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
>   gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
> */
>   /* show all notebooks */
>   gtk_widget_show_all(notebook);
>
>   /* and the buttons */
>   box4 = gtk_hbutton_box_new();
>   gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, TRUE, 0);
>   gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
>   gtk_button_box_set_layout(GTK_BUTTON_BOX(box4), GTK_BUTTONBOX_SPREAD);
>   gtk_widget_show(box4);
>
>   voice_bt = gtk_button_new_with_label("Save voice data");
>   gtk_container_add(GTK_CONTAINER(box4), voice_bt);
>   gtk_widget_show(voice_bt);
>   gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked",
> GTK_SIGNAL_FUNC(voice_cb), rs);
>
>   refresh_bt = gtk_button_new_with_label("Refresh");
>   gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
>   gtk_widget_show(refresh_bt);
>   gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked",
> GTK_SIGNAL_FUNC(refresh_cb), rs);
>
>   close_bn = gtk_button_new_with_label("Close");
>   gtk_container_add(GTK_CONTAINER(box4), close_bn);
>   gtk_widget_show(close_bn);
>   gtk_signal_connect(GTK_OBJECT(close_bn), "clicked",
> GTK_SIGNAL_FUNC(rtp_destroy), GTK_OBJECT(rtp_w));
>
>   redissect_packets(&cfile);
>
>   draw_stat(rs);
> }
>
>
> /* when we click on the selected row it copies that ssrc value into
ssrc_reversed */
> static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column,
> GdkEventButton *event _U_, gpointer data)
> {
>   info_stat *rs=(info_stat *)data;
>   gchar *text;
>
>   gtk_clist_get_text(GTK_CLIST(clist_r), row, column, &text);
>   /* XXX is this strtoul portable for guint32? */
>   rs->ssrc_reversed = strtoul(text, (char **)NULL, 10);
>   return;
> }
>
> /* when we click apply button in ssrc reversed dialog */
> static void apply_selected_ssrc(GtkWidget *w _U_, gpointer data)
> {
>   info_stat *rs=(info_stat *)data;
>   add_rtp_notebook(rs);
> }
>
> /* this function goes through all the packets that have the same ip and
port combination
>  * (only inversed) as the forward direction (XXX what if the reversed
direction doesn't use
>  * the same ports???) and looks for different SSRC values. This can happen
if you capture
>  * two RTP conversations one after another from the same pair of phones
(PC's).
>  * Both have same IP's and can also have same port numbers, so they
(should) differ only
>  * in SSRC values. In such case we get a list of ssrc values and we have
to choose the right
>  * one from the list. If there is only one or none, we do it automatically
*/
> static void get_reversed_ssrc(void *prs)
> {
> info_stat *ri = prs;
> GtkWidget *scroll_r, *clist_r, *ok_bt, *label, *label2, *label1,
*main_hbnbox;
> gchar temp[150];
> guint i;
>
> switch(ri->reversed_ip_and_port)
> {
> /* in case we haven't found any reversed ssrc */
> /* XXX in this case we could look for the inversed IP only */
> case 0: {
> ri->ssrc_reversed = 0;
> ri->search_ssrc = FALSE;
> add_rtp_notebook(ri);
> return;
> }
> /* in case we found exactly one matching ssrc for reversed connection */
> case 1: {
> ri->ssrc_reversed = ri->ssrc_tmp[0];
> ri->search_ssrc = FALSE;
> add_rtp_notebook(ri);
> return;
> }
> /* there is more then one matching ssrc, so we have to choose between them
*/
> default: {
> ri->search_ssrc = FALSE;
> /* let's draw the window */
> label = gtk_label_new("Found more SSRC values for the reversed\n"
> "connection with following parameters:\n");
> g_snprintf(temp, 149, "Source %s port %lu Destination %s port %lu",
> ri->destination, ri->dstport, ri->source, ri->srcport);
> label2 = gtk_label_new(temp);
> gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE, FALSE, 0);
> gtk_box_pack_start(GTK_BOX(main_vb), label2, FALSE, FALSE, 0);
> scroll_r = gtk_scrolled_window_new(NULL, NULL);
> gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_r),
> GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
> clist_r = gtk_clist_new(1);
> gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
> gtk_container_add(GTK_CONTAINER(scroll_r), clist_r);
> gtk_box_pack_start(GTK_BOX(main_vb), scroll_r, TRUE, TRUE, 0);
> label1 = gtk_label_new("Select one value and click apply");
> gtk_box_pack_start(GTK_BOX(main_vb), label1, FALSE, FALSE, 0);
>
> main_hbnbox = gtk_hbutton_box_new();
> gtk_box_pack_start(GTK_BOX(main_vb), main_hbnbox, FALSE, TRUE, 0);
> gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
> gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox),
> GTK_BUTTONBOX_SPREAD);
> gtk_widget_show(main_hbnbox);
>
> ok_bt = gtk_button_new_with_label("Apply");
> gtk_container_add(GTK_CONTAINER(main_hbnbox), ok_bt);
> gtk_signal_connect(GTK_OBJECT(clist_r), "select_row",
> GTK_SIGNAL_FUNC(get_selected_ssrc), ri);
> gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
> GTK_SIGNAL_FUNC(apply_selected_ssrc), ri);
>
> /* add all the ssrc values in the clist */
> /* XXX I'm sure the tmp variable could be avoided here
> * i tried to assign guint32 from ri->ssrc_tmp somehow to gchar **text
> * but gave up. So if you can do this, just go ahead */
> for (i=0; i < ri->reversed_ip_and_port; i++) {
> gchar *text[0];
> gchar tmp[20];
> g_snprintf(tmp, 20, "%lu", ri->ssrc_tmp[i]);
> text[0] = (gchar *)&tmp;
> gtk_clist_append(GTK_CLIST(clist_r), text);
> }
>
> gtk_clist_select_row(GTK_CLIST(clist_r), 0, 0);
>
> gtk_widget_show(label);
> gtk_widget_show(label1);
> gtk_widget_show(label2);
> gtk_widget_show(ok_bt);
> gtk_widget_show(clist_r);
> gtk_widget_show(scroll_r);
> }
> }
> }
>
> /* when the user clicks the RTP dialog button */
> static void rtp_analyse_cb(GtkWidget *w _U_, gpointer data _U_)
> {
>   info_stat *rs;
>   gchar filter_text[]="rtp";
>   dfilter_t *sfcode;
>   capture_file *cf;
>   epan_dissect_t *edt;
>   gint err;
>   gboolean frame_matched;
>   frame_data *fdata;
>
>   /* There's already a "Display Options" dialog box; reactivate it. */
>   if (rtp_w != NULL) {
> reactivate_window(rtp_w);
> return;
>   }
>
>   /* Try to compile the filter. */
>   if (!dfilter_compile(filter_text, &sfcode)) {
> simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
> return;
>   }
>   /* we load the current file into cf variable */
>   cf = &cfile;
>   fdata = cf->current_frame;
>
>   /* we are on the selected frame now */
>   if (fdata == NULL)
> return; /* if we exit here it's an error */
>
>   /* XXX instead of looking for RTP protocol like this, we could do the
process_node() staff */
>   /* dissect the current frame */
>   wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd,
fdata->cap_len, &err);
>   edt = epan_dissect_new(TRUE, FALSE);
>   epan_dissect_prime_dfilter(edt, sfcode);
>   epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
>   frame_matched = dfilter_apply_edt(sfcode, edt);
>
>   /* if it is not an rtp frame, exit */
>   frame_matched = dfilter_apply_edt(sfcode, edt);
>   if (frame_matched != 1) {
> epan_dissect_free(edt);
> simple_dialog(ESD_TYPE_CRIT, NULL, "You didn't choose a RTP packet!");
> return;
> }
>
>   /* in rs we put all the info */
>   rs=g_malloc(sizeof(info_stat));
>
>   /* ok, it is a RTP frame, so let's get the ip and port values */
>   rs->srcport = edt->pi.srcport;
>   rs->dstport = edt->pi.destport;
>   strncpy(rs->source, ip_to_str(edt->pi.src.data), 16);
>   strncpy(rs->destination, ip_to_str(edt->pi.dst.data), 16);
>
>   /* now we need the SSRC value of the current frame */
>   rs->ssrc_forward = process_tree(edt->tree);
>   if (rs->ssrc_forward == 0) {
> simple_dialog(ESD_TYPE_CRIT, NULL, "SSRC value couldn't be found!");
> return;
>   }
>
>   /* now we have all the information about the forwarding connection
>    * we need to go through all the packets and search for reversed
connection
>    */
>   rs->search_ssrc = TRUE;
>   rs->ssrc_reversed = 0;
>   rs->reversed_ip = 0;
>   rs->reversed_ip_and_port = 0;
>   rs->ssrc_tmp = NULL;
>
> /* XXX compiler warning:passing arg 5 of `register_tap_listener' from
incompatible pointer type */
>   if(register_tap_listener("rtp", rs, NULL, rtp_reset, rtp_packet,
rtp_draw)){
> printf("ethereal: rtp_init() failed to attach the tap.\n");
> /* XXX is this enough or do I have to free anything else? */
> g_free(rs);
> exit(1);
>   }
>
>   /* let's draw the window */
>   rtp_w = dlg_window_new("Ethereal: RTP Analyse");
>   gtk_window_set_position (GTK_WINDOW (rtp_w), GTK_WIN_POS_CENTER);
>   gtk_signal_connect(GTK_OBJECT(rtp_w), "destroy",
> GTK_SIGNAL_FUNC(rtp_destroy_cb), rs);
>
>   /* Container for each row of widgets */
>   main_vb = gtk_vbox_new(FALSE, 3);
>   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
>   gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
>   gtk_widget_show(main_vb);
>
>   /* file names for storing sound data */
>   tmpnam(f_tempname);
>   tmpnam(r_tempname);
>   rs->f_fp = NULL;
>   rs->r_fp = NULL;
>
>   redissect_packets(cf);
>
>   /* so how many reversed connection we have ? */
>   get_reversed_ssrc(rs);
>
>   /* and finally display this window */
>   gtk_widget_show(rtp_w);
> }
>
> /* XXX compiler warning:passing arg 2 of `register_ethereal_tap' from
incompatible pointer type */
> void
> register_tap_listener_gtkrtp(void)
> {
> register_ethereal_tap("rtp", rtp_analyse_cb, NULL, NULL);
> }
>
>
> /* here we save it into a file that user specified */
> /* XXX what about endians here? could go something wrong? */
> static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/
void *data)
> {
> info_stat *rs=(info_stat *)data;
> int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
> gint16 f_pd;
> gint16 r_pd;
> gchar pd[1];
> guint32 f_write_silence = 0;
> guint32 r_write_silence = 0;
> progdlg_t *progbar;
> guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
> gboolean stop_flag = FALSE;
>
> forw_fd = open(f_tempname, O_RDONLY | 0);
> if (forw_fd < 0)
> return FALSE;
> rev_fd = open(r_tempname, O_RDONLY | 0);
> if (rev_fd < 0) {
> close(forw_fd);
> return FALSE;
> }
>
> /* open file for saving */
> to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | 0, 0644);
> if (to_fd < 0) {
> close(forw_fd);
> close(rev_fd);
> return FALSE;
> }
>
> progbar = create_progress_dlg("Saving voice in a file", dest, "Stop",
&stop_flag);
>
> /* First we write the .au header. XXX Hope this is endian independant */
> /* the magic word 0x2e736e64 == .snd */
> *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
> *pd = (unsigned char)0x73; write(to_fd, pd, 1);
> *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
> *pd = (unsigned char)0x64; write(to_fd, pd, 1);
> /* header offset == 24 bytes */
> *pd = (unsigned char)0x00; write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> *pd = (unsigned char)0x18; write(to_fd, pd, 1);
> /* total length, it is permited to set this to 0xffffffff */
> *pd = (unsigned char)0xff; write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> /* encoding format == 8 bit ulaw */
> *pd = (unsigned char)0x00; write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> *pd = (unsigned char)0x01; write(to_fd, pd, 1);
> /* sample rate == 8000 Hz */
> *pd = (unsigned char)0x00; write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
> *pd = (unsigned char)0x40; write(to_fd, pd, 1);
> /* channels == 1 */
> *pd = (unsigned char)0x00; write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> write(to_fd, pd, 1);
> *pd = (unsigned char)0x01; write(to_fd, pd, 1);
>
> switch (channels) {
> /* only forward direction */
> case 1: {
> progbar_count = rs->f_count;
> progbar_quantum = rs->f_count/100;
> while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
> if(stop_flag)
> break;
> if((count > progbar_nextstep) && (count <= progbar_count)) {
> update_progress_dlg(progbar,
> (gfloat) count/progbar_count, "Saving");
> progbar_nextstep = progbar_nextstep + progbar_quantum;
> }
> count++;
> *pd = (unsigned char)linear2ulaw(f_pd);
> fwritten = write(to_fd, pd, 1);
> if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
> close(forw_fd);
> close(rev_fd);
> close(to_fd);
> destroy_progress_dlg(progbar);
> return FALSE;
> }
> }
> break;
> }
> /* only reversed direction */
> case 2: {
> progbar_count = rs->r_count;
> progbar_quantum = rs->r_count/100;
> while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
> if(stop_flag)
> break;
> if((count > progbar_nextstep) && (count <= progbar_count)) {
> update_progress_dlg(progbar,
> (gfloat) count/progbar_count, "Saving");
> progbar_nextstep = progbar_nextstep + progbar_quantum;
> }
> count++;
> *pd = (unsigned char)linear2ulaw(r_pd);
> rwritten = write(to_fd, pd, 1);
> if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
> close(forw_fd);
> close(rev_fd);
> close(to_fd);
> destroy_progress_dlg(progbar);
> return FALSE;
> }
> }
> break;
> }
> /* both directions */
> default: {
> (rs->f_count > rs->r_count) ? (progbar_count = rs->f_count) :
> (progbar_count = rs->r_count);
> progbar_quantum = progbar_count/100;
> /* since conversation in one way can start later than in the other one,
> * we have to write some silence information for one channel */
> if (rs->f_start_time > rs->r_start_time) {
> f_write_silence = (rs->f_start_time-rs->r_start_time)*8000;
> }
> else if (rs->f_start_time < rs->r_start_time) {
> r_write_silence = (rs->r_start_time-rs->f_start_time)*8000;
> }
> for(;;) {
> if(stop_flag)
> break;
> if((count > progbar_nextstep) && (count <= progbar_count)) {
> update_progress_dlg(progbar,
> (gfloat) count/progbar_count, "Saving");
> progbar_nextstep = progbar_nextstep + progbar_quantum;
> }
> count++;
> if(f_write_silence > 0) {
> rread = read(rev_fd, &r_pd, 2);
> f_pd = 0;
> fread = 1;
> f_write_silence--;
> }
> else if(r_write_silence > 0) {
> fread = read(forw_fd, &f_pd, 2);
> r_pd = 0;
> rread = 1;
> r_write_silence--;
> }
> else {
> fread = read(forw_fd, &f_pd, 2);
> rread = read(rev_fd, &r_pd, 2);
> }
> if ((rread == 0) && (fread == 0))
> break;
> *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
> rwritten = write(to_fd, pd, 1);
> if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
> close(forw_fd);
> close(rev_fd);
> close(to_fd);
> destroy_progress_dlg(progbar);
> return FALSE;
> }
> }
> }
> }
> destroy_progress_dlg(progbar);
> close(forw_fd);
> close(rev_fd);
> close(to_fd);
> return TRUE;
>
> }
>
>


----------------------------------------------------------------------------
----






----------------------------------------------------------------------------
----


> /*
>  * This source code is a product of Sun Microsystems, Inc. and is provided
>  * for unrestricted use.  Users may copy or modify this source code
without
>  * charge.
>  *
>  * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
INCLUDING
>  * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
>  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
>  *
>  * Sun source code is provided with no support and without any obligation
on
>  * the part of Sun Microsystems, Inc. to assist in its use, correction,
>  * modification or enhancement.
>  *
>  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
>  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS
SOFTWARE
>  * OR ANY PART THEREOF.
>  *
>  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
>  * or profits or other special, indirect and consequential damages, even
if
>  * Sun has been advised of the possibility of such damages.
>  *
>  * Sun Microsystems, Inc.
>  * 2550 Garcia Avenue
>  * Mountain View, California  94043
>  */
>
> /*
>  * g711.c
>  *
>  * u-law, A-law and linear PCM conversions.
>  */
> #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
> #define QUANT_MASK (0xf) /* Quantization field mask. */
> #define NSEGS (8) /* Number of A-law segments. */
> #define SEG_SHIFT (4) /* Left shift for segment number. */
> #define SEG_MASK (0x70) /* Segment field mask. */
>
> static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
>     0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
>
> /* copy from CCITT G.711 specifications */
> unsigned char _u2a[128] = { /* u- to A-law conversions */
> 1, 1, 2, 2, 3, 3, 4, 4,
> 5, 5, 6, 6, 7, 7, 8, 8,
> 9, 10, 11, 12, 13, 14, 15, 16,
> 17, 18, 19, 20, 21, 22, 23, 24,
> 25, 27, 29, 31, 33, 34, 35, 36,
> 37, 38, 39, 40, 41, 42, 43, 44,
> 46, 48, 49, 50, 51, 52, 53, 54,
> 55, 56, 57, 58, 59, 60, 61, 62,
> 64, 65, 66, 67, 68, 69, 70, 71,
> 72, 73, 74, 75, 76, 77, 78, 79,
> 81, 82, 83, 84, 85, 86, 87, 88,
> 89, 90, 91, 92, 93, 94, 95, 96,
> 97, 98, 99, 100, 101, 102, 103, 104,
> 105, 106, 107, 108, 109, 110, 111, 112,
> 113, 114, 115, 116, 117, 118, 119, 120,
> 121, 122, 123, 124, 125, 126, 127, 128};
>
> unsigned char _a2u[128] = { /* A- to u-law conversions */
> 1, 3, 5, 7, 9, 11, 13, 15,
> 16, 17, 18, 19, 20, 21, 22, 23,
> 24, 25, 26, 27, 28, 29, 30, 31,
> 32, 32, 33, 33, 34, 34, 35, 35,
> 36, 37, 38, 39, 40, 41, 42, 43,
> 44, 45, 46, 47, 48, 48, 49, 49,
> 50, 51, 52, 53, 54, 55, 56, 57,
> 58, 59, 60, 61, 62, 63, 64, 64,
> 65, 66, 67, 68, 69, 70, 71, 72,
> 73, 74, 75, 76, 77, 78, 79, 79,
> 80, 81, 82, 83, 84, 85, 86, 87,
> 88, 89, 90, 91, 92, 93, 94, 95,
> 96, 97, 98, 99, 100, 101, 102, 103,
> 104, 105, 106, 107, 108, 109, 110, 111,
> 112, 113, 114, 115, 116, 117, 118, 119,
> 120, 121, 122, 123, 124, 125, 126, 127};
>
> static int
> search(
> int val,
> short *table,
> int size)
> {
> int i;
>
> for (i = 0; i < size; i++) {
> if (val <= *table++)
> return (i);
> }
> return (size);
> }
>
> /*
>  * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
>  *
>  * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
>  *
>  * Linear Input Code Compressed Code
>  * ------------------------ ---------------
>  * 0000000wxyza 000wxyz
>  * 0000001wxyza 001wxyz
>  * 000001wxyzab 010wxyz
>  * 00001wxyzabc 011wxyz
>  * 0001wxyzabcd 100wxyz
>  * 001wxyzabcde 101wxyz
>  * 01wxyzabcdef 110wxyz
>  * 1wxyzabcdefg 111wxyz
>  *
>  * For further information see John C. Bellamy's Digital Telephony, 1982,
>  * John Wiley & Sons, pps 98-111 and 472-476.
>  */
> unsigned char
> linear2alaw(
> int pcm_val) /* 2's complement (16-bit range) */
> {
> int mask;
> int seg;
> unsigned char aval;
> if (pcm_val >= 0) {
> mask = 0xD5; /* sign (7th) bit = 1 */
> } else {
> mask = 0x55; /* sign bit = 0 */
> pcm_val = -pcm_val - 8;
> }
>
> /* Convert the scaled magnitude to segment number. */
> seg = search(pcm_val, seg_end, 8);
>
> /* Combine the sign, segment, and quantization bits. */
>
> if (seg >= 8) /* out of range, return maximum value. */
> return (0x7F ^ mask);
> else {
> aval = seg << SEG_SHIFT;
> if (seg < 2)
> aval |= (pcm_val >> 4) & QUANT_MASK;
> else
> aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
> return (aval ^ mask);
> }
> }
>
> /*
>  * alaw2linear() - Convert an A-law value to 16-bit linear PCM
>  *
>  */
> int
> alaw2linear(
> unsigned char a_val)
> {
> int t;
> int seg;
> //printf(" vrednost a_val %X ", a_val);
> a_val ^= 0x55;
>
> t = (a_val & QUANT_MASK) << 4;
> seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
> switch (seg) {
> case 0:
> t += 8;
> break;
> case 1:
> t += 0x108;
> break;
> default:
> t += 0x108;
> t <<= seg - 1;
> }
> //printf("izracunan int %d in njegov hex %X \n", t,t);
> return ((a_val & SIGN_BIT) ? t : -t);
> }
>
> #define BIAS (0x84) /* Bias for linear code. */
>
> /*
>  * linear2ulaw() - Convert a linear PCM value to u-law
>  *
>  * In order to simplify the encoding process, the original linear
magnitude
>  * is biased by adding 33 which shifts the encoding range from (0 - 8158)
to
>  * (33 - 8191). The result can be seen in the following encoding table:
>  *
>  * Biased Linear Input Code Compressed Code
>  * ------------------------ ---------------
>  * 00000001wxyza 000wxyz
>  * 0000001wxyzab 001wxyz
>  * 000001wxyzabc 010wxyz
>  * 00001wxyzabcd 011wxyz
>  * 0001wxyzabcde 100wxyz
>  * 001wxyzabcdef 101wxyz
>  * 01wxyzabcdefg 110wxyz
>  * 1wxyzabcdefgh 111wxyz
>  *
>  * Each biased linear code has a leading 1 which identifies the segment
>  * number. The value of the segment number is equal to 7 minus the number
>  * of leading 0's. The quantization interval is directly available as the
>  * four bits wxyz.  * The trailing bits (a - h) are ignored.
>  *
>  * Ordinarily the complement of the resulting code word is used for
>  * transmission, and so the code word is complemented before it is
returned.
>  *
>  * For further information see John C. Bellamy's Digital Telephony, 1982,
>  * John Wiley & Sons, pps 98-111 and 472-476.
>  */
> unsigned char
> linear2ulaw(
> int pcm_val) /* 2's complement (16-bit range) */
> {
> int mask;
> int seg;
> unsigned char uval;
>
> /* Get the sign and the magnitude of the value. */
> if (pcm_val < 0) {
> pcm_val = BIAS - pcm_val;
> mask = 0x7F;
> } else {
> pcm_val += BIAS;
> mask = 0xFF;
> }
>
> /* Convert the scaled magnitude to segment number. */
> seg = search(pcm_val, seg_end, 8);
>
> /*
> * Combine the sign, segment, quantization bits;
> * and complement the code word.
> */
> if (seg >= 8) /* out of range, return maximum value. */
> return (0x7F ^ mask);
> else {
> uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
> return (uval ^ mask);
> }
>
> }
>
> /*
>  * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
>  *
>  * First, a biased linear code is derived from the code word. An unbiased
>  * output can then be obtained by subtracting 33 from the biased code.
>  *
>  * Note that this function expects to be passed the complement of the
>  * original code word. This is in keeping with ISDN conventions.
>  */
> int
> ulaw2linear(
> unsigned char u_val)
> {
> int t;
>
> /* Complement to obtain normal u-law value. */
> u_val = ~u_val;
>
> /*
> * Extract and bias the quantization bits. Then
> * shift up by the segment number and subtract out the bias.
> */
> t = ((u_val & QUANT_MASK) << 3) + BIAS;
> t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
>
> return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
> }
>
> /* A-law to u-law conversion */
> unsigned char
> alaw2ulaw(
> unsigned char aval)
> {
> aval &= 0xff;
> return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
>     (0x7F ^ _a2u[aval ^ 0x55]));
> }
>
> /* u-law to A-law conversion */
> unsigned char
> ulaw2alaw(
> unsigned char uval)
> {
> uval &= 0xff;
> return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
>     (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
> }
>


----------------------------------------------------------------------------
----


> diff -r -2 -c ethereal-0.9.8-test/packet-rtp.c ethereal-0.9.8/packet-rtp.c
>
> *** ethereal-0.9.8-test/packet-rtp.c Wed Mar  5 16:08:19 2003
> --- ethereal-0.9.8/packet-rtp.c Thu Aug 29 02:40:00 2002
> ***************
> *** 64,70 ****
>   #include "packet-rtp.h"
>   #include <epan/conversation.h>
> - #include "tap.h"
> -
> - static int rtp_tap = -1;
>
>   /* RTP header fields             */
> --- 64,67 ----
> ***************
> *** 343,348 ****
>   guint32     csrc_item;
>
> - static struct _rtp_info rtp_info;
> -
>   /* Get the fields in the first octet */
>   octet = tvb_get_guint8( tvb, offset );
> --- 340,343 ----
> ***************
> *** 386,399 ****
>   sync_src = tvb_get_ntohl( tvb, offset + 8 );
>
> - /* fill in the rtp_info structure */
> - rtp_info.info_padding_set = padding_set;
> - rtp_info.info_padding_count = 0;
> - rtp_info.info_marker_set = marker_set;
> - rtp_info.info_payload_type = payload_type;
> - rtp_info.info_seq_num = seq_num;
> - rtp_info.info_timestamp = timestamp;
> - rtp_info.info_sync_src = sync_src;
> - rtp_info.info_data_len = tvb_reported_length_remaining( tvb, offset );
> -
>   if ( check_col( pinfo->cinfo, COL_PROTOCOL ) )   {
>   col_set_str( pinfo->cinfo, COL_PROTOCOL, "RTP" );
> --- 381,384 ----
> ***************
> *** 410,413 ****
> --- 395,399 ----
>       marker_set ? ", Mark" : "");
>   }
> +
>   if ( tree ) {
>   ti = proto_tree_add_item( tree, proto_rtp, tvb, offset, -1, FALSE );
> ***************
> *** 560,564 ****
>   }
>   }
> - tap_queue_packet(rtp_tap, pinfo, &rtp_info);
>   }
>
> --- 546,549 ----
> ***************
> *** 776,780 ****
>
>   register_dissector("rtp", dissect_rtp, proto_rtp);
> - rtp_tap = register_tap("rtp");
>
>   #if 0
> --- 761,764 ----
>