Witness: enum witness_notifyResponse_type
[wireshark-wip.git] / ui / gtk / rtp_player.c
blob82f833ccf1a5066bad874b48352a21fc6be50baf
1 /* rtp_player.c
3 * $Id$
5 * Copyright 2006, Alejandro Vaquero <alejandrovaquero@yahoo.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1999 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * Here is a summary on how this works:
28 * - The VoipCalls will call add_rtp_packet() every time there is an RTP
29 * packet
30 * - add_rtp_packet() will add the RTP packet in a RTP stream struct, and
31 * create the RTP stream if it is the first RTP packet in the stream.
32 * - Each new RTP stream will be added to a list of RTP streams, called
33 * rtp_streams_list
34 * - When the user clicks "Player" in the VoipCall dialogue,
35 * rtp_player_init() is called.
36 * - rtp_player_init() creates the main dialog, and it calls:
37 * + mark_rtp_stream_to_play() to mark the RTP streams that needs to be
38 * displayed. These are the RTP streams that match the selected calls in
39 * the VoipCall dlg.
40 * + decode_rtp_stream() this will decode the RTP packets in each RTP
41 * stream, and will also create the RTP channels. An RTP channel is a
42 * group of RTP streams that have in common the source and destination
43 * IP and UDP ports. The RTP channels is what the user will listen in
44 * one of the two Audio channels.
45 * The RTP channels are stored in the hash table rtp_channels_hash
46 * + add_channel_to_window() will create and add the Audio graphic
47 * representation in the main window
48 * - When the user clicks the check box to listen one of the Audio channels,
49 * the structure rtp_channels is filled to play one or two RTP channels
50 * (a max of two channels can be listened at a given moment)
54 #include "config.h"
56 #ifdef HAVE_LIBPORTAUDIO
57 #include <math.h>
58 #include <string.h>
59 #include <portaudio.h>
61 #include <gtk/gtk.h>
63 #include <epan/stats_tree.h>
64 #include <epan/addr_resolv.h>
65 #include <epan/dissectors/packet-rtp.h>
66 #include <epan/rtp_pt.h>
67 #include <epan/codecs.h>
68 #include <epan/prefs.h>
70 #include "../globals.h"
71 #include "../codecs/G711a/G711adecode.h"
72 #include "../codecs/G711u/G711udecode.h"
74 #include "ui/gtk/gui_utils.h"
75 #include "ui/gtk/dlg_utils.h"
76 #include "ui/gtk/graph_analysis.h"
77 #include "ui/gtk/voip_calls_dlg.h"
78 #include "ui/gtk/voip_calls.h"
79 #include "ui/gtk/gtkglobals.h"
80 #include "ui/gtk/rtp_player.h"
81 #include "ui/gtk/stock_icons.h"
83 #include "ui/gtk/old-gtk-compat.h"
84 #include "ui/gtk/gui_utils.h"
86 /*define this symbol to compile with G729 and G723 codecs*/
87 /*#define HAVE_G729_G723 1*/
89 #ifdef HAVE_G729_G723
90 #include "codecs/G729/G729decode.h"
91 #include "codecs/G723/G723decode.h"
92 #endif /* HAVE_G729_G723 */
94 static gboolean initialized = FALSE;
96 static voip_calls_tapinfo_t *voip_calls = NULL;
98 /* Hash table with all the RTP streams */
99 static GHashTable* rtp_streams_hash = NULL;
101 /* List with all the RTP streams (this is used to decode them as it is sorted)*/
102 static GList* rtp_streams_list = NULL;
104 /* the window */
105 static GtkWidget *rtp_player_dlg_w;
106 static GtkWidget *channels_vb;
107 static GtkWidget *main_scrolled_window = NULL;
108 static GtkWidget *jitter_spinner;
109 static GtkWidget *cb_use_rtp_timestamp;
110 static GtkWidget *cb_view_as_time_of_day;
111 static GtkWidget *bt_decode;
112 static GtkWidget *bt_play;
113 static GtkWidget *bt_pause;
114 static GtkWidget *bt_stop;
115 static GtkWidget *progress_bar;
116 static GtkWidget *info_bar;
117 static GtkWidget *stat_hbox;
119 static guint32 total_packets;
120 static guint32 total_frames;
121 static guint32 progbar_count;
123 static int new_jitter_buff;
125 /* a hash table with the RTP streams to play per audio channel */
126 static GHashTable *rtp_channels_hash = NULL;
128 static int sample_rate = 8000;
130 /* Port Audio stuff */
131 static int output_channels = 2;
133 #define PA_SAMPLE_TYPE paInt16
134 typedef gint16 SAMPLE;
135 #define SAMPLE_SILENCE (0)
136 #define FRAMES_PER_BUFFER (512)
138 typedef struct _sample_t {
139 SAMPLE val;
140 guint8 status;
141 } sample_t;
143 #define S_NORMAL 0
144 #define S_DROP_BY_JITT 1
145 #define S_WRONG_SEQ 2
146 #define S_WRONG_TIMESTAMP 3 /* The timestamp does not reflect the number of samples - samples have been dropped or silence inserted to match timestamp */
147 #define S_SILENCE 4 /* Silence inserted by Wireshark, rather than contained in a packet */
149 /* Display channels constants */
150 #define MULT 80
151 #define CHANNEL_WIDTH 500
152 #define CHANNEL_HEIGHT 100
153 #define MAX_TIME_LABEL 10
154 #define HEIGHT_TIME_LABEL 18
155 #define MAX_NUM_COL_CONV 10
157 #if PORTAUDIO_API_1
158 static PortAudioStream *pa_stream;
159 #else /* PORTAUDIO_API_1 */
160 static PaStream *pa_stream;
161 #endif /* PORTAUDIO_API_1 */
163 /* defines a RTP stream */
164 typedef struct _rtp_stream_info {
165 address src_addr;
166 guint16 src_port;
167 address dest_addr;
168 guint16 dest_port;
169 guint32 ssrc;
170 guint32 first_frame_number; /* first RTP frame for the stream */
171 double start_time; /* RTP stream start time in ms */
172 nstime_t start_time_abs;
173 gboolean play;
174 guint16 call_num;
175 GList* rtp_packets_list; /* List of RTP packets in the stream */
176 guint32 num_packets;
177 } rtp_stream_info_t;
180 /* defines the RTP streams to be played in an audio channel */
181 typedef struct _rtp_channel_info {
182 double start_time; /* RTP stream start time in ms */
183 nstime_t start_time_abs;
184 double end_time; /* RTP stream end time in ms */
185 GArray *samples; /* the array with decoded audio */
186 guint16 call_num;
187 gboolean selected;
188 unsigned long frame_index;
189 guint32 drop_by_jitter_buff;
190 guint32 out_of_seq;
191 guint32 wrong_timestamp;
192 guint32 max_frame_index;
193 GtkWidget *check_bt;
194 GtkWidget *separator;
195 GtkWidget *scroll_window;
196 GtkWidget *draw_area;
197 #if GTK_CHECK_VERSION(2,22,0)
198 cairo_surface_t *surface;
199 #else
200 GdkPixmap *pixmap;
201 #endif
202 GtkAdjustment *h_scrollbar_adjustment;
203 GdkPixbuf* cursor_pixbuf;
204 #if PORTAUDIO_API_1
205 PaTimestamp cursor_prev;
206 #else /* PORTAUDIO_API_1 */
207 PaTime cursor_prev;
208 #endif /* PORTAUDIO_API_1 */
209 GdkRGBA bg_color[MAX_NUM_COL_CONV+1];
210 gboolean cursor_catch;
211 rtp_stream_info_t *first_stream; /* This is the first RTP stream in the channel */
212 guint32 num_packets;
213 } rtp_channel_info_t;
215 /* defines a RTP packet */
216 typedef struct _rtp_packet {
217 struct _rtp_info *info; /* the RTP dissected info */
218 double arrive_offset; /* arrive offset time since the beginning of the stream in ms */
219 guint8* payload_data;
220 } rtp_packet_t;
222 /* defines the two RTP channels to be played */
223 typedef struct _rtp_play_channles {
224 rtp_channel_info_t* rci[2]; /* Channels to be played */
225 guint32 start_index[2];
226 guint32 end_index[2];
227 int channel;
228 guint32 max_frame_index;
229 unsigned long frame_index;
230 gboolean pause;
231 gboolean stop;
232 unsigned long pause_duration;
233 #if PORTAUDIO_API_1
234 PaTimestamp out_diff_time;
235 #else /* PORTAUDIO_API_1 */
236 PaTime out_diff_time;
237 PaTime pa_start_time;
238 #endif /* PORTAUDIO_API_1 */
239 } rtp_play_channels_t;
241 /* The two RTP channels to play */
242 static rtp_play_channels_t *rtp_channels = NULL;
244 typedef struct _rtp_decoder_t {
245 codec_handle_t handle;
246 void *context;
247 } rtp_decoder_t;
250 /****************************************************************************/
251 static void
252 rtp_key_destroy(gpointer key)
254 g_free(key);
255 key = NULL;
258 /****************************************************************************/
259 static void
260 rtp_channel_value_destroy(gpointer rci_arg)
262 rtp_channel_info_t *rci = (rtp_channel_info_t *)rci_arg;
264 g_array_free(rci->samples, TRUE);
265 g_free(rci);
266 rci = NULL;
269 /****************************************************************************/
270 static void
271 rtp_stream_value_destroy(gpointer rsi_arg)
273 rtp_stream_info_t *rsi = (rtp_stream_info_t *)rsi_arg;
274 GList* rtp_packets_list;
275 rtp_packet_t *rp;
277 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
278 while (rtp_packets_list)
280 rp = (rtp_packet_t *)rtp_packets_list->data;
282 g_free(rp->info);
283 g_free(rp->payload_data);
284 g_free(rp);
285 rp = NULL;
287 rtp_packets_list = g_list_next(rtp_packets_list);
289 g_free((void *)(rsi->src_addr.data));
290 g_free((void *)(rsi->dest_addr.data));
291 g_free(rsi);
292 rsi = NULL;
295 /****************************************************************************/
296 static void
297 rtp_decoder_value_destroy(gpointer dec_arg)
299 rtp_decoder_t *dec = (rtp_decoder_t *)dec_arg;
301 if (dec->handle)
302 codec_release(dec->handle, dec->context);
303 g_free(dec_arg);
306 /****************************************************************************/
307 static void
308 set_sensitive_check_bt(gchar *key _U_ , rtp_channel_info_t *rci, guint *stop_p )
310 gtk_widget_set_sensitive(rci->check_bt, !(*stop_p));
313 /****************************************************************************/
314 static void
315 bt_state(gboolean decode, gboolean play_state, gboolean pause_state, gboolean stop_state)
317 gboolean new_jitter_value = FALSE;
318 gboolean false_val = FALSE;
320 gtk_widget_set_sensitive(bt_decode, decode);
321 gtk_widget_set_sensitive(cb_use_rtp_timestamp, decode);
322 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
323 gtk_widget_set_sensitive(jitter_spinner, FALSE);
324 } else {
325 gtk_widget_set_sensitive(jitter_spinner, decode);
328 if (new_jitter_buff != (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner)) {
329 new_jitter_value = TRUE;
332 /* set the sensitive state of play only if there is a channel selected */
333 if ( play_state && (rtp_channels->rci[0] || rtp_channels->rci[1]) && !new_jitter_value) {
334 gtk_widget_set_sensitive(bt_play, TRUE);
335 } else {
336 gtk_widget_set_sensitive(bt_play, FALSE);
339 if (!new_jitter_value) {
340 gtk_widget_set_sensitive(bt_pause, pause_state);
341 gtk_widget_set_sensitive(bt_stop, stop_state);
343 /* Set sensitive to the check buttons based on the STOP state */
344 if (rtp_channels_hash)
345 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &stop_state);
346 } else {
347 gtk_widget_set_sensitive(bt_pause, FALSE);
348 gtk_widget_set_sensitive(bt_stop, FALSE);
350 if (rtp_channels_hash)
351 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &false_val);
355 /****************************************************************************/
356 void
357 add_rtp_packet(const struct _rtp_info *rtp_info, packet_info *pinfo)
359 rtp_stream_info_t *stream_info = NULL;
360 rtp_packet_t *new_rtp_packet;
361 GString *key_str = NULL;
363 /* create the streams hash if it doen't exist */
364 if (!rtp_streams_hash)
365 rtp_streams_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_stream_value_destroy);
367 /* Create a hash key to lookup in the RTP streams hash table
368 * uses: src_ip:src_port dst_ip:dst_port ssrc
370 key_str = g_string_new("");
371 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(pinfo->src)),
372 pinfo->srcport, get_addr_name(&(pinfo->dst)),
373 pinfo->destport, rtp_info->info_sync_src );
375 /* lookup for this RTP packet in the stream hash table */
376 stream_info = (rtp_stream_info_t *)g_hash_table_lookup( rtp_streams_hash, key_str->str);
378 /* if it is not in the hash table, create a new stream */
379 if (stream_info==NULL) {
380 stream_info = g_new(rtp_stream_info_t,1);
381 COPY_ADDRESS(&(stream_info->src_addr), &(pinfo->src));
382 stream_info->src_port = pinfo->srcport;
383 COPY_ADDRESS(&(stream_info->dest_addr), &(pinfo->dst));
384 stream_info->dest_port = pinfo->destport;
385 stream_info->ssrc = rtp_info->info_sync_src;
386 stream_info->rtp_packets_list = NULL;
387 stream_info->first_frame_number = pinfo->fd->num;
388 stream_info->start_time = nstime_to_msec(&pinfo->rel_ts);
389 stream_info->start_time_abs = pinfo->fd->abs_ts;
390 stream_info->call_num = 0;
391 stream_info->play = FALSE;
392 stream_info->num_packets = 0;
394 g_hash_table_insert(rtp_streams_hash, g_strdup(key_str->str), stream_info);
396 /* Add the element to the List too. The List is used to decode the packets because it is sorted */
397 rtp_streams_list = g_list_append(rtp_streams_list, stream_info);
400 /* increment the number of packets in this stream, this is used for the progress bar and statistics */
401 stream_info->num_packets++;
403 /* Add the RTP packet to the list */
404 new_rtp_packet = g_new(rtp_packet_t,1);
405 new_rtp_packet->info = (struct _rtp_info *)g_malloc(sizeof(struct _rtp_info));
407 memcpy(new_rtp_packet->info, rtp_info, sizeof(struct _rtp_info));
408 new_rtp_packet->arrive_offset = nstime_to_msec(&pinfo->rel_ts) - stream_info->start_time;
409 /* copy the RTP payload to the rtp_packet to be decoded later */
410 if (rtp_info->info_all_data_present && (rtp_info->info_payload_len != 0)) {
411 new_rtp_packet->payload_data = (guint8 *)g_malloc(rtp_info->info_payload_len);
412 memcpy(new_rtp_packet->payload_data, &(rtp_info->info_data[rtp_info->info_payload_offset]), rtp_info->info_payload_len);
413 } else {
414 new_rtp_packet->payload_data = NULL;
417 stream_info->rtp_packets_list = g_list_append(stream_info->rtp_packets_list, new_rtp_packet);
419 g_string_free(key_str, TRUE);
422 /****************************************************************************/
423 /* Mark the RTP stream to be played. Use the voip_calls graph to see if the
424 * setup_frame is there and then if the associated voip_call is selected.
426 static void
427 mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
429 GList* graph_list;
430 seq_analysis_item_t *graph_item;
431 GList* voip_calls_list;
432 voip_calls_info_t *tmp_voip_call;
434 /* Reset the "to be play" value because the user can close and reopen the RTP Player window
435 * and the streams are not reset in that case
437 rsi->play = FALSE;
439 /* and associate the RTP stream with a call using the first RTP packet in the stream */
440 graph_list = g_list_first(voip_calls->graph_analysis->list);
441 while (graph_list)
443 graph_item = (seq_analysis_item_t *)graph_list->data;
444 if (rsi->first_frame_number == graph_item->fd->num) {
445 rsi->call_num = graph_item->conv_num;
446 /* if it is in the graph list, then check if the voip_call is selected */
447 voip_calls_list = g_list_first(voip_calls->callsinfo_list);
448 while (voip_calls_list)
450 tmp_voip_call = (voip_calls_info_t *)voip_calls_list->data;
451 if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
452 rsi->play = TRUE;
453 total_packets += rsi->num_packets;
454 break;
456 voip_calls_list = g_list_next(voip_calls_list);
458 break;
460 graph_list = g_list_next(graph_list);
464 /****************************************************************************/
465 /* Mark the ALL RTP stream to be played. This is called when calling the
466 * RTP player from the "RTP Analysis" window
468 static void
469 mark_all_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
471 rsi->play = TRUE;
472 total_packets += rsi->num_packets;
475 /****************************************************************************/
476 /* Decode a RTP packet
477 * Return the number of decoded bytes
479 static int
480 decode_rtp_packet(rtp_packet_t *rp, SAMPLE **out_buff, GHashTable *decoders_hash)
482 unsigned int payload_type;
483 const gchar *p;
484 rtp_decoder_t *decoder;
485 SAMPLE *tmp_buff = NULL;
486 int tmp_buff_len;
487 int decoded_bytes = 0;
489 if ((rp->payload_data == NULL) || (rp->info->info_payload_len == 0) ) {
490 return 0;
493 payload_type = rp->info->info_payload_type;
495 /* Look for registered codecs */
496 decoder = (rtp_decoder_t *)g_hash_table_lookup(decoders_hash, GUINT_TO_POINTER(payload_type));
497 if (!decoder) { /* Put either valid or empty decoder into the hash table */
498 decoder = g_new(rtp_decoder_t,1);
499 decoder->handle = NULL;
500 decoder->context = NULL;
501 p = try_val_to_str_ext(payload_type, &rtp_payload_type_short_vals_ext);
502 if (p) {
503 decoder->handle = find_codec(p);
504 if (decoder->handle)
505 decoder->context = codec_init(decoder->handle);
507 g_hash_table_insert(decoders_hash, GUINT_TO_POINTER(payload_type), decoder);
509 if (decoder->handle) { /* Decode with registered codec */
510 tmp_buff_len = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, NULL, NULL);
511 tmp_buff = (SAMPLE *)g_malloc(tmp_buff_len);
512 decoded_bytes = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, tmp_buff, &tmp_buff_len);
513 *out_buff = tmp_buff;
514 return decoded_bytes;
517 /* Try to decode with built-in codec */
519 switch (payload_type) {
521 case PT_PCMU: /* G.711 u-law */
522 tmp_buff = (SAMPLE *)g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
523 decodeG711u(rp->payload_data, rp->info->info_payload_len,
524 tmp_buff, &decoded_bytes);
525 break;
527 case PT_PCMA: /* G.711 A-law */
528 tmp_buff = (SAMPLE *)g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
529 decodeG711a(rp->payload_data, rp->info->info_payload_len,
530 tmp_buff, &decoded_bytes);
531 break;
533 #ifdef HAVE_G729_G723
534 case PT_G729: /* G.729 */
535 /* G729 8kbps => 64kbps/8kbps = 8 */
536 /* Compensate for possible 2 octet SID frame (G.729B) */
537 tmp_buff = g_malloc(sizeof(SAMPLE) * ((rp->info->info_payload_len + 8) / 10) * 80);
538 decodeG729(rp->payload_data, rp->info->info_payload_len,
539 tmp_buff, &decoded_bytes);
540 break;
542 case PT_G723: /* G.723 */
543 if (rp->info->info_payload_len%24 == 0) /* G723 High 6.4kbps */
544 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 10); /* G723 High 64kbps/6.4kbps = 10 */
545 else if (rp->info->info_payload_len%20 == 0) /* G723 Low 5.3kbps */
546 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 13); /* G723 High 64kbps/5.3kbps = 13 */
547 else {
548 return 0;
550 decodeG723(rp->payload_data, rp->info->info_payload_len,
551 tmp_buff, &decoded_bytes);
552 break;
553 #endif /* HAVE_G729_G723 */
555 default:
557 * XXX - return an error here, so the user gets told that
558 * we don't support this codec!
560 break;
563 *out_buff = tmp_buff;
564 return decoded_bytes;
567 /****************************************************************************/
568 static void
569 update_progress_bar(gfloat fraction)
572 if (GTK_IS_PROGRESS_BAR(progress_bar))
573 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), fraction);
575 /* Force gtk to redraw the window before starting decoding the packet */
576 while (gtk_events_pending())
577 gtk_main_iteration();
580 /****************************************************************************/
581 /* Decode the RTP streams and add them to the RTP channels struct
583 static void
584 decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr _U_)
586 GString *key_str = NULL;
587 rtp_channel_info_t *rci;
588 gboolean first = TRUE;
589 GList* rtp_packets_list;
590 rtp_packet_t *rp;
592 int i;
593 double rtp_time;
594 double rtp_time_prev;
595 double arrive_time;
596 double arrive_time_prev;
597 double start_time;
598 double start_rtp_time = 0;
599 double diff;
600 double pack_period;
601 gint32 silence_frames;
602 int seq;
603 #ifdef DEBUG /* XXX: "mean_delay" and "variation" are always zero */
604 double total_time;
605 double mean_delay;
606 double variation;
607 #endif
609 int decoded_bytes;
610 int decoded_bytes_prev;
611 int jitter_buff;
612 SAMPLE *out_buff = NULL;
613 sample_t silence;
614 sample_t sample;
615 guint8 status;
616 guint32 start_timestamp;
617 GHashTable *decoders_hash = NULL;
619 guint32 progbar_nextstep;
620 int progbar_quantum;
621 gfloat progbar_val;
623 silence.val = 0;
624 silence.status = S_NORMAL;
626 /* skip it if we are not going to play it */
627 if (rsi->play == FALSE) {
628 return;
631 /* get the static jitter buffer from the spinner gui */
632 jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
634 /* Create a hash key to lookup in the RTP channels hash
635 * uses: src_ip:src_port dst_ip:dst_port call_num
637 key_str = g_string_new("");
638 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(rsi->src_addr)),
639 rsi->src_port, get_addr_name(&(rsi->dest_addr)),
640 rsi->dest_port, rsi->call_num );
642 /* create the rtp_channels_hash table if it doesn't exist */
643 if (!rtp_channels_hash) {
644 rtp_channels_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_channel_value_destroy);
647 /* lookup for this stream in the channel hash table */
648 rci = (rtp_channel_info_t *)g_hash_table_lookup( rtp_channels_hash, key_str->str);
650 /* ..if it is not in the hash, create an entry */
651 if (rci == NULL) {
652 rci = g_new(rtp_channel_info_t,1);
653 rci->call_num = rsi->call_num;
654 rci->start_time = rsi->start_time;
655 rci->start_time_abs = rsi->start_time_abs;
656 rci->end_time = rsi->start_time;
657 rci->selected = FALSE;
658 rci->frame_index = 0;
659 rci->drop_by_jitter_buff = 0;
660 rci->out_of_seq = 0;
661 rci->wrong_timestamp = 0;
662 rci->max_frame_index = 0;
663 rci->samples = g_array_new (FALSE, FALSE, sizeof(sample_t));
664 rci->check_bt = NULL;
665 rci->separator = NULL;
666 rci->draw_area = NULL;
667 #if GTK_CHECK_VERSION(2,22,0)
668 rci->surface = NULL;
669 #else
670 rci->pixmap = NULL;
671 #endif
672 rci->h_scrollbar_adjustment = NULL;
673 rci->cursor_pixbuf = NULL;
674 rci->cursor_prev = 0;
675 rci->cursor_catch = FALSE;
676 rci->first_stream = rsi;
677 rci->num_packets = rsi->num_packets;
678 g_hash_table_insert(rtp_channels_hash, g_strdup(key_str->str), rci);
679 } else {
680 /* Add silence between the two streams if needed */
681 silence_frames = (gint32)( ((rsi->start_time - rci->end_time)/1000)*sample_rate );
682 for (i = 0; i< silence_frames; i++) {
683 g_array_append_val(rci->samples, silence);
685 rci->num_packets += rsi->num_packets;
688 /* decode the RTP stream */
689 first = TRUE;
690 rtp_time_prev = 0;
691 decoded_bytes_prev = 0;
692 start_time = 0;
693 arrive_time_prev = 0;
694 pack_period = 0;
695 #ifdef DEBUG /* ?? */
696 total_time = 0;
697 mean_delay = 0;
698 variation = 0;
699 #endif
700 seq = 0;
701 start_timestamp = 0;
702 decoders_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rtp_decoder_value_destroy);
704 /* we update the progress bar 100 times */
706 /* Update the progress bar when it gets to this value. */
707 progbar_nextstep = 0;
708 /* When we reach the value that triggers a progress bar update,
709 bump that value by this amount. */
710 progbar_quantum = total_packets/100;
712 status = S_NORMAL;
714 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
715 while (rtp_packets_list)
718 if (progbar_count >= progbar_nextstep) {
719 g_assert(total_packets > 0);
721 progbar_val = (gfloat) progbar_count / total_packets;
723 update_progress_bar(progbar_val);
725 progbar_nextstep += progbar_quantum;
729 rp = (rtp_packet_t *)rtp_packets_list->data;
730 if (first == TRUE) {
731 start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
732 start_rtp_time = 0;
733 rtp_time_prev = start_rtp_time;
734 first = FALSE;
735 seq = rp->info->info_seq_num - 1;
738 decoded_bytes = decode_rtp_packet(rp, &out_buff, decoders_hash);
739 if (decoded_bytes == 0) {
740 seq = rp->info->info_seq_num;
743 rtp_time = (double)(rp->info->info_timestamp-start_timestamp)/sample_rate - start_rtp_time;
745 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
746 arrive_time = rtp_time;
747 } else {
748 arrive_time = (double)rp->arrive_offset/1000 - start_time;
751 if (rp->info->info_seq_num != seq+1){
752 rci->out_of_seq++;
753 status = S_WRONG_SEQ;
755 seq = rp->info->info_seq_num;
757 diff = arrive_time - rtp_time;
759 if (diff<0) diff = -diff;
761 #ifdef DEBUG
762 total_time = (double)rp->arrive_offset/1000;
763 printf("seq = %d arr = %f abs_diff = %f index = %d tim = %f ji=%d jb=%f\n",rp->info->info_seq_num,
764 total_time, diff, rci->samples->len, ((double)rci->samples->len / sample_rate - total_time) * 1000, 0,
765 (mean_delay + 4 * variation) * 1000);
766 fflush(stdout);
767 #endif
768 /* if the jitter buffer was exceeded */
769 if ( diff*1000 > jitter_buff ) {
770 #ifdef DEBUG
771 printf("Packet drop by jitter buffer exceeded\n");
772 #endif
773 rci->drop_by_jitter_buff++;
774 status = S_DROP_BY_JITT;
776 /* if there was a silence period (more than two packetization period) resync the source */
777 if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
778 #ifdef DEBUG
779 printf("Resync...\n");
780 #endif
781 silence_frames = (gint32)((arrive_time - arrive_time_prev)*sample_rate - decoded_bytes_prev/2);
783 /* Fix for bug 4119/5902: don't insert too many silence frames.
784 * XXX - is there a better thing to do here?
786 #define MAX_SILENCE_FRAMES 240000
787 if (silence_frames > MAX_SILENCE_FRAMES)
788 silence_frames = MAX_SILENCE_FRAMES;
790 for (i = 0; i< silence_frames; i++) {
791 silence.status = status;
792 g_array_append_val(rci->samples, silence);
794 /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
795 status = S_NORMAL;
798 decoded_bytes_prev = 0;
799 start_timestamp = rp->info->info_timestamp; /* defined start_timestamp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
800 start_rtp_time = 0;
801 start_time = (double)rp->arrive_offset/1000;
802 rtp_time_prev = 0;
804 } else {
805 /* Add silence if it is necessary */
806 silence_frames = (gint32)((rtp_time - rtp_time_prev)*sample_rate - decoded_bytes_prev/2);
807 if (silence_frames != 0) {
808 rci->wrong_timestamp++;
809 status = S_WRONG_TIMESTAMP;
812 /* Fix for bug 4119/5902: don't insert too many silence frames.
813 * XXX - is there a better thing to do here?
815 if (silence_frames > MAX_SILENCE_FRAMES)
816 silence_frames = MAX_SILENCE_FRAMES;
818 for (i = 0; i< silence_frames; i++) {
819 silence.status = status;
820 g_array_append_val(rci->samples, silence);
822 /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
823 status = S_NORMAL;
827 if (silence_frames > 0) {
828 silence_frames = 0;
830 /* Add the audio */
831 for (i = - silence_frames; i< (decoded_bytes/2); i++) {
832 sample.val = out_buff[i];
833 sample.status = status;
834 g_array_append_val(rci->samples, sample);
835 status = S_NORMAL;
838 rtp_time_prev = rtp_time;
839 pack_period = (double)(decoded_bytes/2)/sample_rate;
840 decoded_bytes_prev = decoded_bytes;
841 arrive_time_prev = arrive_time;
844 if (out_buff) {
845 g_free(out_buff);
846 out_buff = NULL;
848 rtp_packets_list = g_list_next (rtp_packets_list);
849 progbar_count++;
851 rci->max_frame_index = rci->samples->len;
852 rci->end_time = rci->start_time + ((double)rci->samples->len/sample_rate)*1000;
854 g_string_free(key_str, TRUE);
855 g_hash_table_destroy(decoders_hash);
858 /****************************************************************************/
859 static gint
860 h_scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
862 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
863 rci->cursor_catch = TRUE;
864 return TRUE;
867 static gboolean draw_cursors(gpointer data);
869 /****************************************************************************/
870 static void
871 stop_channels(void)
873 PaError err;
874 GtkWidget *dialog;
876 /* we should never be here if we are already in STOP */
877 g_assert(rtp_channels->stop == FALSE);
879 rtp_channels->stop = TRUE;
880 /* force a draw_cursor to stop it */
881 draw_cursors(NULL);
883 err = Pa_StopStream(pa_stream);
885 if( err != paNoError ) {
886 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
887 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
888 "Can not Stop Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
889 gtk_dialog_run (GTK_DIALOG (dialog));
890 gtk_widget_destroy (dialog);
891 return;
894 err = Pa_CloseStream(pa_stream);
895 if( err != paNoError ) {
896 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
897 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
898 "Can not Close Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
899 gtk_dialog_run (GTK_DIALOG (dialog));
900 gtk_widget_destroy (dialog);
901 return;
903 pa_stream = NULL; /* to catch errors better */
905 rtp_channels->start_index[0] = 0;
906 rtp_channels->start_index[1] = 0;
907 rtp_channels->end_index[0] = 0;
908 rtp_channels->end_index[1] = 0;
909 rtp_channels->max_frame_index = 0;
910 rtp_channels->frame_index = 0;
911 rtp_channels->pause = FALSE;
912 rtp_channels->pause_duration = 0;
913 rtp_channels->stop = TRUE;
914 rtp_channels->out_diff_time = 10000;
916 if (rtp_channels->rci[0]) rtp_channels->rci[0]->frame_index = 0;
917 if (rtp_channels->rci[1]) rtp_channels->rci[1]->frame_index = 0;
919 /* set the sensitive state of the buttons (decode, play, pause, stop) */
920 bt_state(TRUE, TRUE, FALSE, FALSE);
924 /****************************************************************************/
925 /* Draw a cursor in a channel graph
927 static void
928 draw_channel_cursor(rtp_channel_info_t *rci, guint32 start_index)
930 #if PORTAUDIO_API_1
931 PaTimestamp idx;
932 #else /* PORTAUDIO_API_1 */
933 PaTime idx;
934 #endif /* PORTAUDIO_API_1 */
935 int i;
936 GtkAllocation widget_alloc;
937 cairo_t *cr;
939 if (!rci) return;
941 #if PORTAUDIO_API_1
942 idx = Pa_StreamTime( pa_stream ) - rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
943 #else /* PORTAUDIO_API_1 */
944 idx = ((guint32)(sample_rate) * (Pa_GetStreamTime(pa_stream)-rtp_channels->pa_start_time))- rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
945 #endif /* PORTAUDIO_API_1 */
948 /* If we finished playing both channels, then stop them */
949 if ( (rtp_channels && (!rtp_channels->stop) && (!rtp_channels->pause)) && (idx > rtp_channels->max_frame_index) ) {
950 stop_channels();
951 return;
954 /* If only this channel finished, then return */
955 if (idx > rci->max_frame_index) {
956 return;
959 gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
960 /* draw the previous saved pixbuf line */
961 if (rci->cursor_pixbuf && (rci->cursor_prev>=0)) {
963 #if GTK_CHECK_VERSION(2,22,0)
964 cr = cairo_create (rci->surface);
965 #else
966 cr = gdk_cairo_create (rci->pixmap);
967 #endif
968 gdk_cairo_set_source_pixbuf (cr, rci->cursor_pixbuf, 0, 0);
969 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
970 cairo_rectangle (cr, rci->cursor_prev/MULT, 0, -1, -1);
971 cairo_fill (cr);
973 #if GTK_CHECK_VERSION(2,22,0)
974 cairo_set_source_surface (cr, rci->surface, idx/MULT, 0);
975 #else
976 gdk_cairo_set_source_pixmap (cr, rci->pixmap,idx/MULT, 0);
977 #endif
978 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
979 cairo_rectangle (cr, rci->cursor_prev/MULT, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
980 cairo_fill (cr);
981 cairo_destroy (cr);
983 g_object_unref(rci->cursor_pixbuf);
984 rci->cursor_pixbuf = NULL;
987 if (idx>0 && (rci->cursor_prev>=0)) {
988 #if GTK_CHECK_VERSION(2,22,0)
989 rci->cursor_pixbuf = gdk_pixbuf_get_from_surface (rci->surface,0, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
990 cr = cairo_create (rci->surface);
991 #else
992 rci->cursor_pixbuf = gdk_pixbuf_get_from_drawable(NULL, rci->pixmap, NULL, (int) (idx/MULT), 0, 0, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
993 cr = gdk_cairo_create (rci->pixmap);
994 #endif
995 if (cr != NULL) {
996 cairo_set_line_width (cr, 1.0);
997 cairo_move_to(cr, idx/MULT, 0);
998 cairo_line_to(cr, idx/MULT, widget_alloc.height-HEIGHT_TIME_LABEL);
999 cairo_stroke(cr);
1000 cairo_destroy(cr);
1003 cr = gdk_cairo_create (gtk_widget_get_window(rci->draw_area));
1004 if (cr != NULL) {
1005 #if GTK_CHECK_VERSION(2,22,0)
1006 cairo_set_source_surface (cr, rci->surface, idx/MULT, 0);
1007 #else
1008 gdk_cairo_set_source_pixmap (cr, rci->pixmap, idx/MULT, 0);
1009 #endif
1010 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
1011 cairo_rectangle (cr, idx/MULT, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
1012 cairo_fill (cr);
1013 cairo_destroy (cr);
1017 /* Disconnect the scroll bar "value" signal to not be called */
1018 g_signal_handlers_disconnect_by_func(rci->h_scrollbar_adjustment, h_scrollbar_changed, rci);
1020 /* Move the horizontal scroll bar */
1021 #if 0
1022 if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
1023 (idx/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
1024 for (i=1; i<10; i++) {
1025 rci->h_scrollbar_adjustment->value += rci->h_scrollbar_adjustment->page_size/10;
1026 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1029 #endif
1030 if (!rci->cursor_catch) {
1031 if (idx/MULT < gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2) {
1032 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, gtk_adjustment_get_lower(rci->h_scrollbar_adjustment));
1033 } else if (idx/MULT > (gtk_adjustment_get_upper(rci->h_scrollbar_adjustment) - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2)) {
1034 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, gtk_adjustment_get_upper(rci->h_scrollbar_adjustment) - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment));
1035 } else {
1036 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, idx/MULT - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2);
1039 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1040 } else if ( (rci->cursor_prev/MULT < gtk_adjustment_get_value(rci->h_scrollbar_adjustment)+gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) &&
1041 (idx/MULT >= gtk_adjustment_get_value(rci->h_scrollbar_adjustment) + gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) ){
1042 rci->cursor_catch = FALSE;
1043 for (i=1; i<10; i++) {
1044 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, MIN(gtk_adjustment_get_upper(rci->h_scrollbar_adjustment)-gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment), gtk_adjustment_get_value(rci->h_scrollbar_adjustment) + gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/20));
1045 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1049 /* Connect back the "value" scroll signal */
1050 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
1052 #if 0
1053 if (idx/MULT < rci->h_scrollbar_adjustment->page_increment) {
1054 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
1055 } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size + rci->h_scrollbar_adjustment->page_increment)) {
1056 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1057 } else {
1058 if ( (idx/MULT < rci->h_scrollbar_adjustment->value) || (idx/MULT > (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
1059 rci->h_scrollbar_adjustment->value = idx/MULT;
1062 #endif
1064 #if 0
1065 if (idx/MULT < rci->h_scrollbar_adjustment->page_size/2) {
1066 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
1067 } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
1068 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1069 } else {
1070 rci->h_scrollbar_adjustment->value = idx/MULT - rci->h_scrollbar_adjustment->page_size/2;
1072 #endif
1074 #if 0
1075 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1076 #endif
1077 rci->cursor_prev = idx;
1080 /****************************************************************************/
1081 /* Move and draw the cursor in the graph
1083 static gboolean
1084 draw_cursors(gpointer data _U_)
1086 if (!rtp_channels) return FALSE;
1088 /* Draw and move each of the two channels */
1089 draw_channel_cursor(rtp_channels->rci[0], rtp_channels->start_index[0]);
1090 draw_channel_cursor(rtp_channels->rci[1], rtp_channels->start_index[1]);
1092 if ((rtp_channels->stop) || (rtp_channels->pause)) return FALSE;
1094 return TRUE;
1097 /****************************************************************************/
1098 static void
1099 init_rtp_channels_vals(void)
1101 rtp_play_channels_t *rpci = rtp_channels;
1103 /* if we only have one channel to play, we just use the info from that channel */
1104 if (rpci->rci[0] == NULL) {
1105 rpci->max_frame_index = rpci->rci[1]->max_frame_index;
1106 rpci->start_index[0] = rpci->max_frame_index;
1107 rpci->start_index[1] = 0;
1108 rpci->end_index[0] = rpci->max_frame_index;
1109 rpci->end_index[1] = rpci->max_frame_index;
1110 } else if (rpci->rci[1] == NULL) {
1111 rpci->max_frame_index = rpci->rci[0]->max_frame_index;
1112 rpci->start_index[1] = rpci->max_frame_index;
1113 rpci->start_index[0] = 0;
1114 rpci->end_index[0] = rpci->max_frame_index;
1115 rpci->end_index[1] = rpci->max_frame_index;
1117 /* if the two channels are to be played, then we need to sync both based on the start/end time of each one */
1118 } else {
1119 rpci->max_frame_index = (guint32)(sample_rate/1000) * (guint32)(MAX(rpci->rci[0]->end_time, rpci->rci[1]->end_time) -
1120 (guint32)MIN(rpci->rci[0]->start_time, rpci->rci[1]->start_time));
1122 if (rpci->rci[0]->start_time < rpci->rci[1]->start_time) {
1123 rpci->start_index[0] = 0;
1124 rpci->start_index[1] = (guint32)(sample_rate/1000) * (guint32)(rpci->rci[1]->start_time - rpci->rci[0]->start_time);
1125 } else {
1126 rpci->start_index[1] = 0;
1127 rpci->start_index[0] = (guint32)(sample_rate/1000) * (guint32)(rpci->rci[0]->start_time - rpci->rci[1]->start_time);
1130 if (rpci->rci[0]->end_time < rpci->rci[1]->end_time) {
1131 rpci->end_index[0] = rpci->max_frame_index - ((guint32)(sample_rate/1000) * (guint32)(rpci->rci[1]->end_time - rpci->rci[0]->end_time));
1132 rpci->end_index[1] = rpci->max_frame_index;
1133 } else {
1134 rpci->end_index[1] = rpci->max_frame_index - ((guint32)(sample_rate/1000) * (guint32)(rpci->rci[0]->end_time - rpci->rci[1]->end_time));
1135 rpci->end_index[0] = rpci->max_frame_index;
1141 /****************************************************************************/
1142 /* This routine will be called by the PortAudio engine when audio is needed.
1143 * It may called at interrupt level on some machines so don't do anything
1144 * that could mess up the system like calling malloc() or free().
1146 #if PORTAUDIO_API_1
1148 static int paCallback( void *inputBuffer _U_, void *outputBuffer,
1149 unsigned long framesPerBuffer,
1150 PaTimestamp outTime _U_, void *userData)
1152 #else /* PORTAUDIO_API_1 */
1153 static int paCallback( const void *inputBuffer _U_, void *outputBuffer,
1154 unsigned long framesPerBuffer,
1155 const PaStreamCallbackTimeInfo* outTime _U_,
1156 PaStreamCallbackFlags statusFlags _U_,
1157 void *userData)
1159 #endif /* PORTAUDIO_API_1 */
1160 rtp_play_channels_t *rpci = (rtp_play_channels_t *)userData;
1161 SAMPLE *wptr = (SAMPLE*)outputBuffer;
1162 sample_t sample;
1163 unsigned long i;
1164 int finished;
1165 unsigned long framesLeft;
1166 unsigned long framesToPlay;
1168 /* if it is pasued, we keep the stream running but with silence only */
1169 if (rtp_channels->pause) {
1170 for(i=0; i<framesPerBuffer; i++ ) {
1171 *wptr++ = 0;
1172 *wptr++ = 0;
1174 rtp_channels->pause_duration += framesPerBuffer;
1175 return 0;
1178 #if PORTAUDIO_API_1
1179 rpci->out_diff_time = outTime - Pa_StreamTime(pa_stream) ;
1180 #else /* PORTAUDIO_API_1 */
1181 rpci->out_diff_time = (guint32)(sample_rate) * (outTime->outputBufferDacTime - Pa_GetStreamTime(pa_stream)) ;
1182 #endif /* PORTAUDIO_API_1 */
1185 /* set the values if this is the first time */
1186 if (rpci->max_frame_index == 0) {
1187 init_rtp_channels_vals();
1191 framesLeft = rpci->max_frame_index - rpci->frame_index;
1193 if( framesLeft < framesPerBuffer )
1195 framesToPlay = framesLeft;
1196 finished = 1;
1198 else
1200 framesToPlay = framesPerBuffer;
1201 finished = 0;
1204 for( i=0; i<(unsigned int)framesToPlay; i++ )
1206 if (rpci->rci[0] && ( (rpci->frame_index >= rpci->start_index[0]) && (rpci->frame_index <= rpci->end_index[0]) )) {
1207 sample = g_array_index(rpci->rci[0]->samples, sample_t, rpci->rci[0]->frame_index++);
1208 *wptr++ = sample.val;
1209 } else {
1210 *wptr++ = 0;
1213 if (rpci->rci[1] && ( (rpci->frame_index >= rpci->start_index[1]) && (rpci->frame_index <= rpci->end_index[1]) )) {
1214 sample = g_array_index(rpci->rci[1]->samples, sample_t, rpci->rci[1]->frame_index++);
1215 *wptr++ = sample.val;
1216 } else {
1217 *wptr++ = 0;
1220 for( ; i<framesPerBuffer; i++ )
1222 *wptr++ = 0;
1223 *wptr++ = 0;
1225 rpci->frame_index += framesToPlay;
1227 return finished;
1230 /****************************************************************************/
1231 static void
1232 on_bt_check_clicked(GtkButton *button _U_, gpointer user_data)
1234 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
1236 if (rci->selected) {
1237 if (rtp_channels->rci[0] == rci) {
1238 rtp_channels->rci[0] = NULL;
1239 rtp_channels->channel = 0;
1240 } else {
1241 rtp_channels->rci[1] = NULL;
1242 rtp_channels->channel = 1;
1244 } else {
1245 /* if there are already both channels selected, unselect the old one */
1246 if (rtp_channels->rci[rtp_channels->channel]) {
1247 /* we disconnect the signal temporarly to avoid been called back */
1248 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1249 gtk_toggle_button_set_active((GtkToggleButton *)rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1250 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1251 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1254 rtp_channels->rci[rtp_channels->channel] = rci;
1255 rtp_channels->channel = !(rtp_channels->channel);
1258 rci->selected = !(rci->selected);
1260 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1261 bt_state(TRUE, TRUE, FALSE, FALSE);
1264 /****************************************************************************/
1265 static void channel_draw(rtp_channel_info_t* rci)
1267 int i, imax;
1268 int j;
1269 sample_t sample;
1270 SAMPLE min, max;
1271 PangoLayout *small_layout;
1272 guint32 label_width, label_height;
1273 char label_string[MAX_TIME_LABEL];
1274 double offset;
1275 guint32 progbar_nextstep;
1276 int progbar_quantum;
1277 gfloat progbar_val;
1278 guint status;
1279 GdkRGBA red_color = {1.0, 0.0, 0.1, 1.0}; /* Red */
1280 /*GdkColor amber_color = {0, 65535, 49152, 0};*/
1281 GdkRGBA amber_color = {1.0, 0.75, 0.0, 1.0};
1282 GdkRGBA white_color = {1.0, 1.0, 1.0, 1.0 };
1283 GdkRGBA black_color = {0.0, 0.0, 0.0, 1.0}; /* Black */
1285 GdkRGBA *draw_color_p;
1286 time_t seconds;
1287 struct tm *timestamp;
1288 GtkAllocation widget_alloc;
1289 cairo_t *cr;
1291 #if GTK_CHECK_VERSION(2,22,0)
1292 gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
1293 /* Clear out old plot */
1294 cr = cairo_create (rci->surface);
1295 gdk_cairo_set_source_rgba (cr, &rci->bg_color[1+rci->call_num%MAX_NUM_COL_CONV]);
1296 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1297 cairo_fill (cr);
1298 cairo_destroy (cr);
1299 cr = NULL;
1301 small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
1302 pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
1304 /* calculated the pixel offset to display integer seconds */
1305 offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*sample_rate/MULT;
1307 cr = cairo_create (rci->surface);
1308 cairo_set_line_width (cr, 1.0);
1309 cairo_move_to(cr, 0, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1310 cairo_line_to(cr, widget_alloc.width, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1311 cairo_stroke(cr);
1312 cairo_destroy(cr);
1313 cr = NULL;
1315 imax = MIN(widget_alloc.width,(gint)(rci->samples->len/MULT));
1317 /* we update the progress bar 100 times */
1319 /* Update the progress bar when it gets to this value. */
1320 progbar_nextstep = 0;
1321 /* When we reach the value that triggers a progress bar update,
1322 bump that value by this amount. */
1323 progbar_quantum = imax/100;
1325 for (i=0; i< imax; i++) {
1326 sample.val = 0;
1327 status = S_NORMAL;
1328 max=(SAMPLE)0xFFFF;
1329 min=(SAMPLE)0x7FFF;
1331 if (progbar_count >= progbar_nextstep) {
1332 g_assert(total_frames > 0);
1334 progbar_val = (gfloat) i / imax;
1336 update_progress_bar(progbar_val);
1338 progbar_nextstep += progbar_quantum;
1341 for (j=0; j<MULT; j++) {
1342 sample = g_array_index(rci->samples, sample_t, i*MULT+j);
1343 max = MAX(max, sample.val);
1344 min = MIN(min, sample.val);
1345 if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
1346 if (sample.status == S_WRONG_TIMESTAMP) status = S_WRONG_TIMESTAMP;
1347 if (sample.status == S_SILENCE) status = S_SILENCE;
1350 /* Set the line color, default is black */
1351 if (status == S_DROP_BY_JITT) {
1352 draw_color_p = &red_color;
1353 } else if (status == S_WRONG_TIMESTAMP) {
1354 draw_color_p = &amber_color;
1355 } else if (status == S_SILENCE) {
1356 draw_color_p = &white_color;
1357 } else {
1358 draw_color_p = &black_color;
1361 /* if silence added by Wireshark, graphically show it with letter to indicate why */
1362 if ((status == S_DROP_BY_JITT) || (status == S_WRONG_TIMESTAMP) || (status == S_SILENCE)) {
1363 cr = cairo_create (rci->surface);
1364 cairo_set_line_width (cr, 1.0);
1365 gdk_cairo_set_source_rgba (cr, draw_color_p);
1366 cairo_move_to(cr, i+0.5, 0);
1367 cairo_line_to(cr, i+0.5, (widget_alloc.height-HEIGHT_TIME_LABEL)-1);
1368 cairo_stroke(cr);
1369 cairo_destroy(cr);
1370 cr=NULL;
1372 if (status == S_DROP_BY_JITT) g_snprintf(label_string, MAX_TIME_LABEL,"D");
1373 if (status == S_WRONG_TIMESTAMP) g_snprintf(label_string, MAX_TIME_LABEL, "W");
1374 if (status == S_SILENCE) g_snprintf(label_string, MAX_TIME_LABEL, "S");
1376 pango_layout_set_text(small_layout, label_string, -1);
1377 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1378 cr = cairo_create (rci->surface);
1379 gdk_cairo_set_source_rgba (cr, draw_color_p);
1380 cairo_move_to (cr, i, 0);
1381 pango_cairo_show_layout (cr, small_layout);
1382 cairo_destroy (cr);
1383 cr = NULL;
1384 } else {
1385 /* Draw a graphical representation of the sample */
1386 cr = cairo_create (rci->surface);
1387 cairo_set_line_width (cr, 1.0);
1388 gdk_cairo_set_source_rgba (cr, draw_color_p);
1389 cairo_move_to(cr, i+0.5, ( (0x7FFF+min) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1390 cairo_line_to(cr, i+0.5, ( (0x7FFF+max) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1391 cairo_stroke(cr);
1392 cairo_destroy(cr);
1395 /* Draw the x-axis (seconds since beginning of packet flow for this call) */
1397 /* Draw tick mark and put a number for each whole second */
1398 if ( !((i*MULT)%(sample_rate)) ) {
1399 cr = cairo_create (rci->surface);
1400 cairo_set_line_width (cr, 1.0);
1401 cairo_move_to(cr, i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1402 cairo_line_to(cr, i+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+4);
1403 cairo_stroke(cr);
1404 cairo_destroy(cr);
1405 cr=NULL;
1407 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day))) {
1408 seconds = rci->start_time_abs.secs + i * MULT / sample_rate;
1409 timestamp = localtime(&seconds);
1410 g_snprintf(label_string, MAX_TIME_LABEL, "%02d:%02d:%02d", timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec);
1411 } else {
1412 g_snprintf(label_string, MAX_TIME_LABEL, "%.0f s", floor(rci->start_time/1000) + i*MULT/sample_rate);
1415 pango_layout_set_text(small_layout, label_string, -1);
1416 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1417 cr = cairo_create (rci->surface);
1418 cairo_move_to (cr, i - offset - label_width/2, widget_alloc.height - label_height);
1419 pango_cairo_show_layout (cr, small_layout);
1420 cairo_destroy (cr);
1421 cr = NULL;
1424 /* Draw only a tick mark for half second intervals */
1425 } else if ( !((i*MULT)%(sample_rate/2)) ) {
1426 cr = cairo_create (rci->surface);
1427 cairo_set_line_width (cr, 1.0);
1428 cairo_move_to(cr,i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1429 cairo_line_to(cr, (i - offset)+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+2);
1430 cairo_stroke(cr);
1431 cairo_destroy(cr);
1432 cr=NULL;
1435 progbar_count++;
1437 g_object_unref(G_OBJECT(small_layout));
1438 #else
1439 if (GDK_IS_DRAWABLE(rci->pixmap)) {
1440 gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
1441 /* Clear out old plot */
1442 cr = gdk_cairo_create (rci->pixmap);
1443 gdk_cairo_set_source_rgba (cr, &rci->bg_color[1+rci->call_num%MAX_NUM_COL_CONV]);
1444 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1445 cairo_fill (cr);
1446 cairo_destroy (cr);
1447 cr = NULL;
1449 small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
1450 pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
1452 /* calculated the pixel offset to display integer seconds */
1453 offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*sample_rate/MULT;
1455 cr = gdk_cairo_create (rci->pixmap);
1456 cairo_set_line_width (cr, 1.0);
1457 cairo_move_to(cr, 0, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1458 cairo_line_to(cr, widget_alloc.width, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1459 cairo_stroke(cr);
1460 cairo_destroy(cr);
1461 cr = NULL;
1463 imax = MIN(widget_alloc.width,(gint)(rci->samples->len/MULT));
1465 /* we update the progress bar 100 times */
1467 /* Update the progress bar when it gets to this value. */
1468 progbar_nextstep = 0;
1469 /* When we reach the value that triggers a progress bar update,
1470 bump that value by this amount. */
1471 progbar_quantum = imax/100;
1473 for (i=0; i< imax; i++) {
1474 sample.val = 0;
1475 status = S_NORMAL;
1476 max=(SAMPLE)0xFFFF;
1477 min=(SAMPLE)0x7FFF;
1479 if (progbar_count >= progbar_nextstep) {
1480 g_assert(total_frames > 0);
1482 progbar_val = (gfloat) i / imax;
1484 update_progress_bar(progbar_val);
1486 progbar_nextstep += progbar_quantum;
1489 for (j=0; j<MULT; j++) {
1490 sample = g_array_index(rci->samples, sample_t, i*MULT+j);
1491 max = MAX(max, sample.val);
1492 min = MIN(min, sample.val);
1493 if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
1494 if (sample.status == S_WRONG_TIMESTAMP) status = S_WRONG_TIMESTAMP;
1495 if (sample.status == S_SILENCE) status = S_SILENCE;
1498 /* Set the line color, default is black */
1499 if (status == S_DROP_BY_JITT) {
1500 draw_color_p = &red_color;
1501 } else if (status == S_WRONG_TIMESTAMP) {
1502 draw_color_p = &amber_color;
1503 } else if (status == S_SILENCE) {
1504 draw_color_p = &white_color;
1505 } else {
1506 draw_color_p = &black_color;
1509 /* if silence added by Wireshark, graphically show it with letter to indicate why */
1510 if ((status == S_DROP_BY_JITT) || (status == S_WRONG_TIMESTAMP) || (status == S_SILENCE)) {
1511 cr = gdk_cairo_create (rci->pixmap);
1512 cairo_set_line_width (cr, 1.0);
1513 gdk_cairo_set_source_rgba (cr, draw_color_p);
1514 cairo_move_to(cr, i+0.5, 0);
1515 cairo_line_to(cr, i+0.5, (widget_alloc.height-HEIGHT_TIME_LABEL)-1);
1516 cairo_stroke(cr);
1517 cairo_destroy(cr);
1518 cr=NULL;
1520 if (status == S_DROP_BY_JITT) g_snprintf(label_string, MAX_TIME_LABEL,"D");
1521 if (status == S_WRONG_TIMESTAMP) g_snprintf(label_string, MAX_TIME_LABEL, "W");
1522 if (status == S_SILENCE) g_snprintf(label_string, MAX_TIME_LABEL, "S");
1524 pango_layout_set_text(small_layout, label_string, -1);
1525 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1526 cr = gdk_cairo_create (rci->pixmap);
1527 gdk_cairo_set_source_rgba (cr, draw_color_p);
1528 cairo_move_to (cr, i, 0);
1529 pango_cairo_show_layout (cr, small_layout);
1530 cairo_destroy (cr);
1531 cr = NULL;
1532 } else {
1533 /* Draw a graphical representation of the sample */
1534 cr = gdk_cairo_create (rci->pixmap);
1535 cairo_set_line_width (cr, 1.0);
1536 gdk_cairo_set_source_rgba (cr, draw_color_p);
1537 cairo_move_to(cr, i+0.5, ( (0x7FFF+min) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1538 cairo_line_to(cr, i+0.5, ( (0x7FFF+max) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1539 cairo_stroke(cr);
1540 cairo_destroy(cr);
1543 /* Draw the x-axis (seconds since beginning of packet flow for this call) */
1545 /* Draw tick mark and put a number for each whole second */
1546 if ( !((i*MULT)%(sample_rate)) ) {
1547 cr = gdk_cairo_create (rci->pixmap);
1548 cairo_set_line_width (cr, 1.0);
1549 cairo_move_to(cr, i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1550 cairo_line_to(cr, i+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+4);
1551 cairo_stroke(cr);
1552 cairo_destroy(cr);
1553 cr=NULL;
1555 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day))) {
1556 seconds = rci->start_time_abs.secs + i * MULT / sample_rate;
1557 timestamp = localtime(&seconds);
1558 g_snprintf(label_string, MAX_TIME_LABEL, "%02d:%02d:%02d", timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec);
1559 } else {
1560 g_snprintf(label_string, MAX_TIME_LABEL, "%.0f s", floor(rci->start_time/1000) + i*MULT/sample_rate);
1563 pango_layout_set_text(small_layout, label_string, -1);
1564 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1565 cr = gdk_cairo_create (rci->pixmap);
1566 cairo_move_to (cr, i - offset - label_width/2, widget_alloc.height - label_height);
1567 pango_cairo_show_layout (cr, small_layout);
1568 cairo_destroy (cr);
1569 cr = NULL;
1572 /* Draw only a tick mark for half second intervals */
1573 } else if ( !((i*MULT)%(sample_rate/2)) ) {
1574 cr = gdk_cairo_create (rci->pixmap);
1575 cairo_set_line_width (cr, 1.0);
1576 cairo_move_to(cr,i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1577 cairo_line_to(cr, (i - offset)+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+2);
1578 cairo_stroke(cr);
1579 cairo_destroy(cr);
1580 cr=NULL;
1583 progbar_count++;
1585 g_object_unref(G_OBJECT(small_layout));
1587 #endif
1590 /****************************************************************************/
1591 static gboolean expose_event_channels(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1593 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
1594 cairo_t *cr;
1596 if (gtk_widget_is_drawable(widget)){
1597 cr = gdk_cairo_create (gtk_widget_get_window(widget));
1598 #if GTK_CHECK_VERSION(2,22,0)
1599 cairo_set_source_surface (cr, rci->surface, 0, 0);
1600 #else
1601 gdk_cairo_set_source_pixmap (cr, rci->pixmap, event->area.x, event->area.y);
1602 #endif
1603 cairo_rectangle (cr, event->area.x,event->area.y, event->area.width, event->area.height);
1604 cairo_fill (cr);
1605 cairo_destroy(cr);
1606 cr = NULL;
1609 return FALSE;
1612 /****************************************************************************/
1613 static gboolean
1614 configure_event_channels(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1616 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
1617 int i;
1618 GtkAllocation widget_alloc;
1619 cairo_t *cr;
1621 /* the first color is blue to highlight the selected item
1622 * the other collors are the same as in the Voip Graph analysys
1623 * to match the same calls
1625 #if 0
1626 static GdkColor col[MAX_NUM_COL_CONV+1] = {
1627 {0, 0x00FF, 0x00FF, 0xFFFF},
1628 {0, 0x90FF, 0xEEFF, 0x90FF},
1629 {0, 0xFFFF, 0xA0FF, 0x7AFF},
1630 {0, 0xFFFF, 0xB6FF, 0xC1FF},
1631 {0, 0xFAFF, 0xFAFF, 0xD2FF},
1632 {0, 0xFFFF, 0xFFFF, 0x33FF},
1633 {0, 0x66FF, 0xCDFF, 0xAAFF},
1634 {0, 0xE0FF, 0xFFFF, 0xFFFF},
1635 {0, 0xB0FF, 0xC4FF, 0xDEFF},
1636 {0, 0x87FF, 0xCEFF, 0xFAFF},
1637 {0, 0xD3FF, 0xD3FF, 0xD3FF}
1639 #endif
1641 static GdkRGBA col[MAX_NUM_COL_CONV+1] = {
1642 /* Red, Green, Blue Alpha */
1643 {0.0039, 0.0039, 1.0000, 1.0},
1644 {0.5664, 0.6289, 0.5664, 1.0},
1645 {1.0000, 0.6289, 0.4805, 1.0},
1646 {1.0000, 0.7148, 0.7578, 1.0},
1647 {0.9805, 0.9805, 0.8242, 1.0},
1648 {1.0000, 1.0000, 0.2031, 1.0},
1649 {0.4023, 0.8046, 0.6680, 1.0},
1650 {0.8789, 1.0000, 1.0000, 1.0},
1651 {0.6914, 0.7695, 0.8710, 1.0},
1652 {0.8281, 0.8281, 0.8281, 1.0},
1655 #if GTK_CHECK_VERSION(2,22,0)
1656 if(rci->surface){
1657 cairo_surface_destroy (rci->surface);
1658 rci->surface=NULL;
1660 gtk_widget_get_allocation(widget, &widget_alloc);
1661 rci->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1662 CAIRO_CONTENT_COLOR,
1663 widget_alloc.width,
1664 widget_alloc.height);
1666 cr = cairo_create (rci->surface);
1667 cairo_set_source_rgb (cr, 1, 1, 1);
1668 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1669 cairo_fill (cr);
1670 cairo_destroy (cr);
1671 #else
1672 if(rci->pixmap){
1673 g_object_unref(rci->pixmap);
1674 rci->pixmap=NULL;
1676 gtk_widget_get_allocation(widget, &widget_alloc);
1677 rci->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
1678 widget_alloc.width,
1679 widget_alloc.height,
1680 -1);
1681 if ( GDK_IS_DRAWABLE(rci->pixmap) ){
1682 cr = gdk_cairo_create (rci->pixmap);
1683 cairo_set_source_rgb (cr, 1, 1, 1);
1684 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1685 cairo_fill (cr);
1686 cairo_destroy (cr);
1688 #endif
1690 /* create gc's for the background color of each channel */
1691 for (i=0; i<MAX_NUM_COL_CONV+1; i++){
1692 rci->bg_color[i].alpha=col[i].alpha;
1693 rci->bg_color[i].red=col[i].red;
1694 rci->bg_color[i].green=col[i].green;
1695 rci->bg_color[i].blue=col[i].blue;
1698 channel_draw(rci);
1700 return TRUE;
1703 /****************************************************************************/
1704 static gboolean
1705 button_press_event_channel(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data)
1707 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
1708 int this_channel;
1709 unsigned long prev_index;
1711 if (!rci->selected) {
1713 /* only select a new channels if we are in STOP */
1714 if (!rtp_channels->stop) return 0;
1716 /* if there are already both channels selected, unselect the old one */
1717 if (rtp_channels->rci[rtp_channels->channel]) {
1718 /* we disconnect the signal temporarly to avoid been called back */
1719 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1720 gtk_toggle_button_set_active((GtkToggleButton *) rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1721 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1722 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1725 /* we disconnect the signal temporarly to avoid been called back */
1726 g_signal_handlers_disconnect_by_func(rci->check_bt, on_bt_check_clicked, rci);
1727 gtk_toggle_button_set_active((GtkToggleButton *) rci->check_bt, TRUE);
1728 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1730 rtp_channels->rci[rtp_channels->channel] = rci;
1731 rtp_channels->channel = !(rtp_channels->channel);
1732 rci->selected = TRUE;
1734 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1735 bt_state(TRUE, TRUE, FALSE, FALSE);
1738 if (rci == rtp_channels->rci[0]) {
1739 this_channel = 0;
1740 } else {
1741 this_channel = 1;
1744 rci->frame_index = (unsigned int) (event->x * MULT);
1746 prev_index = rtp_channels->frame_index;
1747 rtp_channels->frame_index = rtp_channels->start_index[this_channel] + rci->frame_index;
1748 rtp_channels->pause_duration += prev_index - rtp_channels->frame_index;
1752 /* change the index in the other channel if selected, according with the index position */
1753 if (rtp_channels->rci[!this_channel]) {
1754 init_rtp_channels_vals();
1756 if (rtp_channels->frame_index < rtp_channels->start_index[!this_channel]) {
1757 rtp_channels->rci[!this_channel]->frame_index = 0;
1758 } else if (rtp_channels->frame_index > rtp_channels->end_index[!this_channel]) {
1759 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->rci[!this_channel]->max_frame_index;
1760 } else {
1761 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->frame_index - rtp_channels->start_index[!this_channel];
1763 } else {
1764 init_rtp_channels_vals();
1767 rtp_channels->out_diff_time = 0;
1769 rci->cursor_catch = TRUE;
1771 /* redraw the cusrsor */
1772 draw_cursors(NULL);
1774 return TRUE;
1777 /****************************************************************************/
1778 static void
1779 add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, guint *counter _U_ )
1781 GString *label;
1782 GtkWidget *viewport;
1785 /* create the channel draw area */
1786 rci->draw_area=gtk_drawing_area_new();
1788 rci->scroll_window=gtk_scrolled_window_new(NULL, NULL);
1790 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (rci->scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
1791 rci->h_scrollbar_adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window));
1794 gtk_widget_set_size_request(rci->draw_area, (gint)(rci->samples->len/MULT), CHANNEL_HEIGHT);
1797 viewport = gtk_viewport_new(rci->h_scrollbar_adjustment, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window)));
1798 gtk_container_add(GTK_CONTAINER(viewport), rci->draw_area);
1799 gtk_container_add(GTK_CONTAINER(rci->scroll_window), viewport);
1800 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1801 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1802 gtk_widget_set_can_focus(rci->draw_area, TRUE);
1803 gtk_widget_grab_focus(rci->draw_area);
1805 gtk_box_pack_start(GTK_BOX (channels_vb), rci->scroll_window, FALSE, FALSE, 0);
1807 /* signals needed to handle backing pixmap */
1808 g_signal_connect(rci->draw_area, "expose_event", G_CALLBACK(expose_event_channels), rci);
1809 g_signal_connect(rci->draw_area, "configure_event", G_CALLBACK(configure_event_channels), rci);
1810 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1811 g_signal_connect(rci->draw_area, "button_press_event", G_CALLBACK(button_press_event_channel), rci);
1812 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
1815 label = g_string_new("");
1816 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
1817 g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Out of Seq: %d(%.1f%%) Wrong Timestamp: %d(%.1f%%)",
1818 get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
1819 get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1820 (double)rci->samples->len/sample_rate,
1821 rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
1822 rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
1823 } else {
1824 g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Drop by Jitter Buff:%d(%.1f%%) Out of Seq: %d(%.1f%%) Wrong Timestamp: %d(%.1f%%)",
1825 get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
1826 get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1827 (double)rci->samples->len/sample_rate,
1828 rci->drop_by_jitter_buff, (double)rci->drop_by_jitter_buff * 100 / (double)rci->num_packets,
1829 rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
1830 rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
1833 rci->check_bt = gtk_check_button_new_with_label(label->str);
1834 gtk_box_pack_start(GTK_BOX (channels_vb), rci->check_bt, FALSE, FALSE, 1);
1836 /* Create the Separator if it is not the last one */
1837 (*counter)++;
1838 if (*counter < g_hash_table_size(rtp_channels_hash)) {
1839 rci->separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1840 gtk_box_pack_start(GTK_BOX (channels_vb), rci->separator, FALSE, FALSE, 5);
1843 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1845 g_string_free(label, TRUE);
1848 /****************************************************************************/
1849 static void
1850 count_channel_frames(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
1852 total_frames += rci->samples->len;
1855 /****************************************************************************/
1856 static void
1857 play_channels(void)
1859 PaError err;
1860 GtkWidget *dialog;
1862 /* we should never be here if we are in PLAY and !PAUSE */
1863 g_assert(rtp_channels->stop || rtp_channels->pause);
1865 /* if we are in PAUSE change the state */
1866 if (rtp_channels->pause) {
1867 rtp_channels->pause = FALSE;
1868 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1869 bt_state(FALSE, FALSE, TRUE, TRUE);
1871 /* if not PAUSE, then start to PLAY */
1872 } else {
1873 #if PORTAUDIO_API_1
1874 err = Pa_OpenStream(
1875 &pa_stream,
1876 paNoDevice, /* default input device */
1877 0, /* no input */
1878 PA_SAMPLE_TYPE, /* 16 bit Integer input */
1879 NULL,
1880 Pa_GetDefaultOutputDeviceID(),
1881 output_channels, /* Stereo output */
1882 PA_SAMPLE_TYPE, /* 16 bit Integer output */
1883 NULL,
1884 sample_rate, /* 8 kHz */
1885 FRAMES_PER_BUFFER,
1886 0, /* number of buffers, if zero then use default minimum */
1887 paClipOff, /* we won't output out of range samples so don't bother clipping them */
1888 paCallback,
1889 rtp_channels );
1891 if( err != paNoError ) {
1892 const char *deviceName = "No Device";
1894 PaDeviceID device = Pa_GetDefaultOutputDeviceID();
1896 if (device != paNoDevice)
1898 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
1899 if (deviceInfo)
1900 deviceName = deviceInfo->name;
1901 else
1902 deviceName = "(No device info)";
1905 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1906 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1907 "Got this info from PortAudio Library:\n"
1908 " Default deviceName: %s (%d)", deviceName, device);
1909 gtk_dialog_run (GTK_DIALOG (dialog));
1910 gtk_widget_destroy (dialog);
1912 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1913 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1914 "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1915 gtk_dialog_run (GTK_DIALOG (dialog));
1916 gtk_widget_destroy (dialog);
1917 return;
1919 #else /* PORTAUDIO_API_1 */
1920 if (Pa_GetDefaultOutputDevice() != paNoDevice) {
1921 err = Pa_OpenDefaultStream(
1922 &pa_stream,
1924 output_channels, /* Stereo output */
1925 PA_SAMPLE_TYPE, /* 16 bit Integer output */
1926 sample_rate, /* 8 kHz */
1927 FRAMES_PER_BUFFER,
1928 paCallback,
1929 rtp_channels );
1930 } else {
1931 /* If the Default Host API doesn't even provide a device
1932 * we might as well go look for another.
1934 PaHostApiIndex host_api_count = Pa_GetHostApiCount();
1935 PaHostApiIndex default_host_api_index = Pa_GetDefaultHostApi();
1937 PaHostApiIndex host_api_index;
1938 const PaHostApiInfo *host_api_info;
1940 for (host_api_index=0; host_api_index<host_api_count; host_api_index++)
1942 /* Skip the default host API, that didn't work before */
1943 if (host_api_index == default_host_api_index)
1944 continue;
1946 /* If we find a host API with a device, then take it. */
1947 host_api_info = Pa_GetHostApiInfo(host_api_index);
1948 if (host_api_info->deviceCount > 0)
1949 break;
1952 if (host_api_index<host_api_count)
1954 PaStreamParameters stream_parameters;
1955 stream_parameters.device = host_api_info->defaultOutputDevice;
1956 stream_parameters.channelCount = output_channels; /* Stereo output */
1957 stream_parameters.sampleFormat = PA_SAMPLE_TYPE; /* 16 bit Integer output */
1958 stream_parameters.suggestedLatency = 0;
1959 stream_parameters.hostApiSpecificStreamInfo = NULL;
1960 #ifdef DEBUG
1961 g_print("Trying Host API: %s\n", host_api_info->name);
1962 #endif
1963 err = Pa_OpenStream(
1964 &pa_stream,
1965 NULL, /* no input */
1966 &stream_parameters,
1967 sample_rate, /* 8 kHz */
1968 FRAMES_PER_BUFFER,
1969 paClipOff, /* we won't output out of range samples so don't bother clipping them */
1970 paCallback,
1971 rtp_channels );
1973 else
1975 err = paNoDevice;
1979 if( err != paNoError ) {
1980 PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
1981 if (hostApi < 0)
1983 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1984 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1985 "Can not even get the default host API from PortAudio Library.\n Error: %s",
1986 Pa_GetErrorText( hostApi ));
1987 gtk_dialog_run (GTK_DIALOG (dialog));
1988 gtk_widget_destroy (dialog);
1990 else
1992 const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( hostApi );
1994 if ( !hostApiInfo )
1996 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1997 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1998 "Can not even get the host API info from PortAudio Library.");
1999 gtk_dialog_run (GTK_DIALOG (dialog));
2000 gtk_widget_destroy (dialog);
2002 else
2004 const char *hostApiName = hostApiInfo->name;
2005 const char *deviceName = "No Device";
2007 PaDeviceIndex device = hostApiInfo->defaultOutputDevice;
2009 if (device != paNoDevice)
2011 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
2012 if (deviceInfo)
2013 deviceName = deviceInfo->name;
2014 else
2015 deviceName = "(No device info)";
2018 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2019 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2020 "Got this info from PortAudio Library:\n"
2021 " Default hostApiName: %s\n"
2022 " Default deviceName: %s (%d)", hostApiName, deviceName, device);
2023 gtk_dialog_run (GTK_DIALOG (dialog));
2024 gtk_widget_destroy (dialog);
2028 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2029 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2030 "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2031 gtk_dialog_run (GTK_DIALOG (dialog));
2032 gtk_widget_destroy (dialog);
2033 return;
2035 #endif
2037 err = Pa_StartStream( pa_stream );
2038 if( err != paNoError ) {
2039 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2040 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2041 "Can not Start Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2042 gtk_dialog_run (GTK_DIALOG (dialog));
2043 gtk_widget_destroy (dialog);
2044 return;
2046 #if !PORTAUDIO_API_1
2047 rtp_channels->pa_start_time = Pa_GetStreamTime(pa_stream);
2048 #endif /* PORTAUDIO_API_1 */
2050 rtp_channels->stop = FALSE;
2052 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2053 bt_state(FALSE, FALSE, TRUE, TRUE);
2056 /* Draw the cursor in the graph */
2057 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/sample_rate, draw_cursors, NULL, NULL);
2061 /****************************************************************************/
2062 static void
2063 pause_channels(void)
2065 rtp_channels->pause = !(rtp_channels->pause);
2067 /* reactivate the cusrosr display if no in pause */
2068 if (!rtp_channels->pause) {
2069 /* Draw the cursor in the graph */
2070 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/sample_rate, draw_cursors, NULL, NULL);
2073 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2074 bt_state(FALSE, TRUE, FALSE, TRUE);
2077 /****************************************************************************/
2078 static void
2079 reset_rtp_channels(void)
2081 rtp_channels->channel = 0;
2082 rtp_channels->rci[0] = NULL;
2083 rtp_channels->rci[1] = NULL;
2084 rtp_channels->start_index[0] = 0;
2085 rtp_channels->start_index[1] = 0;
2086 rtp_channels->end_index[0] = 0;
2087 rtp_channels->end_index[1] = 0;
2088 rtp_channels->max_frame_index = 0;
2089 rtp_channels->frame_index = 0;
2090 rtp_channels->pause = FALSE;
2091 rtp_channels->pause_duration = 0;
2092 rtp_channels->stop = TRUE;
2093 rtp_channels->out_diff_time = 10000;
2096 /****************************************************************************/
2097 static void
2098 remove_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
2100 #if GTK_CHECK_VERSION(2,22,0)
2101 if(rci->surface){
2102 cairo_surface_destroy (rci->surface);
2103 rci->surface=NULL;
2105 #else
2106 g_object_unref(rci->pixmap);
2107 #endif
2108 gtk_widget_destroy(rci->draw_area);
2109 gtk_widget_destroy(rci->scroll_window);
2110 gtk_widget_destroy(rci->check_bt);
2111 if (rci->separator)
2112 gtk_widget_destroy(rci->separator);
2115 /****************************************************************************/
2116 static void
2117 reset_channels(void)
2120 if (rtp_channels_hash) {
2121 /* Remove the channels from the main window if there are there */
2122 g_hash_table_foreach( rtp_channels_hash, (GHFunc)remove_channel_to_window, NULL);
2125 /* destroy the rtp channels hash table */
2126 g_hash_table_destroy(rtp_channels_hash);
2127 rtp_channels_hash = NULL;
2130 if (rtp_channels) {
2131 reset_rtp_channels();
2135 /****************************************************************************/
2136 void
2137 reset_rtp_player(void)
2139 /* Destroy the rtp channels */
2140 reset_channels();
2142 /* destroy the rtp streams hash table */
2143 if (rtp_streams_hash) {
2144 g_hash_table_destroy(rtp_streams_hash);
2145 rtp_streams_hash = NULL;
2148 /* destroy the rtp streams list */
2149 if (rtp_streams_list) {
2150 g_list_free (rtp_streams_list);
2151 rtp_streams_list = NULL;
2156 /****************************************************************************/
2157 static void
2158 decode_streams(void)
2160 guint statusbar_context;
2161 guint counter;
2163 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2164 bt_state(FALSE, FALSE, FALSE, FALSE);
2166 reset_channels();
2168 progress_bar = gtk_progress_bar_new();
2169 gtk_widget_set_size_request(progress_bar, 100, -1);
2170 gtk_box_pack_start(GTK_BOX (stat_hbox), progress_bar, FALSE, FALSE, 2);
2171 gtk_widget_show(progress_bar);
2172 statusbar_context = gtk_statusbar_get_context_id((GtkStatusbar *) info_bar, "main");
2173 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Decoding RTP packets...");
2175 #if !GTK_CHECK_VERSION(3,0,0)
2176 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
2177 #endif
2178 /* reset the number of packet to be decoded, this is used for the progress bar */
2179 total_packets = 0;
2180 /* reset the Progress Bar count */
2181 progbar_count = 0;
2183 /* Mark the RTP streams to be played using the selected VoipCalls. If voip_calls is NULL
2184 then this was called from "RTP Analysis" so mark all strams */
2185 if (rtp_streams_hash) {
2186 if (voip_calls)
2187 g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_rtp_stream_to_play, NULL);
2188 else
2189 g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_all_rtp_stream_to_play, NULL);
2192 /* Decode the RTP streams and add them to the RTP channels to be played */
2193 g_list_foreach( rtp_streams_list, (GFunc)decode_rtp_stream, NULL);
2195 /* reset the number of frames to be displayed, this is used for the progress bar */
2196 total_frames = 0;
2197 /* Count the frames in all the RTP channels */
2198 if (rtp_channels_hash)
2199 g_hash_table_foreach( rtp_channels_hash, (GHFunc)count_channel_frames, NULL);
2201 /* reset the Progress Bar count again for the progress of creating the channels view */
2202 progbar_count = 0;
2203 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2204 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Creating channels view...");
2206 /* Display the RTP channels in the window */
2207 counter = 0;
2208 if (rtp_channels_hash)
2209 g_hash_table_foreach( rtp_channels_hash, (GHFunc)add_channel_to_window, &counter);
2211 /* Resize the main scroll window to display no more than preferred (or default) max channels, scroll bar will be used if needed */
2213 if (prefs.rtp_player_max_visible < 1 || prefs.rtp_player_max_visible > 10)
2214 prefs.rtp_player_max_visible = RTP_PLAYER_DEFAULT_VISIBLE;
2216 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH,
2217 MIN(counter, prefs.rtp_player_max_visible) * (CHANNEL_HEIGHT+60));
2219 gtk_widget_show_all(main_scrolled_window);
2221 gtk_widget_destroy(progress_bar);
2222 #if !GTK_CHECK_VERSION(3,0,0)
2223 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
2224 #endif
2225 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2227 /* blank the status label */
2228 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2230 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2231 bt_state(TRUE, FALSE, FALSE, FALSE);
2233 /* get the static jitter buffer from the spinner gui */
2234 new_jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
2238 /****************************************************************************/
2239 static void
2240 on_cb_view_as_time_of_day_clicked(GtkButton *button _U_, gpointer user_data _U_)
2242 /* Decode the streams again as if the decode button was pushed to update the time display */
2243 decode_streams();
2246 /****************************************************************************/
2247 static void
2248 on_cb_use_rtp_clicked(GtkToggleButton *button _U_, gpointer user_data _U_)
2250 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2251 bt_state(TRUE, FALSE, FALSE, FALSE);
2254 /****************************************************************************/
2255 static void
2256 on_bt_decode_clicked(GtkButton *button _U_, gpointer user_data _U_)
2258 decode_streams();
2261 /****************************************************************************/
2262 static void
2263 on_bt_play_clicked(GtkButton *button _U_, gpointer user_data _U_)
2265 play_channels();
2268 /****************************************************************************/
2269 static void
2270 on_bt_pause_clicked(GtkButton *button _U_, gpointer user_data _U_)
2272 pause_channels();
2275 /****************************************************************************/
2276 static void
2277 on_bt_stop_clicked(GtkButton *button _U_, gpointer user_data _U_)
2279 stop_channels();
2282 /****************************************************************************/
2283 static void
2284 rtp_player_on_destroy(GObject *object _U_, gpointer user_data _U_)
2286 PaError err;
2287 GtkWidget *dialog;
2289 /* Stop the channels if necesary */
2290 if(rtp_channels && (!rtp_channels->stop)){
2291 stop_channels();
2294 /* Destroy the rtp channels */
2295 reset_channels();
2297 g_free(rtp_channels);
2298 rtp_channels = NULL;
2300 /* Terminate the use of PortAudio library */
2301 err = Pa_Terminate();
2302 initialized = FALSE;
2303 if( err != paNoError ) {
2304 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2305 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2306 "Can not terminate the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2307 gtk_dialog_run (GTK_DIALOG (dialog));
2308 gtk_widget_destroy (dialog);
2311 gtk_widget_destroy(rtp_player_dlg_w);
2312 main_scrolled_window = NULL;
2313 rtp_player_dlg_w = NULL;
2316 /****************************************************************************/
2317 static void
2318 jitter_spinner_value_changed (GtkSpinButton *spinner _U_, gpointer user_data _U_)
2320 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2321 bt_state(TRUE, TRUE, FALSE, FALSE);
2324 /****************************************************************************/
2325 static void
2326 rtp_player_dlg_create(void)
2328 GtkWidget *main_vb;
2329 GtkWidget *hbuttonbox;
2330 GtkWidget *timestamp_hb;
2331 GtkWidget *h_jitter_buttons_box;
2332 GtkWidget *bt_close;
2333 GtkAdjustment *jitter_spinner_adj;
2334 GtkWidget *label;
2335 gchar *title_name_ptr;
2336 gchar *win_name;
2338 title_name_ptr = cf_get_display_name(&cfile);
2339 win_name = g_strdup_printf("%s - VoIP - RTP Player", title_name_ptr);
2340 g_free(title_name_ptr);
2342 rtp_player_dlg_w = dlg_window_new(win_name); /* transient_for top_level */
2343 gtk_window_set_destroy_with_parent (GTK_WINDOW(rtp_player_dlg_w), TRUE);
2344 gtk_window_set_position(GTK_WINDOW(rtp_player_dlg_w), GTK_WIN_POS_NONE);
2346 gtk_window_set_default_size(GTK_WINDOW(rtp_player_dlg_w), 400, 50);
2348 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2349 gtk_container_add(GTK_CONTAINER(rtp_player_dlg_w), main_vb);
2350 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 2);
2352 main_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
2353 gtk_container_set_border_width (GTK_CONTAINER (main_scrolled_window), 4);
2354 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH, 0);
2356 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2357 gtk_box_pack_start(GTK_BOX(main_vb), main_scrolled_window, TRUE, TRUE, 0);
2359 channels_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2360 gtk_container_set_border_width (GTK_CONTAINER (channels_vb), 2);
2361 #if ! GTK_CHECK_VERSION(3,8,0)
2362 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (main_scrolled_window), channels_vb);
2363 #else
2364 gtk_container_add(GTK_CONTAINER (main_scrolled_window), channels_vb);
2365 #endif
2367 timestamp_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
2368 gtk_box_pack_start(GTK_BOX(main_vb), timestamp_hb, FALSE, FALSE, 0);
2369 cb_view_as_time_of_day = gtk_check_button_new_with_label("View as time of day");
2370 gtk_box_pack_start(GTK_BOX(timestamp_hb), cb_view_as_time_of_day, TRUE, FALSE, 0); /* Centered */
2371 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day), FALSE);
2372 gtk_widget_set_tooltip_text(cb_view_as_time_of_day, "View the timestamps as time of day instead of seconds since beginning of capture");
2373 g_signal_connect(cb_view_as_time_of_day, "toggled", G_CALLBACK(on_cb_view_as_time_of_day_clicked), NULL);
2375 h_jitter_buttons_box = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
2376 gtk_container_set_border_width (GTK_CONTAINER (h_jitter_buttons_box), 10);
2377 gtk_box_pack_start (GTK_BOX(main_vb), h_jitter_buttons_box, FALSE, FALSE, 0);
2378 label = gtk_label_new("Jitter buffer [ms] ");
2379 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), label, FALSE, FALSE, 0);
2381 jitter_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (50, 0, 500, 5, 10, 0);
2382 jitter_spinner = gtk_spin_button_new (jitter_spinner_adj, 5, 0);
2383 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), jitter_spinner, FALSE, FALSE, 0);
2384 gtk_widget_set_tooltip_text (jitter_spinner, "The simulated jitter buffer in [ms]");
2385 g_signal_connect(G_OBJECT (jitter_spinner_adj), "value_changed", G_CALLBACK(jitter_spinner_value_changed), NULL);
2387 cb_use_rtp_timestamp = gtk_check_button_new_with_label("Use RTP timestamp");
2388 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp), FALSE);
2389 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), cb_use_rtp_timestamp, FALSE, FALSE, 10);
2390 g_signal_connect(cb_use_rtp_timestamp, "toggled", G_CALLBACK(on_cb_use_rtp_clicked), NULL);
2391 gtk_widget_set_tooltip_text (cb_use_rtp_timestamp, "Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing");
2393 /* button row */
2394 hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
2395 gtk_box_pack_start (GTK_BOX (h_jitter_buttons_box), hbuttonbox, TRUE, TRUE, 0);
2396 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
2397 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 10);
2399 bt_decode = gtk_button_new_from_stock(WIRESHARK_STOCK_DECODE);
2400 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_decode);
2401 g_signal_connect(bt_decode, "clicked", G_CALLBACK(on_bt_decode_clicked), NULL);
2402 gtk_widget_set_tooltip_text (bt_decode, "Decode the RTP stream(s)");
2404 bt_play = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
2405 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_play);
2406 g_signal_connect(bt_play, "clicked", G_CALLBACK(on_bt_play_clicked), NULL);
2407 gtk_widget_set_tooltip_text (bt_play, "Play the RTP channel(s)");
2409 bt_pause = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE);
2410 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_pause);
2411 g_signal_connect(bt_pause, "clicked", G_CALLBACK(on_bt_pause_clicked), NULL);
2412 gtk_widget_set_tooltip_text (bt_pause, "Pause the RTP channel(s)");
2414 bt_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
2415 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_stop);
2416 g_signal_connect(bt_stop, "clicked", G_CALLBACK(on_bt_stop_clicked), NULL);
2417 gtk_widget_set_tooltip_text (bt_stop, "Stop the RTP channel(s)");
2419 bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2420 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
2421 gtk_widget_set_can_default(bt_close, TRUE);
2422 gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
2423 window_set_cancel_button(rtp_player_dlg_w, bt_close, window_cancel_button_cb);
2425 g_signal_connect(rtp_player_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2426 g_signal_connect(rtp_player_dlg_w, "destroy", G_CALLBACK(rtp_player_on_destroy), NULL);
2428 /* button row */
2429 /* ?? hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);*/
2431 /* Filter/status hbox */
2432 stat_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
2433 gtk_container_set_border_width(GTK_CONTAINER(stat_hbox), 0);
2435 /* statusbar */
2436 info_bar = gtk_statusbar_new();
2437 #if !GTK_CHECK_VERSION(3,0,0)
2438 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
2439 #endif
2440 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
2442 /* statusbar hbox */
2443 gtk_box_pack_start(GTK_BOX(main_vb), stat_hbox, FALSE, TRUE, 0);
2445 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2446 bt_state(TRUE, FALSE, FALSE, FALSE);
2448 gtk_widget_show_all(rtp_player_dlg_w);
2450 /* Force gtk to redraw the window before starting decoding the packet */
2451 while (g_main_context_iteration(NULL, FALSE));
2453 g_free(win_name);
2456 /****************************************************************************/
2457 void
2458 rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap)
2460 PaError err;
2461 GtkWidget *dialog;
2463 if (initialized) return;
2464 initialized = TRUE;
2466 voip_calls = voip_calls_tap;
2467 err = Pa_Initialize();
2468 if( err != paNoError ) {
2469 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2470 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2471 "Can not Initialize the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2472 gtk_dialog_run (GTK_DIALOG (dialog));
2473 gtk_widget_destroy (dialog);
2474 initialized = FALSE;
2475 return;
2478 new_jitter_buff = -1;
2480 #ifdef HAVE_G729_G723
2481 /* Initialize the G729 and G723 decoders */
2482 initG723();
2483 initG729();
2484 #endif /* HAVE_G729_G723 */
2486 if (!rtp_channels) {
2487 rtp_channels = g_new(rtp_play_channels_t,1);
2490 reset_rtp_channels();
2492 #ifdef DEBUG
2493 g_print("Pa_GetHostApiCount() = %d\n", Pa_GetHostApiCount());
2494 g_print("Pa_GetDefaultHostApi() = %d\n", Pa_GetDefaultHostApi());
2496 if ((Pa_GetHostApiCount() >= 0) && (Pa_GetDefaultHostApi() >= 0))
2498 unsigned int i;
2499 PaHostApiIndex api_index;
2500 const PaHostApiInfo *api_info = Pa_GetHostApiInfo( (unsigned int)Pa_GetDefaultHostApi() );
2501 g_print("Default PaHostApiInfo.type = %d (%s)\n", api_info->type, api_info->name);
2503 for (i=0; i<(unsigned int)Pa_GetHostApiCount(); i++)
2505 api_info = Pa_GetHostApiInfo( i );
2506 g_print("PaHostApiInfo[%u].type = %d (%s)\n", i, api_info->type, api_info->name);
2509 api_index = Pa_HostApiTypeIdToHostApiIndex( paALSA );
2510 if (api_index < 0)
2512 g_print("api_index for paALSA not found (%d)\n", api_index);
2514 else
2516 api_info = Pa_GetHostApiInfo( (unsigned int)api_index );
2517 g_print("This should be ALSA: %s\n", api_info->name);
2520 #endif
2522 /* create the dialog window */
2523 rtp_player_dlg_create();
2527 #endif /* HAVE_LIBPORTAUDIO */