summaryrefslogtreecommitdiff
path: root/gtk/rtp_player.c
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2006-08-30 20:50:37 +0000
committerAnders Broman <anders.broman@ericsson.com>2006-08-30 20:50:37 +0000
commit55ddc3dd59102d8ebc30acb9af7bc5c395bcdc1b (patch)
treedb3afc1ce305ca61f8fc8712c35659d2e144b3e4 /gtk/rtp_player.c
parent7577c1a2d88945876b9d3e758b974a2b2fe2d7a0 (diff)
downloadwireshark-55ddc3dd59102d8ebc30acb9af7bc5c395bcdc1b.tar.gz
From Alejandro Vaquero:
- Change the "listen_rtp" to "rtp_player" - Change from a plugin to be part of the core - By default it will not compile with the rtp_player. In order to compile it is necessary to: + For windows: uncomment the line "PORTAUDIO_DIR=$(WIRESHARK_LIBS)\portaudio_v18_1" in config.nmake + For linux: using the "--with-portaudio=yes" svn path=/trunk/; revision=19093
Diffstat (limited to 'gtk/rtp_player.c')
-rw-r--r--gtk/rtp_player.c1830
1 files changed, 1830 insertions, 0 deletions
diff --git a/gtk/rtp_player.c b/gtk/rtp_player.c
new file mode 100644
index 0000000000..bc18cd8ad6
--- /dev/null
+++ b/gtk/rtp_player.c
@@ -0,0 +1,1830 @@
+/* rtp_player.c
+* RTP Player
+*
+* (c) 2006, Alejandro Vaquero <alejandrovaquero@yahoo.com>
+*
+* Here is a summary on how this works:
+* - The VoipCalls will call add_rtp_packet() every time there is an RTP packet
+* - add_rtp_packet() will add the RTP packet in a RTP stream struct, and create the RTP stream if it is the
+* first RTP in the stream.
+* - Each new RTP stream will be added to a list of RTP stream, called rtp_streams_list
+* - When the user clicks "Player" in the VoipCall dialogue, rtp_player_init() is called.
+* - rtp_player_init() create the main dialog, and it calls:
+* + mark_rtp_stream_to_play() to mark the RTP streams that needs to be displayed. These are the RTP stream
+* that match the selected calls in the VoipCall dlg.
+* + decode_rtp_stream() this will decode the RTP packets in each RTP stream, and will also create
+* the RTP channles. An RTP channel is a group of RTP stream that have in common the source and destination
+* IP and UPD ports. The RTP channels is what the user will listen in one of the two Audio channles.
+* The RTP channels are stored in the hash table rtp_channels_hash
+* + add_channel_to_window() will create and add the Audio graphic representation in the main window
+* - When the user click the check box to listen one of the Audio channels, the structure rtp_channels is filled
+* to play one or two RTP channels (a max of two channels can be listened at a given moment)
+*
+*
+*
+*
+*
+* Wireshark - Network traffic analyzer
+* By Gerald Combs <gerald@wireshark.org>
+* Copyright 1999 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.
+*/
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <epan/stats_tree.h>
+#include <epan/addr_resolv.h>
+#include <string.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "globals.h"
+#include "portaudio.h"
+#include "simple_dialog.h"
+#include "gui_utils.h"
+#include "dlg_utils.h"
+#include "compat_macros.h"
+
+#include "graph_analysis.h"
+#include "voip_calls_dlg.h"
+#include "voip_calls.h"
+#include "gtkglobals.h"
+
+
+#include <epan/dissectors/packet-rtp.h>
+
+#include "rtp_player.h"
+#include "gtk/codecs/G711a/G711adecode.h"
+#include "gtk/codecs/G711u/G711udecode.h"
+#include <math.h>
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/*define this symbol to compile with G729 and G723 codecs*/
+/*#define HAVE_G729_G723 1*/
+
+#ifdef HAVE_G729_G723
+#include "gtk/codecs/G729/G729decode.h"
+#include "gtk/codecs/G723/G723decode.h"
+#endif
+
+static gboolean initialized = FALSE;
+
+voip_calls_tapinfo_t *voip_calls = NULL;
+
+/* Hash table with all the RTP streams */
+static GHashTable* rtp_streams_hash = NULL;
+
+/* List with all the RTP streams (this is used to decode them as it is sorted)*/
+static GList* rtp_streams_list = NULL;
+
+/* the window */
+static GtkWidget *rtp_player_dlg_w;
+static GtkWidget *channels_vb;
+static GtkWidget *main_scrolled_window = NULL;
+static GtkWidget *jitter_spinner;
+static GtkWidget *bt_decode;
+static GtkWidget *bt_play;
+static GtkWidget *bt_pause;
+static GtkWidget *bt_stop;
+static GtkWidget *progress_bar;
+static GtkWidget *info_bar;
+static GtkWidget *stat_hbox;
+
+static guint32 total_packets;
+static guint32 total_frames;
+static guint32 progbar_count;
+
+static int new_jitter_buff;
+
+/* a hash table with the RTP streams to play per audio channel */
+static GHashTable *rtp_channels_hash = NULL;
+
+/* Port Audio staff */
+#define SAMPLE_RATE (8000)
+#define NUM_CHANNELS (2)
+
+#define PA_SAMPLE_TYPE paInt16
+typedef gint16 SAMPLE;
+#define SAMPLE_SILENCE (0)
+#define FRAMES_PER_BUFFER (512)
+
+typedef struct _sample_t {
+ SAMPLE val;
+ guint8 status;
+} sample_t;
+
+#define S_NORMAL 0
+#define S_DROP_BY_JITT 1
+#define S_WRONG_SEQ 2
+
+/* Display channels constants */
+#define MULT 80
+#define CHANNEL_WIDTH 500
+#define CHANNEL_HEIGHT 100
+#define MAX_TIME_LABEL 10
+#define HEIGHT_TIME_LABEL 18
+#define MAX_NUM_COL_CONV 10
+
+PortAudioStream *pa_stream;
+
+/* TODO: The RTP Player it is only supported for GTK >=2 */
+#if GTK_MAJOR_VERSION >= 2
+
+/* defines a RTP stream */
+typedef struct _rtp_stream_info {
+ address src_addr;
+ guint16 src_port;
+ address dest_addr;
+ guint16 dest_port;
+ guint32 ssrc;
+ guint32 first_frame_number; /* first RTP frame for the stream */
+ double start_time; /* RTP stream start time in ms */
+ gboolean play;
+ guint16 call_num;
+ GList* rtp_packets_list; /* List of RTP packets in the stream */
+ guint32 num_packets;
+} rtp_stream_info_t;
+
+
+/* defines the RTP streams to be played in an audio channel */
+typedef struct _rtp_channel_info {
+ double start_time; /* RTP stream start time in ms */
+ double end_time; /* RTP stream end time in ms */
+ GArray *samples; /* the array with decoded audio */
+ guint16 call_num;
+ gboolean selected;
+ guint32 frame_index;
+ guint32 drop_by_jitter_buff;
+ guint32 out_of_seq;
+ guint32 max_frame_index;
+ GtkWidget *check_bt;
+ GtkWidget *separator;
+ GtkWidget *scroll_window;
+ GtkWidget *draw_area;
+ GdkPixmap *pixmap;
+ GtkAdjustment *h_scrollbar_adjustment;
+ GdkPixbuf* cursor_pixbuf;
+ PaTimestamp cursor_prev;
+ GdkGC *bg_gc[MAX_NUM_COL_CONV+1];
+ gboolean cursor_catch;
+ rtp_stream_info_t *first_stream; /* This is the first RTP stream in the channel */
+ guint32 num_packets;
+} rtp_channel_info_t;
+
+/* defines a RTP packet */
+typedef struct _rtp_packet {
+ struct _rtp_info *info; /* the RTP dissected info */
+ double arrive_offset; /* arrive offset time since the begining of the stream in ms */
+ guint8* payload_data;
+} rtp_packet_t;
+
+/* defines the two RTP channels to be played */
+typedef struct _rtp_play_channles {
+ rtp_channel_info_t* rci[2]; /* Channels to be played */
+ guint32 start_index[2];
+ guint32 end_index[2];
+ int channel;
+ guint32 max_frame_index;
+ guint32 frame_index;
+ gboolean pause;
+ gboolean stop;
+ gint32 pause_duration;
+ PaTimestamp out_diff_time;
+} rtp_play_channles_t;
+
+/* The two RTP channles to play */
+static rtp_play_channles_t *rtp_channels = NULL;
+
+
+/****************************************************************************/
+static void
+rtp_key_destroy(gchar *key)
+{
+ g_free(key);
+ key = NULL;
+}
+
+/****************************************************************************/
+static void
+rtp_channel_value_destroy(rtp_channel_info_t *rci)
+{
+ g_array_free(rci->samples, TRUE);
+ g_free(rci);
+ rci = NULL;
+}
+
+/****************************************************************************/
+static void
+rtp_stream_value_destroy(rtp_stream_info_t *rsi)
+{
+ GList* rtp_packets_list;
+ rtp_packet_t *rp;
+
+ rtp_packets_list = g_list_first(rsi->rtp_packets_list);
+ while (rtp_packets_list)
+ {
+ rp = rtp_packets_list->data;
+
+ g_free(rp->info);
+ g_free(rp->payload_data);
+ g_free(rp);
+ rp = NULL;
+
+ rtp_packets_list = g_list_next(rtp_packets_list);
+ }
+ g_free(rsi);
+ rsi = NULL;
+}
+
+/****************************************************************************/
+static void
+set_sensitive_check_bt(gchar *key _U_ , rtp_channel_info_t *rci, guint *stop _U_ )
+{
+ gtk_widget_set_sensitive(rci->check_bt, !(*stop));
+}
+
+/****************************************************************************/
+static void
+bt_state(gboolean decode, gboolean play, gboolean pause, gboolean stop)
+{
+ gboolean new_jitter_value = FALSE;
+ gboolean false_val = FALSE;
+
+ gtk_widget_set_sensitive(bt_decode, decode);
+ gtk_widget_set_sensitive(jitter_spinner, decode);
+
+ if (new_jitter_buff != (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner)) {
+ new_jitter_value = TRUE;
+ }
+
+ /* set the sensitive state of play only if there is a channel selected */
+ if ( play && (rtp_channels->rci[0] || rtp_channels->rci[1]) && !new_jitter_value) {
+ gtk_widget_set_sensitive(bt_play, TRUE);
+ } else {
+ gtk_widget_set_sensitive(bt_play, FALSE);
+ }
+
+ if (!new_jitter_value) {
+ gtk_widget_set_sensitive(bt_pause, pause);
+ gtk_widget_set_sensitive(bt_stop, stop);
+
+ /* Set sensitive to the check buttons based on the STOP state */
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &stop);
+ } else {
+ gtk_widget_set_sensitive(bt_pause, FALSE);
+ gtk_widget_set_sensitive(bt_stop, FALSE);
+
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &false_val);
+ }
+}
+
+/****************************************************************************/
+void
+add_rtp_packet(struct _rtp_info *rtp_info, packet_info *pinfo)
+{
+ rtp_stream_info_t *stream_info = NULL;
+ rtp_packet_t *new_rtp_packet;
+ GString *key_str = NULL;
+
+ /* create the the streams hash if it doen't exist */
+ if (!rtp_streams_hash)
+ rtp_streams_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_stream_value_destroy);
+
+ /* Create a hash key to lookup in the RTP streams hash table
+ * uses: src_ip:src_port dst_ip:dst_port ssrc
+ */
+ key_str = g_string_new("");
+ g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(pinfo->src)),
+ pinfo->srcport, get_addr_name(&(pinfo->dst)),
+ pinfo->destport, rtp_info->info_sync_src );
+
+ /* lookup for this rtp packet in the stream hash table*/
+ stream_info = g_hash_table_lookup( rtp_streams_hash, key_str->str);
+
+ /* if it is not in the hash table, create a new stream */
+ if (stream_info==NULL) {
+ stream_info = g_malloc(sizeof(rtp_stream_info_t));
+ COPY_ADDRESS(&(stream_info->src_addr), &(pinfo->src));
+ stream_info->src_port = pinfo->srcport;
+ COPY_ADDRESS(&(stream_info->dest_addr), &(pinfo->dst));
+ stream_info->dest_port = pinfo->destport;
+ stream_info->ssrc = rtp_info->info_sync_src;
+ stream_info->rtp_packets_list = NULL;
+ stream_info->first_frame_number = pinfo->fd->num;
+ stream_info->start_time = nstime_to_msec(&pinfo->fd->rel_ts);
+ stream_info->call_num = 0;
+ stream_info->play = FALSE;
+ stream_info->num_packets = 0;
+
+ g_hash_table_insert(rtp_streams_hash, g_strdup(key_str->str), stream_info);
+
+ /* Add the element to the List too. The List is used to decode the packets because it is sordted */
+ rtp_streams_list = g_list_append(rtp_streams_list, stream_info);
+ }
+
+ /* increment the number of packets in this stream, this is used for the progress bar and statistics*/
+ stream_info->num_packets++;
+
+ /* Add the RTP packet to the list */
+ new_rtp_packet = g_malloc(sizeof(rtp_packet_t));
+ new_rtp_packet->info = g_malloc(sizeof(struct _rtp_info));
+
+ memcpy(new_rtp_packet->info, rtp_info, sizeof(struct _rtp_info));
+ new_rtp_packet->arrive_offset = nstime_to_msec(&pinfo->fd->rel_ts) - stream_info->start_time;
+ /* copy the RTP payload to the rtp_packet to be decoded later */
+ if (rtp_info->info_payload_len) {
+ new_rtp_packet->payload_data = g_malloc(rtp_info->info_payload_len);
+ memcpy(new_rtp_packet->payload_data, &(rtp_info->info_data[rtp_info->info_payload_offset]), rtp_info->info_payload_len);
+ } else {
+ new_rtp_packet->payload_data = NULL;
+ }
+
+ stream_info->rtp_packets_list = g_list_append(stream_info->rtp_packets_list, new_rtp_packet);
+
+ g_string_free(key_str, TRUE);
+}
+
+/****************************************************************************/
+/* Mark the RTP stream to be played. Use the voip_calls graph to see if the
+ * setup_frame is there and then if the associated voip_call is selected.
+ */
+static void
+mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
+{
+ GList* graph_list;
+ graph_analysis_item_t *graph_item;
+ GList* voip_calls_list;
+ voip_calls_info_t *tmp_voip_call;
+
+ /* Reset the "to be play" value because the user can close and reopen the RTP Player window
+ * and the streams are nor reset in that case
+ */
+ rsi->play = FALSE;
+
+ /* and associate the RTP stream with a call using the first RTP in the stream*/
+ graph_list = g_list_first(voip_calls->graph_analysis->list);
+ while (graph_list)
+ {
+ graph_item = graph_list->data;
+ if (rsi->first_frame_number == graph_item->frame_num) {
+ rsi->call_num = graph_item->conv_num;
+ /* if it is in the graph list, then check if the voip_call is selected */
+ voip_calls_list = g_list_first(voip_calls->strinfo_list);
+ while (voip_calls_list)
+ {
+ tmp_voip_call = voip_calls_list->data;
+ if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
+ rsi->play = TRUE;
+ total_packets += rsi->num_packets;
+ break;
+ }
+ voip_calls_list = g_list_next(voip_calls_list);
+ }
+ break;
+ }
+ graph_list = g_list_next(graph_list);
+ }
+}
+
+
+/****************************************************************************/
+/* Decode a RTP packet
+ * Return the number of decoded bytes
+ */
+static int
+decode_rtp_packet(rtp_packet_t *rp, rtp_channel_info_t *rci, SAMPLE **out_buff)
+{
+ unsigned int payload_type;
+ SAMPLE *tmp_buff = NULL;
+ int decoded_bytes = 0;
+
+ if ((rp->payload_data == NULL) || (rp->info->info_payload_len == 0) ) {
+ return 0;
+ }
+
+ payload_type = rp->info->info_payload_type;
+ switch (payload_type) {
+ case 0: /* G711 Ulaw */
+ tmp_buff = malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
+ decodeG711u(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+ case 8: /* G711 Alaw */
+ tmp_buff = malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
+ decodeG711a(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+#ifdef HAVE_G729_G723
+ case 18: /* G729 */
+ tmp_buff = malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 8); /* G729 8kbps => 64kbps/8kbps = 8 */
+ decodeG729(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+ case 4: /* G723 */
+
+ if (rp->info->info_payload_len%24 == 0) /* G723 High 6.4kbps */
+ tmp_buff = malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 10); /* G723 High 64kbps/6.4kbps = 10 */
+ else if (rp->info->info_payload_len%20 == 0) /* G723 Low 5.3kbps */
+ tmp_buff = malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 13); /* G723 High 64kbps/5.3kbps = 13 */
+ else {
+ return 0;
+ }
+ decodeG723(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+#endif
+ }
+
+ *out_buff = tmp_buff;
+ return decoded_bytes;
+}
+
+/****************************************************************************/
+void
+update_progress_bar(gfloat percentage)
+{
+
+ gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), percentage);
+
+ /* Force gtk to redraw the window before starting decoding the packet */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+/****************************************************************************/
+/* Decode the RTP streams and add them to the RTP channels struct
+ */
+static void
+decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr _U_)
+{
+ GString *key_str = NULL;
+ rtp_channel_info_t *rci;
+ gboolean first = TRUE;
+ GList* rtp_packets_list;
+ rtp_packet_t *rp;
+
+ int i;
+ double rtp_time;
+ double rtp_time_prev;
+ double arrive_time;
+ double arrive_time_prev;
+ double start_time;
+ double start_rtp_time;
+ double diff;
+ double pack_period;
+ double total_time;
+ double total_time_prev;
+ gint32 silence_frames;
+ int seq;
+ double delay;
+ double prev_diff;
+ double mean_delay;
+ double variation;
+ int decoded_bytes;
+ int decoded_bytes_prev;
+ int jitter_buff;
+ SAMPLE *out_buff = NULL;
+ sample_t silence;
+ sample_t sample;
+ guint8 status;
+ guint32 start_timestamp;
+
+ guint32 progbar_nextstep;
+ int progbar_quantum;
+ gfloat progbar_val;
+
+ silence.val = 0;
+ silence.status = S_NORMAL;
+
+ /* skip it if we are not going to play it */
+ if (rsi->play == FALSE) {
+ return;
+ }
+
+ /* get the static jitter buffer from the spinner gui */
+ jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
+
+ /* Create a hash key to lookup in the RTP channels hash
+ * uses: src_ip:src_port dst_ip:dst_port call_num
+ */
+ key_str = g_string_new("");
+ g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(rsi->src_addr)),
+ rsi->src_port, get_addr_name(&(rsi->dest_addr)),
+ rsi->dest_port, rsi->call_num );
+
+ /* create the rtp_channels_hash table if it doesn't exist */
+ if (!rtp_channels_hash) {
+ rtp_channels_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_channel_value_destroy);
+ }
+
+ /* lookup for this stream in the channel hash table */
+ rci = g_hash_table_lookup( rtp_channels_hash, key_str->str);
+
+ /* ..if it is not in the hash, create an entry */
+ if (rci == NULL) {
+ rci = malloc(sizeof(rtp_channel_info_t));
+ rci->call_num = rsi->call_num;
+ rci->start_time = rsi->start_time;
+ rci->end_time = rsi->start_time;
+ rci->selected = FALSE;
+ rci->frame_index = 0;
+ rci->drop_by_jitter_buff = 0;
+ rci->out_of_seq = 0;
+ rci->max_frame_index = 0;
+ rci->samples = g_array_new (FALSE, FALSE, sizeof(sample_t));
+ rci->check_bt = NULL;
+ rci->separator = NULL;
+ rci->draw_area = NULL;
+ rci->pixmap = NULL;
+ rci->h_scrollbar_adjustment = NULL;
+ rci->cursor_pixbuf = NULL;
+ rci->cursor_prev = 0;
+ rci->cursor_catch = FALSE;
+ rci->first_stream = rsi;
+ rci->num_packets = rsi->num_packets;
+ g_hash_table_insert(rtp_channels_hash, g_strdup(key_str->str), rci);
+ } else {
+ /* Add silence between the two streams if needed */
+ silence_frames = (gint32)( ((rsi->start_time - rci->end_time)/1000)*SAMPLE_RATE );
+ for (i = 0; i< silence_frames; i++) {
+ g_array_append_val(rci->samples, silence);
+ }
+ rci->num_packets += rsi->num_packets;
+ }
+
+ /* decode the RTP stream */
+ first = TRUE;
+ rtp_time = 0;
+ decoded_bytes = 0;
+ decoded_bytes_prev = 0;
+ silence_frames = 0;
+ arrive_time = start_time = 0;
+ arrive_time_prev = 0;
+ pack_period = 0;
+ total_time = 0;
+ total_time_prev = 0;
+ seq = 0;
+ delay = 0;
+ prev_diff = 0;
+ mean_delay = 0;
+ variation = 0;
+ start_timestamp = 0;
+
+ /* we update the progress bar 100 times */
+ progbar_quantum = total_packets/100;
+ progbar_nextstep = progbar_count;
+
+ status = S_NORMAL;
+
+ rtp_packets_list = g_list_first(rsi->rtp_packets_list);
+ while (rtp_packets_list)
+ {
+
+ if (progbar_count >= progbar_nextstep) {
+ g_assert(total_packets > 0);
+
+ progbar_val = (gfloat) progbar_count / total_packets;
+
+ update_progress_bar(progbar_val);
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+
+ rp = rtp_packets_list->data;
+ if (first == TRUE) {
+ start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
+ start_rtp_time = 0;
+ rtp_time_prev = start_rtp_time;
+ first = FALSE;
+ seq = rp->info->info_seq_num - 1;
+ }
+
+ decoded_bytes = decode_rtp_packet(rp, rci, &out_buff);
+ if (decoded_bytes == 0) {
+ seq = rp->info->info_seq_num;
+ }
+
+ rtp_time = (double)(rp->info->info_timestamp-start_timestamp)/SAMPLE_RATE - start_rtp_time;
+ arrive_time = (double)rp->arrive_offset/1000 - start_time;
+
+ if (rp->info->info_seq_num != seq+1){
+ rci->out_of_seq++;
+ status = S_WRONG_SEQ;
+ }
+ seq = rp->info->info_seq_num;
+
+ diff = arrive_time - rtp_time;
+
+ delay = diff - prev_diff;
+ prev_diff = diff;
+ if (delay<0) delay = -delay;
+
+ if (diff<0) diff = -diff;
+
+ total_time = (double)rp->arrive_offset/1000;
+
+ printf("seq = %d arr = %f abs_diff = %f index = %d tim = %f ji=%d jb=%f\n",rp->info->info_seq_num,
+ total_time, diff, rci->samples->len, ((double)rci->samples->len/8000 - total_time)*1000, 0,
+ (mean_delay + 4*variation)*1000);
+ fflush(stdout);
+
+ /* if the jitter buffer was exceded */
+ if ( diff*1000 > jitter_buff ) {
+ printf("Packet drop by jitter buffer exceded\n");
+ rci->drop_by_jitter_buff++;
+ status = S_DROP_BY_JITT;
+
+ /* if there was a silence period (more than two packetization period) resync the source */
+ if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
+ printf("Resync...\n");
+
+ silence_frames = (gint32)((arrive_time - arrive_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
+ for (i = 0; i< silence_frames; i++) {
+ silence.status = status;
+ g_array_append_val(rci->samples, silence);
+
+ /* only mark the fisrt in the silence that has the previos problem (S_DROP_BY_JITT or S_WRONG_SEQ ) */
+ status = S_NORMAL;
+ }
+
+ decoded_bytes_prev = 0;
+ start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
+ start_rtp_time = 0;
+ start_time = (double)rp->arrive_offset/1000;
+ rtp_time_prev = 0;
+ }
+ } else {
+ /* Add silence if it is necessary */
+ silence_frames = (gint32)((rtp_time - rtp_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
+ for (i = 0; i< silence_frames; i++) {
+ silence.status = status;
+ g_array_append_val(rci->samples, silence);
+
+ /* only mark the fisrt in the silence that has the previos problem (S_DROP_BY_JITT or S_WRONG_SEQ ) */
+ status = S_NORMAL;
+ }
+
+ status = S_NORMAL;
+
+ /* Add the audio */
+ for (i = 0; i< (decoded_bytes/2); i++) {
+ sample.val = out_buff[i];
+ sample.status = status;
+ g_array_append_val(rci->samples, sample);
+ }
+
+ rtp_time_prev = rtp_time;
+ pack_period = (double)(decoded_bytes/2)/SAMPLE_RATE;
+ decoded_bytes_prev = decoded_bytes;
+ arrive_time_prev = arrive_time;
+
+ }
+
+ rtp_packets_list = g_list_next (rtp_packets_list);
+ progbar_count++;
+ }
+ rci->max_frame_index = rci->samples->len;
+ rci->end_time = rci->start_time + ((double)rci->samples->len/SAMPLE_RATE)*1000;
+
+ g_string_free(key_str, TRUE);
+}
+
+/****************************************************************************/
+static gint
+h_scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
+{
+ rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
+ rci->cursor_catch = TRUE;
+ return TRUE;
+}
+
+static gboolean draw_cursors(gpointer *data);
+
+/****************************************************************************/
+static void
+stop_channels()
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ /* we should never be here if we are already in STOP */
+ if(rtp_channels->stop){
+ exit(10);
+ }
+
+ rtp_channels->stop = TRUE;
+ /* force a draw_cursor to stop it */
+ draw_cursors(NULL);
+
+ err = Pa_StopStream(pa_stream);
+
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Stop Stream in PortAduio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ rtp_channels->start_index[0] = 0;
+ rtp_channels->start_index[1] = 0;
+ rtp_channels->end_index[0] = 0;
+ rtp_channels->end_index[1] = 0;
+ rtp_channels->max_frame_index = 0;
+ rtp_channels->frame_index = 0;
+ rtp_channels->pause = FALSE;
+ rtp_channels->pause_duration = 0;
+ rtp_channels->stop = TRUE;
+ rtp_channels->out_diff_time = 10000;
+
+ if (rtp_channels->rci[0]) rtp_channels->rci[0]->frame_index = 0;
+ if (rtp_channels->rci[1]) rtp_channels->rci[1]->frame_index = 0;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+
+}
+
+/****************************************************************************/
+/* Draw a cursor in a channel graph
+ */
+static void
+draw_channel_cursor(rtp_channel_info_t *rci, guint32 start_index)
+{
+ PaTimestamp index;
+ int i;
+
+ if (!rci) return;
+
+ index = Pa_StreamTime( pa_stream ) - rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
+
+
+ /* If we finished playing both channels, then stop them */
+ if ( (rtp_channels && (!rtp_channels->stop) && (!rtp_channels->pause)) && (index > rtp_channels->max_frame_index) ) {
+ stop_channels();
+ return;
+ }
+
+ /* If only this channel finished, then return */
+ if (index > rci->max_frame_index) {
+ return;
+ }
+
+ /* draw the previous saved pixbuf line */
+ if (rci->cursor_pixbuf) {
+
+ gdk_draw_pixbuf(rci->pixmap, NULL, rci->cursor_pixbuf, 0, 0, (int) (rci->cursor_prev/MULT), 0, -1, -1, GDK_RGB_DITHER_NONE, 0 ,0);
+
+ gdk_draw_drawable(rci->draw_area->window,
+ rci->draw_area->style->fg_gc[GTK_WIDGET_STATE(rci->draw_area)],
+ rci->pixmap,
+ (int) (rci->cursor_prev/MULT), 0,
+ (int) (rci->cursor_prev/MULT), 0,
+ 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
+
+ g_object_unref(rci->cursor_pixbuf);
+ }
+
+ rci->cursor_pixbuf = gdk_pixbuf_get_from_drawable(NULL, rci->pixmap, NULL, (int) (index/MULT), 0, 0, 0, 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
+
+ gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
+ (int) (index/MULT),
+ 0,
+ (int) (index/MULT),
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
+
+ gdk_draw_drawable(rci->draw_area->window,
+ rci->draw_area->style->fg_gc[GTK_WIDGET_STATE(rci->draw_area)],
+ rci->pixmap,
+ (int) (index/MULT), 0,
+ (int) (index/MULT), 0,
+ 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
+
+ /* Disconnect the scroll bar "value" signal to not be called */
+ SIGNAL_DISCONNECT_BY_FUNC(rci->h_scrollbar_adjustment, h_scrollbar_changed, rci);
+
+ /* Move the horizontal scroll bar */
+/* if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
+ (index/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
+ for (i=1; i<10; i++) {
+ rci->h_scrollbar_adjustment->value += rci->h_scrollbar_adjustment->page_size/10;
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ }
+
+ }
+*/
+ if (!rci->cursor_catch) {
+ if (index/MULT < rci->h_scrollbar_adjustment->page_size/2) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
+ } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
+ } else {
+ rci->h_scrollbar_adjustment->value = index/MULT - rci->h_scrollbar_adjustment->page_size/2;
+ }
+
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ } else if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
+ (index/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
+ rci->cursor_catch = FALSE;
+ for (i=1; i<10; i++) {
+ rci->h_scrollbar_adjustment->value = min(rci->h_scrollbar_adjustment->upper-rci->h_scrollbar_adjustment->page_size, rci->h_scrollbar_adjustment->value + (rci->h_scrollbar_adjustment->page_size/20));
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ }
+
+ }
+
+
+ /* Connect back the "value" scroll signal */
+ SIGNAL_CONNECT(rci->h_scrollbar_adjustment, "value_changed", h_scrollbar_changed, rci);
+
+
+/* if (index/MULT < rci->h_scrollbar_adjustment->page_increment) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
+ } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size + rci->h_scrollbar_adjustment->page_increment)) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
+ } else {
+ if ( (index/MULT < rci->h_scrollbar_adjustment->value) || (index/MULT > (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
+ rci->h_scrollbar_adjustment->value = index/MULT;
+ }
+ }
+*/
+
+/* if (index/MULT < rci->h_scrollbar_adjustment->page_size/2) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
+ } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
+ } else {
+ rci->h_scrollbar_adjustment->value = index/MULT - rci->h_scrollbar_adjustment->page_size/2;
+ }
+*/
+/* gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+*/
+ rci->cursor_prev = index;
+}
+
+/****************************************************************************/
+/* Move and draw the cursor in the graph
+ */
+static gboolean
+draw_cursors(gpointer *data)
+{
+ static GdkPixbuf* pixbuf = NULL;
+ static PaTimestamp prev;
+
+ if (!rtp_channels) return FALSE;
+
+ /* Draw and move each of the two channels */
+ draw_channel_cursor(rtp_channels->rci[0], rtp_channels->start_index[0]);
+ draw_channel_cursor(rtp_channels->rci[1], rtp_channels->start_index[1]);
+
+ if ((rtp_channels->stop) || (rtp_channels->pause)) return FALSE;
+
+ return TRUE;
+}
+
+/****************************************************************************/
+void
+init_rtp_channels_vals()
+{
+ rtp_play_channles_t *rpci = rtp_channels;
+
+ /* if we only have one channel to play, we just use the info from that channel */
+ if (rpci->rci[0] == NULL) {
+ rpci->max_frame_index = rpci->rci[1]->max_frame_index;
+ rpci->start_index[0] = rpci->max_frame_index;
+ rpci->start_index[1] = 0;
+ rpci->end_index[0] = rpci->max_frame_index;
+ rpci->end_index[1] = rpci->max_frame_index;
+ } else if (rpci->rci[1] == NULL) {
+ rpci->max_frame_index = rpci->rci[0]->max_frame_index;
+ rpci->start_index[1] = rpci->max_frame_index;
+ rpci->start_index[0] = 0;
+ rpci->end_index[0] = rpci->max_frame_index;
+ rpci->end_index[1] = rpci->max_frame_index;
+
+ /* if the two channels are to be played, then we need to sync both based on the start/end time of each one */
+ } else {
+ rpci->max_frame_index = (guint32)(SAMPLE_RATE/1000) * (guint32)(max(rpci->rci[0]->end_time, rpci->rci[1]->end_time) -
+ (guint32)min(rpci->rci[0]->start_time, rpci->rci[1]->start_time));
+
+ if (rpci->rci[0]->start_time < rpci->rci[1]->start_time) {
+ rpci->start_index[0] = 0;
+ rpci->start_index[1] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->start_time - rpci->rci[0]->start_time);
+ } else {
+ rpci->start_index[1] = 0;
+ rpci->start_index[0] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->start_time - rpci->rci[1]->start_time);
+ }
+
+ if (rpci->rci[0]->end_time < rpci->rci[1]->end_time) {
+ rpci->end_index[0] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->end_time - rpci->rci[0]->end_time));
+ rpci->end_index[1] = rpci->max_frame_index;
+ } else {
+ rpci->end_index[1] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->end_time - rpci->rci[1]->end_time));
+ rpci->end_index[0] = rpci->max_frame_index;
+ }
+ }
+}
+
+
+/****************************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+ * It may called at interrupt level on some machines so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ */
+static int paCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData)
+{
+ rtp_play_channles_t *rpci = (rtp_play_channles_t*)userData;
+ SAMPLE *wptr = (SAMPLE*)outputBuffer;
+ sample_t sample;
+ unsigned int i;
+ int finished;
+ unsigned int framesLeft;
+ int framesToPlay;
+
+ /* if it is pasued, we keep the stream running but with silence only */
+ if (rtp_channels->pause) {
+ for(i=0; i<framesPerBuffer; i++ ) {
+ *wptr++ = 0;
+ *wptr++ = 0;
+ }
+ rtp_channels->pause_duration += framesPerBuffer;
+ return 0;
+ }
+
+ rpci->out_diff_time = outTime - Pa_StreamTime(pa_stream) ;
+
+
+ /* set the values if this is the first time */
+ if (rpci->max_frame_index == 0) {
+ init_rtp_channels_vals();
+
+ }
+
+ framesLeft = rpci->max_frame_index - rpci->frame_index;
+
+ (void) inputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ framesToPlay = framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ framesToPlay = framesPerBuffer;
+ finished = 0;
+ }
+
+ for( i=0; i<(unsigned int)framesToPlay; i++ )
+ {
+ if (rpci->rci[0] && ( (rpci->frame_index >= rpci->start_index[0]) && (rpci->frame_index <= rpci->end_index[0]) )) {
+ sample = g_array_index(rpci->rci[0]->samples, sample_t, rpci->rci[0]->frame_index++);
+ *wptr++ = sample.val;
+ } else {
+ *wptr++ = 0;
+ }
+
+ if (rpci->rci[1] && ( (rpci->frame_index >= rpci->start_index[1]) && (rpci->frame_index <= rpci->end_index[1]) )) {
+ sample = g_array_index(rpci->rci[1]->samples, sample_t, rpci->rci[1]->frame_index++);
+ *wptr++ = sample.val;
+ } else {
+ *wptr++ = 0;
+ }
+ }
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = 0;
+ *wptr++ = 0;
+ }
+ rpci->frame_index += framesToPlay;
+
+ return finished;
+}
+
+/****************************************************************************/
+static void
+on_bt_check_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ rtp_channel_info_t *rci = user_data;
+
+ if (rci->selected) {
+ if (rtp_channels->rci[0] == rci) {
+ rtp_channels->rci[0] = NULL;
+ rtp_channels->channel = 0;
+ } else {
+ rtp_channels->rci[1] = NULL;
+ rtp_channels->channel = 1;
+ }
+ } else {
+ /* if there are already both channels selected, unselect the old one */
+ if (rtp_channels->rci[rtp_channels->channel]) {
+ /* we disconnect the signal temporarly to avoid been called back */
+ SIGNAL_DISCONNECT_BY_FUNC(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ gtk_toggle_button_set_active((GtkToggleButton *)rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
+ SIGNAL_CONNECT(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
+ }
+
+ rtp_channels->rci[rtp_channels->channel] = rci;
+ rtp_channels->channel = !(rtp_channels->channel);
+ }
+
+ rci->selected = !(rci->selected);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+}
+
+/****************************************************************************/
+static void channel_draw(rtp_channel_info_t* rci)
+{
+ int i,j;
+ sample_t sample;
+ SAMPLE min, max;
+ PangoLayout *small_layout;
+ guint32 label_width, label_height;
+ char label_string[MAX_TIME_LABEL];
+ double offset;
+ guint32 progbar_nextstep;
+ int progbar_quantum;
+ gfloat progbar_val;
+ guint status;
+ GdkGC *gc;
+ GdkGC *red_gc;
+ GdkColor red_color = {0, 65535, 0, 0};
+
+ if (GDK_IS_DRAWABLE(rci->pixmap)) {
+ /* Clear out old plot */
+ gdk_draw_rectangle(rci->pixmap,
+ rci->bg_gc[1+rci->call_num%MAX_NUM_COL_CONV],
+ TRUE,
+ 0, 0,
+ rci->draw_area->allocation.width,
+ rci->draw_area->allocation.height);
+
+ small_layout = gtk_widget_create_pango_layout(rci->draw_area, label_string);
+ pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
+
+ /* calculated the pixel offset to display integer seconds */
+ offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*SAMPLE_RATE/MULT;
+
+ gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
+ 0,
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
+ rci->draw_area->allocation.width,
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
+
+ /* we update the progress bar 100 times */
+ progbar_quantum = (total_frames/MULT)/100;
+ progbar_nextstep = progbar_count;
+
+ red_gc = gdk_gc_new(rci->draw_area->window);
+ gdk_gc_set_rgb_fg_color(red_gc, &red_color);
+
+ for (i=0; i< min(rci->draw_area->allocation.width,(gint)(rci->samples->len/MULT)); i++) {
+ sample.val = 0;
+ status = S_NORMAL;
+ max=(SAMPLE)0xFFFF;
+ min=(SAMPLE)0x7FFF;
+
+ if (progbar_count >= progbar_nextstep) {
+ g_assert(total_frames > 0);
+
+ progbar_val = (gfloat) progbar_count / (total_frames/MULT);
+
+ update_progress_bar(progbar_val);
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ for (j=0; j<MULT; j++) {
+ sample = g_array_index(rci->samples, sample_t, i*MULT+j);
+ max = max(max, sample.val);
+ min = min(min, sample.val);
+ if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
+ }
+
+ if (status == S_DROP_BY_JITT) {
+ gc = red_gc;
+ } else {
+ gc = rci->draw_area->style->black_gc;
+ }
+
+ gdk_draw_line(rci->pixmap, gc,
+ i,
+ (gint)(( (0x7FFF+min) * (rci->draw_area->allocation.height-HEIGHT_TIME_LABEL))/0xFFFF),
+ i,
+ (gint)(( (0x7FFF+max) * (rci->draw_area->allocation.height-HEIGHT_TIME_LABEL))/0xFFFF));
+
+ /*draw the time label and grid */
+ if ( !((i*MULT)%(SAMPLE_RATE)) ) {
+ gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
+ (int) (i - offset),
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
+ (int) (i - offset),
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL+4);
+
+ g_snprintf(label_string, MAX_TIME_LABEL, "%.0f", floor(rci->start_time/1000) + i*MULT/SAMPLE_RATE);
+
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ gdk_draw_layout(rci->pixmap,
+ rci->draw_area->style->black_gc,
+ (int) (i - offset - label_width/2),
+ rci->draw_area->allocation.height - label_height,
+ small_layout);
+ /* draw the 1/2 sec grid */
+ } else if ( !((i*MULT)%(SAMPLE_RATE/2)) ) {
+ gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
+ (int) (i - offset),
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
+ (int) (i - offset),
+ rci->draw_area->allocation.height-HEIGHT_TIME_LABEL+2);
+
+ }
+
+ progbar_count++;
+ }
+ }
+
+}
+/****************************************************************************/
+static gint expose_event_channels(GtkWidget *widget, GdkEventExpose *event)
+{
+ rtp_channel_info_t *rci;
+
+ rci=(rtp_channel_info_t *)OBJECT_GET_DATA(widget, "rtp_channel_info_t");
+ if(!rci){
+ exit(10);
+ }
+
+ if (GDK_IS_DRAWABLE(widget->window))
+ gdk_draw_drawable(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ rci->pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+/****************************************************************************/
+static gint
+configure_event_channels(GtkWidget *widget, GdkEventConfigure *event _U_)
+{
+ rtp_channel_info_t *rci;
+ int i;
+
+ /* the first calor is blue to highlight the selected item
+ * the other collors are the same as in the Voip Graph analysys
+ * to match the same calls
+ */
+ static GdkColor col[MAX_NUM_COL_CONV+1] = {
+ {0, 0x00FF, 0x00FF, 0xFFFF},
+ {0, 0x33FF, 0xFFFF, 0x33FF},
+ {0, 0x00FF, 0xCCFF, 0xCCFF},
+ {0, 0x66FF, 0xFFFF, 0xFFFF},
+ {0, 0x99FF, 0x66FF, 0xFFFF},
+ {0, 0xFFFF, 0xFFFF, 0x33FF},
+ {0, 0xCCFF, 0x99FF, 0xFFFF},
+ {0, 0xCCFF, 0xFFFF, 0x33FF},
+ {0, 0xFFFF, 0xCCFF, 0xCCFF},
+ {0, 0xFFFF, 0x99FF, 0x66FF},
+ {0, 0xFFFF, 0xFFFF, 0x99FF}
+ };
+
+ rci=(rtp_channel_info_t *)OBJECT_GET_DATA(widget, "rtp_channel_info_t");
+ if(!rci){
+ exit(10);
+ }
+
+ if(rci->pixmap){
+ g_object_unref(rci->pixmap);
+ rci->pixmap=NULL;
+ }
+
+ rci->pixmap = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+
+ if ( GDK_IS_DRAWABLE(rci->pixmap) )
+ gdk_draw_rectangle(rci->pixmap,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ /* create gcs for the background color of each channel */
+ for (i=0; i<MAX_NUM_COL_CONV+1; i++){
+ rci->bg_gc[i]=gdk_gc_new(rci->pixmap);
+ gdk_gc_set_rgb_fg_color(rci->bg_gc[i], &col[i]);
+ }
+
+ channel_draw(rci);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static gint
+button_press_event_channel(GtkWidget *widget, GdkEventButton *event _U_)
+{
+ rtp_channel_info_t *rci;
+ int this_channel;
+ guint32 prev_index;
+
+ rci=(rtp_channel_info_t *)OBJECT_GET_DATA(widget, "rtp_channel_info_t");
+ if(!rci){
+ exit(10);
+ }
+
+ if (!rci->selected) {
+
+ /* only select a new channels if we are in STOP */
+ if (!rtp_channels->stop) return 0;
+
+ /* if there are already both channels selected, unselect the old one */
+ if (rtp_channels->rci[rtp_channels->channel]) {
+ /* we disconnect the signal temporarly to avoid been called back */
+ SIGNAL_DISCONNECT_BY_FUNC(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ gtk_toggle_button_set_active((GtkToggleButton *) rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
+ SIGNAL_CONNECT(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
+ }
+
+ /* we disconnect the signal temporarly to avoid been called back */
+ SIGNAL_DISCONNECT_BY_FUNC(rci->check_bt, on_bt_check_clicked, rci);
+ gtk_toggle_button_set_active((GtkToggleButton *) rci->check_bt, TRUE);
+ SIGNAL_CONNECT(rci->check_bt, "clicked", on_bt_check_clicked, rci);
+
+ rtp_channels->rci[rtp_channels->channel] = rci;
+ rtp_channels->channel = !(rtp_channels->channel);
+ rci->selected = TRUE;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+ }
+
+ if (rci == rtp_channels->rci[0]) {
+ this_channel = 0;
+ } else {
+ this_channel = 1;
+ }
+
+ rci->frame_index = (unsigned int) (event->x * MULT);
+
+ prev_index = rtp_channels->frame_index;
+ rtp_channels->frame_index = rtp_channels->start_index[this_channel] + rci->frame_index;
+ rtp_channels->pause_duration += prev_index - rtp_channels->frame_index;
+
+
+
+ /* change the index in the other channel if selected, according with the index position */
+ if (rtp_channels->rci[!this_channel]) {
+ init_rtp_channels_vals();
+
+ if (rtp_channels->frame_index < rtp_channels->start_index[!this_channel]) {
+ rtp_channels->rci[!this_channel]->frame_index = 0;
+ } else if (rtp_channels->frame_index > rtp_channels->end_index[!this_channel]) {
+ rtp_channels->rci[!this_channel]->frame_index = rtp_channels->rci[!this_channel]->max_frame_index;
+ } else {
+ rtp_channels->rci[!this_channel]->frame_index = rtp_channels->frame_index - rtp_channels->start_index[!this_channel];
+ }
+ } else {
+ init_rtp_channels_vals();
+ }
+
+ rtp_channels->out_diff_time = 0;
+
+ rci->cursor_catch = TRUE;
+
+ /* redraw the cusrsor */
+ draw_cursors(NULL);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static void
+/*add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ ) */
+add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, guint *counter _U_ )
+{
+ GString *label = NULL;
+ GtkWidget *viewport;
+
+
+ /* create the channel draw area */
+ rci->draw_area=gtk_drawing_area_new();
+
+ rci->scroll_window=gtk_scrolled_window_new(NULL, NULL);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (rci->scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
+ rci->h_scrollbar_adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window));
+
+
+ gtk_widget_set_size_request(rci->draw_area, (gint)(rci->samples->len/MULT), CHANNEL_HEIGHT);
+
+
+ viewport = gtk_viewport_new(rci->h_scrollbar_adjustment, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window)));
+ gtk_container_add(GTK_CONTAINER(viewport), rci->draw_area);
+ gtk_container_add(GTK_CONTAINER(rci->scroll_window), viewport);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+ OBJECT_SET_DATA(rci->draw_area, "rtp_channel_info_t", rci);
+ gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
+ GTK_WIDGET_SET_FLAGS(rci->draw_area, GTK_CAN_FOCUS);
+ gtk_widget_grab_focus(rci->draw_area);
+
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->scroll_window, FALSE, FALSE, 0);
+
+ /* signals needed to handle backing pixmap */
+ SIGNAL_CONNECT(rci->draw_area, "expose_event", expose_event_channels, NULL);
+ SIGNAL_CONNECT(rci->draw_area, "configure_event", configure_event_channels, rci);
+ gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
+ SIGNAL_CONNECT(rci->draw_area, "button_press_event", button_press_event_channel, rci);
+ SIGNAL_CONNECT(rci->h_scrollbar_adjustment, "value_changed", h_scrollbar_changed, rci);
+
+
+ label = g_string_new("");
+ g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Drop by Jitter Buff:%d(%.1f%%) Out of Seq: %d(%.1f%%)", get_addr_name(&(rci->first_stream->src_addr)),
+ rci->first_stream->src_port, get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
+ (double)rci->samples->len/SAMPLE_RATE, rci->drop_by_jitter_buff, (double)rci->drop_by_jitter_buff * 100 / (double)rci->num_packets
+ , rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets);
+
+ rci->check_bt = gtk_check_button_new_with_label(label->str);
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->check_bt, FALSE, FALSE, 1);
+
+ /* Create the Separator if it is not the last one */
+ (*counter)++;
+ if (*counter < g_hash_table_size(rtp_channels_hash)) {
+ rci->separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->separator, FALSE, FALSE, 5);
+ }
+
+ SIGNAL_CONNECT(rci->check_bt, "clicked", on_bt_check_clicked, rci);
+
+ g_string_free(label, TRUE);
+}
+
+/****************************************************************************/
+static void
+count_channel_frames(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
+{
+ total_frames += rci->samples->len;
+}
+
+/****************************************************************************/
+static void
+play_channels()
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ /* we should never be here if we are in PLAY and !PAUSE */
+ if(!rtp_channels->stop && !rtp_channels->pause){
+ exit(10);
+ }
+
+ /* if we are in PAUSE change the sate */
+ if (rtp_channels->pause) {
+ rtp_channels->pause = FALSE;
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, TRUE, TRUE);
+
+ /* if not PAUSE, then start to PLAY */
+ } else {
+ err = Pa_OpenStream(
+ &pa_stream,
+ paNoDevice, /* default input device */
+ 0, /* no input */
+ PA_SAMPLE_TYPE, /* 16 bit Integer input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(),
+ NUM_CHANNELS, /* Stereo output */
+ PA_SAMPLE_TYPE, /* 16 bit Integer output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ paCallback,
+ rtp_channels );
+
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Open Stream in PortAduio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ err = Pa_StartStream( pa_stream );
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Start Stream in PortAduio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ rtp_channels->stop = FALSE;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, TRUE, TRUE);
+ }
+
+ /* Draw the cursor in the graph */
+ g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
+
+}
+
+/****************************************************************************/
+static void
+pause_channels()
+{
+ rtp_channels->pause = !(rtp_channels->pause);
+
+ /* reactivate the cusrosr display if no in pause */
+ if (!rtp_channels->pause) {
+ /* Draw the cursor in the graph */
+ g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
+ }
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, TRUE, FALSE, TRUE);
+}
+
+/****************************************************************************/
+static void
+reset_rtp_channels()
+{
+ rtp_channels->channel = 0;
+ rtp_channels->rci[0] = NULL;
+ rtp_channels->rci[1] = NULL;
+ rtp_channels->start_index[0] = 0;
+ rtp_channels->start_index[1] = 0;
+ rtp_channels->end_index[0] = 0;
+ rtp_channels->end_index[1] = 0;
+ rtp_channels->max_frame_index = 0;
+ rtp_channels->frame_index = 0;
+ rtp_channels->pause = FALSE;
+ rtp_channels->pause_duration = 0;
+ rtp_channels->stop = TRUE;
+ rtp_channels->out_diff_time = 10000;
+}
+
+/****************************************************************************/
+static void
+remove_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
+{
+ g_object_unref(rci->pixmap);
+ gtk_widget_destroy(rci->draw_area);
+ gtk_widget_destroy(rci->scroll_window);
+ gtk_widget_destroy(rci->check_bt);
+ gtk_widget_destroy(rci->separator);
+}
+
+/****************************************************************************/
+static void
+reset_channels()
+{
+
+ /* Remove the channels from the main window if there are there */
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)remove_channel_to_window, NULL);
+
+
+ /* destroy the rtp channels hash table */
+ if (rtp_channels_hash) {
+ g_hash_table_destroy(rtp_channels_hash);
+ rtp_channels_hash = NULL;
+ }
+
+ if (rtp_channels) {
+ reset_rtp_channels();
+ }
+}
+
+/****************************************************************************/
+void
+reset_rtp_player()
+{
+ /* Destroy the rtp channels */
+ reset_channels();
+
+ /* destroy the rtp streams hash table */
+ if (rtp_streams_hash) {
+ g_hash_table_destroy(rtp_streams_hash);
+ rtp_streams_hash = NULL;
+ }
+
+ /* destroy the rtp streams list */
+ if (rtp_streams_list) {
+ g_list_free (rtp_streams_list);
+ rtp_streams_list = NULL;
+ }
+
+}
+
+/****************************************************************************/
+static void
+decode_streams()
+{
+ guint statusbar_context;
+ guint counter;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, FALSE, FALSE);
+
+ reset_channels();
+
+ progress_bar = gtk_progress_bar_new();
+ WIDGET_SET_SIZE(progress_bar, 100, -1);
+ gtk_box_pack_start(GTK_BOX (stat_hbox), progress_bar, FALSE, FALSE, 2);
+ gtk_widget_show(progress_bar);
+ statusbar_context = gtk_statusbar_get_context_id((GtkStatusbar *) info_bar, "main");
+ gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Decoding RTP packets...");
+
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
+
+ /* reset the number of packet to be decoded, this is used for the progress bar */
+ total_packets = 0;
+ /* reset the Progress Bar count */
+ progbar_count = 0;
+
+ /* Mark the RTP streams to be played using the selected VoipCalls*/
+ g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_rtp_stream_to_play, NULL);
+
+ /* Decode the RTP streams and add them to the RTP channels to be played */
+ g_list_foreach( rtp_streams_list, (GFunc)decode_rtp_stream, NULL);
+
+ /* reset the number of frames to be displayed, this is used for the progress bar */
+ total_frames = 0;
+ /* Count the frames in all the RTP channels */
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)count_channel_frames, NULL);
+
+ /* reset the Progress Bar count again for the progress of creating the channels view */
+ progbar_count = 0;
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+ gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Creating channels view...");
+
+ /* Display the RTP channels in the window */
+ counter = 0;
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)add_channel_to_window, &counter);
+
+ /* Resize the main scroll window to display no more than 5 channels, otherwise the scroll bar need to be used */
+ WIDGET_SET_SIZE(main_scrolled_window, CHANNEL_WIDTH,
+ min(g_hash_table_size(rtp_channels_hash), 5) * (CHANNEL_HEIGHT+60));
+
+ gtk_widget_show_all(main_scrolled_window);
+
+ gtk_widget_destroy(progress_bar);
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+
+ /* blank the status label */
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, FALSE, FALSE, FALSE);
+
+ /* get the static jitter buffer from the spinner gui */
+ new_jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
+
+}
+
+/****************************************************************************/
+static void
+on_bt_decode_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ decode_streams();
+}
+
+/****************************************************************************/
+static void
+on_bt_play_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ play_channels();
+}
+
+/****************************************************************************/
+static void
+on_bt_pause_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ pause_channels();
+}
+
+/****************************************************************************/
+static void
+on_bt_stop_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ stop_channels();
+}
+
+/****************************************************************************/
+static void
+rtp_player_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
+{
+ /* Stop the channels if necesary */
+ if(rtp_channels && (!rtp_channels->stop)){
+ stop_channels();
+ }
+
+ /* Destroy the rtp channels */
+ reset_channels();
+
+ g_free(rtp_channels);
+ rtp_channels = NULL;
+
+ initialized = FALSE;
+
+ gtk_widget_destroy(rtp_player_dlg_w);
+ main_scrolled_window = NULL;
+ rtp_player_dlg_w = NULL;
+}
+
+/****************************************************************************/
+static void
+jitter_spinner_value_changed (GtkSpinButton *spinner, gpointer user_data _U_)
+{
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+}
+
+/****************************************************************************/
+static void rtp_player_dlg_create()
+{
+ GtkWidget *main_vb;
+ GtkWidget *hbuttonbox;
+ GtkWidget *h_jitter_buttons_box;
+ GtkWidget *bt_close;
+ GtkAdjustment *jitter_spinner_adj;
+ GtkWidget *label;
+
+ GtkTooltips *tooltips = gtk_tooltips_new();
+
+ rtp_player_dlg_w=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title(GTK_WINDOW(rtp_player_dlg_w), "Wireshark: RTP Player");
+ gtk_window_set_position(GTK_WINDOW(rtp_player_dlg_w), GTK_WIN_POS_NONE);
+
+ gtk_window_set_default_size(GTK_WINDOW(rtp_player_dlg_w), 400, 50);
+
+ main_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(rtp_player_dlg_w), main_vb);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vb), 2);
+
+ main_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (main_scrolled_window), 4);
+ WIDGET_SET_SIZE(main_scrolled_window, CHANNEL_WIDTH, 0);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_scrolled_window);
+
+ channels_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (channels_vb), 2);
+ gtk_scrolled_window_add_with_viewport((GtkScrolledWindow *) main_scrolled_window, channels_vb);
+
+ h_jitter_buttons_box = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (h_jitter_buttons_box), 10);
+ gtk_box_pack_start (GTK_BOX(main_vb), h_jitter_buttons_box, FALSE, FALSE, 0);
+ label = gtk_label_new("Jitter buffer [ms] ");
+ gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), label, FALSE, FALSE, 0);
+
+ jitter_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (50, 0, 500, 5, 10, 10);
+ jitter_spinner = gtk_spin_button_new (jitter_spinner_adj, 5, 0);
+ gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), jitter_spinner, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip (tooltips, jitter_spinner, "The simulated jitter buffer in [ms]", NULL);
+ SIGNAL_CONNECT(GTK_OBJECT (jitter_spinner_adj), "value_changed", (GtkSignalFunc) jitter_spinner_value_changed, NULL);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (h_jitter_buttons_box), hbuttonbox, TRUE, TRUE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
+ gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
+
+ bt_decode = gtk_button_new_with_label("Decode");
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_decode);
+ SIGNAL_CONNECT(bt_decode, "clicked", on_bt_decode_clicked, NULL);
+ gtk_tooltips_set_tip (tooltips, bt_decode, "Decode the RTP stream(s)", NULL);
+
+ bt_play = gtk_button_new_with_label("Play");
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_play);
+ SIGNAL_CONNECT(bt_play, "clicked", on_bt_play_clicked, NULL);
+ gtk_tooltips_set_tip (tooltips, bt_play, "Play the RTP channel(s)", NULL);
+
+ bt_pause = gtk_button_new_with_label("Pause");
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_pause);
+ SIGNAL_CONNECT(bt_pause, "clicked", on_bt_pause_clicked, NULL);
+ gtk_tooltips_set_tip (tooltips, bt_pause, "Pause the RTP channel(s)", NULL);
+
+ bt_stop = gtk_button_new_with_label("Stop");
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_stop);
+ SIGNAL_CONNECT(bt_stop, "clicked", on_bt_stop_clicked, NULL);
+ gtk_tooltips_set_tip (tooltips, bt_stop, "Stop the RTP channel(s)", NULL);
+
+ bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+ gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
+
+ SIGNAL_CONNECT(bt_close, "clicked", rtp_player_on_destroy, NULL);
+ SIGNAL_CONNECT(rtp_player_dlg_w, "destroy", rtp_player_on_destroy, NULL);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+
+ /* Filter/status hbox */
+ stat_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
+
+ /* statusbar */
+ info_bar = gtk_statusbar_new();
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
+
+ gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
+
+ /* statusbar hbox */
+ gtk_box_pack_start(GTK_BOX(main_vb), stat_hbox, FALSE, TRUE, 0);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, FALSE, FALSE, FALSE);
+
+ gtk_widget_show_all(rtp_player_dlg_w);
+
+ /* Force gtk to redraw the window before starting decoding the packet */
+ while (g_main_context_iteration(NULL, FALSE));
+}
+
+/****************************************************************************/
+void
+rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap)
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ if (initialized) return;
+ initialized = TRUE;
+
+ voip_calls = voip_calls_tap;
+ err = Pa_Initialize();
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Initialize the PortAduio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ initialized = FALSE;
+ return;
+ }
+
+ new_jitter_buff = -1;
+
+#ifdef HAVE_G729_G723
+ /* Initialize the G729 and G723 decoders */
+ initG723();
+ initG729();
+#endif
+
+ if (!rtp_channels) {
+ rtp_channels = g_malloc(sizeof(rtp_play_channles_t));
+ }
+
+ reset_rtp_channels();
+
+ /* create the dialog window */
+ rtp_player_dlg_create();
+
+}
+
+#endif