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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 * Here is a summary on how this works:
28 * - The VoipCalls will call add_rtp_packet() every time there is an RTP
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
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
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)
58 #ifdef HAVE_LIBPORTAUDIO
61 #include "portaudio.h"
65 #include <epan/stats_tree.h>
66 #include <epan/addr_resolv.h>
67 #include <epan/dissectors/packet-rtp.h>
68 #include <epan/rtp_pt.h>
69 #include <epan/codecs.h>
70 #include <epan/prefs.h>
72 #include "../globals.h"
73 #include "../simple_dialog.h"
74 #include "../codecs/G711a/G711adecode.h"
75 #include "../codecs/G711u/G711udecode.h"
77 #include "gtk/gui_utils.h"
78 #include "gtk/dlg_utils.h"
79 #include "gtk/graph_analysis.h"
80 #include "gtk/voip_calls_dlg.h"
81 #include "gtk/voip_calls.h"
82 #include "gtk/gtkglobals.h"
83 #include "gtk/rtp_player.h"
84 #include "gtk/stock_icons.h"
86 #include "gtk/old-gtk-compat.h"
88 /*define this symbol to compile with G729 and G723 codecs*/
89 /*#define HAVE_G729_G723 1*/
92 #include "codecs/G729/G729decode.h"
93 #include "codecs/G723/G723decode.h"
94 #endif /* HAVE_G729_G723 */
96 static gboolean initialized = FALSE;
98 static voip_calls_tapinfo_t *voip_calls = NULL;
100 /* Hash table with all the RTP streams */
101 static GHashTable* rtp_streams_hash = NULL;
103 /* List with all the RTP streams (this is used to decode them as it is sorted)*/
104 static GList* rtp_streams_list = NULL;
107 static GtkWidget *rtp_player_dlg_w;
108 static GtkWidget *channels_vb;
109 static GtkWidget *main_scrolled_window = NULL;
110 static GtkWidget *jitter_spinner;
111 static GtkWidget *cb_use_rtp_timestamp;
112 static GtkWidget *cb_view_as_time_of_day;
113 static GtkWidget *bt_decode;
114 static GtkWidget *bt_play;
115 static GtkWidget *bt_pause;
116 static GtkWidget *bt_stop;
117 static GtkWidget *progress_bar;
118 static GtkWidget *info_bar;
119 static GtkWidget *stat_hbox;
121 static guint32 total_packets;
122 static guint32 total_frames;
123 static guint32 progbar_count;
125 static int new_jitter_buff;
127 /* a hash table with the RTP streams to play per audio channel */
128 static GHashTable *rtp_channels_hash = NULL;
130 /* Port Audio stuff */
131 #define SAMPLE_RATE (8000)
132 #define NUM_CHANNELS (2)
134 #define PA_SAMPLE_TYPE paInt16
135 typedef gint16 SAMPLE;
136 #define SAMPLE_SILENCE (0)
137 #define FRAMES_PER_BUFFER (512)
139 typedef struct _sample_t {
145 #define S_DROP_BY_JITT 1
146 #define S_WRONG_SEQ 2
147 #define S_WRONG_TIMESTAMP 3 /* The timestamp does not reflect the number of samples - samples have been dropped or silence inserted to match timestamp */
148 #define S_SILENCE 4 /* Silence inserted by Wireshark, rather than contained in a packet */
150 /* Display channels constants */
152 #define CHANNEL_WIDTH 500
153 #define CHANNEL_HEIGHT 100
154 #define MAX_TIME_LABEL 10
155 #define HEIGHT_TIME_LABEL 18
156 #define MAX_NUM_COL_CONV 10
159 static PortAudioStream *pa_stream;
160 #else /* PORTAUDIO_API_1 */
161 static PaStream *pa_stream;
162 #endif /* PORTAUDIO_API_1 */
164 /* defines a RTP stream */
165 typedef struct _rtp_stream_info {
171 guint32 first_frame_number; /* first RTP frame for the stream */
172 double start_time; /* RTP stream start time in ms */
173 nstime_t start_time_abs;
176 GList* rtp_packets_list; /* List of RTP packets in the stream */
181 /* defines the RTP streams to be played in an audio channel */
182 typedef struct _rtp_channel_info {
183 double start_time; /* RTP stream start time in ms */
184 nstime_t start_time_abs;
185 double end_time; /* RTP stream end time in ms */
186 GArray *samples; /* the array with decoded audio */
190 guint32 drop_by_jitter_buff;
192 guint32 wrong_timestamp;
193 guint32 max_frame_index;
195 GtkWidget *separator;
196 GtkWidget *scroll_window;
197 GtkWidget *draw_area;
199 GtkAdjustment *h_scrollbar_adjustment;
200 GdkPixbuf* cursor_pixbuf;
202 PaTimestamp cursor_prev;
203 #else /* PORTAUDIO_API_1 */
205 #endif /* PORTAUDIO_API_1 */
206 GdkColor bg_color[MAX_NUM_COL_CONV+1];
207 gboolean cursor_catch;
208 rtp_stream_info_t *first_stream; /* This is the first RTP stream in the channel */
210 } rtp_channel_info_t;
212 /* defines a RTP packet */
213 typedef struct _rtp_packet {
214 struct _rtp_info *info; /* the RTP dissected info */
215 double arrive_offset; /* arrive offset time since the begining of the stream in ms */
216 guint8* payload_data;
219 /* defines the two RTP channels to be played */
220 typedef struct _rtp_play_channles {
221 rtp_channel_info_t* rci[2]; /* Channels to be played */
222 guint32 start_index[2];
223 guint32 end_index[2];
225 guint32 max_frame_index;
229 gint32 pause_duration;
231 PaTimestamp out_diff_time;
232 #else /* PORTAUDIO_API_1 */
233 PaTime out_diff_time;
234 PaTime pa_start_time;
235 #endif /* PORTAUDIO_API_1 */
236 } rtp_play_channels_t;
238 /* The two RTP channels to play */
239 static rtp_play_channels_t *rtp_channels = NULL;
241 typedef struct _rtp_decoder_t {
242 codec_handle_t handle;
247 /****************************************************************************/
249 rtp_key_destroy(gpointer key)
255 /****************************************************************************/
257 rtp_channel_value_destroy(gpointer rci_arg)
259 rtp_channel_info_t *rci = rci_arg;
261 g_array_free(rci->samples, TRUE);
266 /****************************************************************************/
268 rtp_stream_value_destroy(gpointer rsi_arg)
270 rtp_stream_info_t *rsi = rsi_arg;
271 GList* rtp_packets_list;
274 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
275 while (rtp_packets_list)
277 rp = rtp_packets_list->data;
280 g_free(rp->payload_data);
284 rtp_packets_list = g_list_next(rtp_packets_list);
286 g_free((void *)(rsi->src_addr.data));
287 g_free((void *)(rsi->dest_addr.data));
292 /****************************************************************************/
294 rtp_decoder_value_destroy(gpointer dec_arg)
296 rtp_decoder_t *dec = dec_arg;
299 codec_release(dec->handle, dec->context);
303 /****************************************************************************/
305 set_sensitive_check_bt(gchar *key _U_ , rtp_channel_info_t *rci, guint *stop _U_ )
307 gtk_widget_set_sensitive(rci->check_bt, !(*stop));
310 /****************************************************************************/
312 bt_state(gboolean decode, gboolean play, gboolean pause, gboolean stop)
314 gboolean new_jitter_value = FALSE;
315 gboolean false_val = FALSE;
317 gtk_widget_set_sensitive(bt_decode, decode);
318 gtk_widget_set_sensitive(cb_use_rtp_timestamp, decode);
319 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
320 gtk_widget_set_sensitive(jitter_spinner, FALSE);
322 gtk_widget_set_sensitive(jitter_spinner, decode);
325 if (new_jitter_buff != (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner)) {
326 new_jitter_value = TRUE;
329 /* set the sensitive state of play only if there is a channel selected */
330 if ( play && (rtp_channels->rci[0] || rtp_channels->rci[1]) && !new_jitter_value) {
331 gtk_widget_set_sensitive(bt_play, TRUE);
333 gtk_widget_set_sensitive(bt_play, FALSE);
336 if (!new_jitter_value) {
337 gtk_widget_set_sensitive(bt_pause, pause);
338 gtk_widget_set_sensitive(bt_stop, stop);
340 /* Set sensitive to the check buttons based on the STOP state */
341 if (rtp_channels_hash)
342 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &stop);
344 gtk_widget_set_sensitive(bt_pause, FALSE);
345 gtk_widget_set_sensitive(bt_stop, FALSE);
347 if (rtp_channels_hash)
348 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &false_val);
352 /****************************************************************************/
354 add_rtp_packet(const struct _rtp_info *rtp_info, packet_info *pinfo)
356 rtp_stream_info_t *stream_info = NULL;
357 rtp_packet_t *new_rtp_packet;
358 GString *key_str = NULL;
360 /* create the streams hash if it doen't exist */
361 if (!rtp_streams_hash)
362 rtp_streams_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_stream_value_destroy);
364 /* Create a hash key to lookup in the RTP streams hash table
365 * uses: src_ip:src_port dst_ip:dst_port ssrc
367 key_str = g_string_new("");
368 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(pinfo->src)),
369 pinfo->srcport, get_addr_name(&(pinfo->dst)),
370 pinfo->destport, rtp_info->info_sync_src );
372 /* lookup for this RTP packet in the stream hash table */
373 stream_info = g_hash_table_lookup( rtp_streams_hash, key_str->str);
375 /* if it is not in the hash table, create a new stream */
376 if (stream_info==NULL) {
377 stream_info = g_malloc(sizeof(rtp_stream_info_t));
378 COPY_ADDRESS(&(stream_info->src_addr), &(pinfo->src));
379 stream_info->src_port = pinfo->srcport;
380 COPY_ADDRESS(&(stream_info->dest_addr), &(pinfo->dst));
381 stream_info->dest_port = pinfo->destport;
382 stream_info->ssrc = rtp_info->info_sync_src;
383 stream_info->rtp_packets_list = NULL;
384 stream_info->first_frame_number = pinfo->fd->num;
385 stream_info->start_time = nstime_to_msec(&pinfo->fd->rel_ts);
386 stream_info->start_time_abs = pinfo->fd->abs_ts;
387 stream_info->call_num = 0;
388 stream_info->play = FALSE;
389 stream_info->num_packets = 0;
391 g_hash_table_insert(rtp_streams_hash, g_strdup(key_str->str), stream_info);
393 /* Add the element to the List too. The List is used to decode the packets because it is sorted */
394 rtp_streams_list = g_list_append(rtp_streams_list, stream_info);
397 /* increment the number of packets in this stream, this is used for the progress bar and statistics */
398 stream_info->num_packets++;
400 /* Add the RTP packet to the list */
401 new_rtp_packet = g_malloc(sizeof(rtp_packet_t));
402 new_rtp_packet->info = g_malloc(sizeof(struct _rtp_info));
404 memcpy(new_rtp_packet->info, rtp_info, sizeof(struct _rtp_info));
405 new_rtp_packet->arrive_offset = nstime_to_msec(&pinfo->fd->rel_ts) - stream_info->start_time;
406 /* copy the RTP payload to the rtp_packet to be decoded later */
407 if (rtp_info->info_all_data_present && (rtp_info->info_payload_len != 0)) {
408 new_rtp_packet->payload_data = g_malloc(rtp_info->info_payload_len);
409 memcpy(new_rtp_packet->payload_data, &(rtp_info->info_data[rtp_info->info_payload_offset]), rtp_info->info_payload_len);
411 new_rtp_packet->payload_data = NULL;
414 stream_info->rtp_packets_list = g_list_append(stream_info->rtp_packets_list, new_rtp_packet);
416 g_string_free(key_str, TRUE);
419 /****************************************************************************/
420 /* Mark the RTP stream to be played. Use the voip_calls graph to see if the
421 * setup_frame is there and then if the associated voip_call is selected.
424 mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
427 graph_analysis_item_t *graph_item;
428 GList* voip_calls_list;
429 voip_calls_info_t *tmp_voip_call;
431 /* Reset the "to be play" value because the user can close and reopen the RTP Player window
432 * and the streams are not reset in that case
436 /* and associate the RTP stream with a call using the first RTP packet in the stream */
437 graph_list = g_list_first(voip_calls->graph_analysis->list);
440 graph_item = graph_list->data;
441 if (rsi->first_frame_number == graph_item->frame_num) {
442 rsi->call_num = graph_item->conv_num;
443 /* if it is in the graph list, then check if the voip_call is selected */
444 voip_calls_list = g_list_first(voip_calls->callsinfo_list);
445 while (voip_calls_list)
447 tmp_voip_call = voip_calls_list->data;
448 if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
450 total_packets += rsi->num_packets;
453 voip_calls_list = g_list_next(voip_calls_list);
457 graph_list = g_list_next(graph_list);
461 /****************************************************************************/
462 /* Mark the ALL RTP stream to be played. This is called when calling the
463 * RTP player from the "RTP Analysis" window
466 mark_all_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
469 total_packets += rsi->num_packets;
472 /****************************************************************************/
473 /* Decode a RTP packet
474 * Return the number of decoded bytes
477 decode_rtp_packet(rtp_packet_t *rp, SAMPLE **out_buff, GHashTable *decoders_hash)
479 unsigned int payload_type;
481 rtp_decoder_t *decoder;
482 SAMPLE *tmp_buff = NULL;
484 int decoded_bytes = 0;
486 if ((rp->payload_data == NULL) || (rp->info->info_payload_len == 0) ) {
490 payload_type = rp->info->info_payload_type;
492 /* Look for registered codecs */
493 decoder = g_hash_table_lookup(decoders_hash, GUINT_TO_POINTER(payload_type));
494 if (!decoder) { /* Put either valid or empty decoder into the hash table */
495 decoder = g_malloc(sizeof(rtp_decoder_t));
496 decoder->handle = NULL;
497 decoder->context = NULL;
498 p = match_strval_ext(payload_type, &rtp_payload_type_short_vals_ext);
500 decoder->handle = find_codec(p);
502 decoder->context = codec_init(decoder->handle);
504 g_hash_table_insert(decoders_hash, GUINT_TO_POINTER(payload_type), decoder);
506 if (decoder->handle) { /* Decode with registered codec */
507 tmp_buff_len = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, NULL, NULL);
508 tmp_buff = g_malloc(tmp_buff_len);
509 decoded_bytes = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, tmp_buff, &tmp_buff_len);
510 *out_buff = tmp_buff;
511 return decoded_bytes;
514 /* Try to decode with built-in codec */
516 switch (payload_type) {
518 case PT_PCMU: /* G.711 u-law */
519 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
520 decodeG711u(rp->payload_data, rp->info->info_payload_len,
521 tmp_buff, &decoded_bytes);
524 case PT_PCMA: /* G.711 A-law */
525 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
526 decodeG711a(rp->payload_data, rp->info->info_payload_len,
527 tmp_buff, &decoded_bytes);
530 #ifdef HAVE_G729_G723
531 case PT_G729: /* G.729 */
532 /* G729 8kbps => 64kbps/8kbps = 8 */
533 /* Compensate for possible 2 octet SID frame (G.729B) */
534 tmp_buff = g_malloc(sizeof(SAMPLE) * ((rp->info->info_payload_len + 8) / 10) * 80);
535 decodeG729(rp->payload_data, rp->info->info_payload_len,
536 tmp_buff, &decoded_bytes);
539 case PT_G723: /* G.723 */
540 if (rp->info->info_payload_len%24 == 0) /* G723 High 6.4kbps */
541 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 10); /* G723 High 64kbps/6.4kbps = 10 */
542 else if (rp->info->info_payload_len%20 == 0) /* G723 Low 5.3kbps */
543 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 13); /* G723 High 64kbps/5.3kbps = 13 */
547 decodeG723(rp->payload_data, rp->info->info_payload_len,
548 tmp_buff, &decoded_bytes);
550 #endif /* HAVE_G729_G723 */
554 * XXX - return an error here, so the user gets told that
555 * we don't support this codec!
560 *out_buff = tmp_buff;
561 return decoded_bytes;
564 /****************************************************************************/
566 update_progress_bar(gfloat fraction)
569 if GTK_IS_PROGRESS_BAR(progress_bar)
570 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), fraction);
572 /* Force gtk to redraw the window before starting decoding the packet */
573 while (gtk_events_pending())
574 gtk_main_iteration();
577 /****************************************************************************/
578 /* Decode the RTP streams and add them to the RTP channels struct
581 decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr _U_)
583 GString *key_str = NULL;
584 rtp_channel_info_t *rci;
585 gboolean first = TRUE;
586 GList* rtp_packets_list;
591 double rtp_time_prev;
593 double arrive_time_prev;
595 double start_rtp_time = 0;
598 #ifdef DEBUG /* ?? */
600 double total_time_prev;
602 gint32 silence_frames;
606 #ifdef DEBUG /* ?? */
611 int decoded_bytes_prev;
613 SAMPLE *out_buff = NULL;
617 guint32 start_timestamp;
618 GHashTable *decoders_hash = NULL;
620 guint32 progbar_nextstep;
625 silence.status = S_NORMAL;
627 /* skip it if we are not going to play it */
628 if (rsi->play == FALSE) {
632 /* get the static jitter buffer from the spinner gui */
633 jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
635 /* Create a hash key to lookup in the RTP channels hash
636 * uses: src_ip:src_port dst_ip:dst_port call_num
638 key_str = g_string_new("");
639 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(rsi->src_addr)),
640 rsi->src_port, get_addr_name(&(rsi->dest_addr)),
641 rsi->dest_port, rsi->call_num );
643 /* create the rtp_channels_hash table if it doesn't exist */
644 if (!rtp_channels_hash) {
645 rtp_channels_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_channel_value_destroy);
648 /* lookup for this stream in the channel hash table */
649 rci = g_hash_table_lookup( rtp_channels_hash, key_str->str);
651 /* ..if it is not in the hash, create an entry */
653 rci = g_malloc(sizeof(rtp_channel_info_t));
654 rci->call_num = rsi->call_num;
655 rci->start_time = rsi->start_time;
656 rci->start_time_abs = rsi->start_time_abs;
657 rci->end_time = rsi->start_time;
658 rci->selected = FALSE;
659 rci->frame_index = 0;
660 rci->drop_by_jitter_buff = 0;
662 rci->wrong_timestamp = 0;
663 rci->max_frame_index = 0;
664 rci->samples = g_array_new (FALSE, FALSE, sizeof(sample_t));
665 rci->check_bt = NULL;
666 rci->separator = NULL;
667 rci->draw_area = NULL;
669 rci->h_scrollbar_adjustment = NULL;
670 rci->cursor_pixbuf = NULL;
671 rci->cursor_prev = 0;
672 rci->cursor_catch = FALSE;
673 rci->first_stream = rsi;
674 rci->num_packets = rsi->num_packets;
675 g_hash_table_insert(rtp_channels_hash, g_strdup(key_str->str), rci);
677 /* Add silence between the two streams if needed */
678 silence_frames = (gint32)( ((rsi->start_time - rci->end_time)/1000)*SAMPLE_RATE );
679 for (i = 0; i< silence_frames; i++) {
680 g_array_append_val(rci->samples, silence);
682 rci->num_packets += rsi->num_packets;
685 /* decode the RTP stream */
690 decoded_bytes_prev = 0;
692 arrive_time = start_time = 0;
693 arrive_time_prev = 0;
695 #ifdef DEBUG /* ?? */
702 #ifdef DEBUG /* ?? */
707 decoders_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rtp_decoder_value_destroy);
709 /* we update the progress bar 100 times */
711 /* Update the progress bar when it gets to this value. */
712 progbar_nextstep = 0;
713 /* When we reach the value that triggers a progress bar update,
714 bump that value by this amount. */
715 progbar_quantum = total_packets/100;
719 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
720 while (rtp_packets_list)
723 if (progbar_count >= progbar_nextstep) {
724 g_assert(total_packets > 0);
726 progbar_val = (gfloat) progbar_count / total_packets;
728 update_progress_bar(progbar_val);
730 progbar_nextstep += progbar_quantum;
734 rp = rtp_packets_list->data;
736 start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
738 rtp_time_prev = start_rtp_time;
740 seq = rp->info->info_seq_num - 1;
743 decoded_bytes = decode_rtp_packet(rp, &out_buff, decoders_hash);
744 if (decoded_bytes == 0) {
745 seq = rp->info->info_seq_num;
748 rtp_time = (double)(rp->info->info_timestamp-start_timestamp)/SAMPLE_RATE - start_rtp_time;
750 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
751 arrive_time = rtp_time;
753 arrive_time = (double)rp->arrive_offset/1000 - start_time;
756 if (rp->info->info_seq_num != seq+1){
758 status = S_WRONG_SEQ;
760 seq = rp->info->info_seq_num;
762 diff = arrive_time - rtp_time;
764 delay = diff - prev_diff;
766 if (delay<0) delay = -delay;
768 if (diff<0) diff = -diff;
771 total_time = (double)rp->arrive_offset/1000;
772 printf("seq = %d arr = %f abs_diff = %f index = %d tim = %f ji=%d jb=%f\n",rp->info->info_seq_num,
773 total_time, diff, rci->samples->len, ((double)rci->samples->len/8000 - total_time)*1000, 0,
774 (mean_delay + 4*variation)*1000);
777 /* if the jitter buffer was exceeded */
778 if ( diff*1000 > jitter_buff ) {
780 printf("Packet drop by jitter buffer exceeded\n");
782 rci->drop_by_jitter_buff++;
783 status = S_DROP_BY_JITT;
785 /* if there was a silence period (more than two packetization period) resync the source */
786 if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
788 printf("Resync...\n");
790 silence_frames = (gint32)((arrive_time - arrive_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
792 /* Fix for bug 4119/5902: don't insert too many silence frames.
793 * XXX - is there a better thing to do here?
795 #define MAX_SILENCE_FRAMES 240000
796 if (silence_frames > MAX_SILENCE_FRAMES)
797 silence_frames = MAX_SILENCE_FRAMES;
799 for (i = 0; i< silence_frames; i++) {
800 silence.status = status;
801 g_array_append_val(rci->samples, silence);
803 /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
807 decoded_bytes_prev = 0;
808 start_timestamp = rp->info->info_timestamp; /* defined start_timestamp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
810 start_time = (double)rp->arrive_offset/1000;
814 /* Add silence if it is necessary */
815 silence_frames = (gint32)((rtp_time - rtp_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
816 if (silence_frames != 0) {
817 rci->wrong_timestamp++;
818 status = S_WRONG_TIMESTAMP;
821 /* Fix for bug 4119/5902: don't insert too many silence frames.
822 * XXX - is there a better thing to do here?
824 if (silence_frames > MAX_SILENCE_FRAMES)
825 silence_frames = MAX_SILENCE_FRAMES;
827 for (i = 0; i< silence_frames; i++) {
828 silence.status = status;
829 g_array_append_val(rci->samples, silence);
831 /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
836 if (silence_frames > 0) {
840 for (i = - silence_frames; i< (decoded_bytes/2); i++) {
841 sample.val = out_buff[i];
842 sample.status = status;
843 g_array_append_val(rci->samples, sample);
847 rtp_time_prev = rtp_time;
848 pack_period = (double)(decoded_bytes/2)/SAMPLE_RATE;
849 decoded_bytes_prev = decoded_bytes;
850 arrive_time_prev = arrive_time;
857 rtp_packets_list = g_list_next (rtp_packets_list);
860 rci->max_frame_index = rci->samples->len;
861 rci->end_time = rci->start_time + ((double)rci->samples->len/SAMPLE_RATE)*1000;
863 g_string_free(key_str, TRUE);
864 g_hash_table_destroy(decoders_hash);
867 /****************************************************************************/
869 h_scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
871 rtp_channel_info_t *rci = user_data;
872 rci->cursor_catch = TRUE;
876 static gboolean draw_cursors(gpointer data);
878 /****************************************************************************/
885 /* we should never be here if we are already in STOP */
886 g_assert(rtp_channels->stop == FALSE);
888 rtp_channels->stop = TRUE;
889 /* force a draw_cursor to stop it */
892 err = Pa_StopStream(pa_stream);
894 if( err != paNoError ) {
895 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
896 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
897 "Can not Stop Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
898 gtk_dialog_run (GTK_DIALOG (dialog));
899 gtk_widget_destroy (dialog);
903 err = Pa_CloseStream(pa_stream);
904 if( err != paNoError ) {
905 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
906 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
907 "Can not Close Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
908 gtk_dialog_run (GTK_DIALOG (dialog));
909 gtk_widget_destroy (dialog);
912 pa_stream = NULL; /* to catch errors better */
914 rtp_channels->start_index[0] = 0;
915 rtp_channels->start_index[1] = 0;
916 rtp_channels->end_index[0] = 0;
917 rtp_channels->end_index[1] = 0;
918 rtp_channels->max_frame_index = 0;
919 rtp_channels->frame_index = 0;
920 rtp_channels->pause = FALSE;
921 rtp_channels->pause_duration = 0;
922 rtp_channels->stop = TRUE;
923 rtp_channels->out_diff_time = 10000;
925 if (rtp_channels->rci[0]) rtp_channels->rci[0]->frame_index = 0;
926 if (rtp_channels->rci[1]) rtp_channels->rci[1]->frame_index = 0;
928 /* set the sensitive state of the buttons (decode, play, pause, stop) */
929 bt_state(TRUE, TRUE, FALSE, FALSE);
933 /****************************************************************************/
934 /* Draw a cursor in a channel graph
937 draw_channel_cursor(rtp_channel_info_t *rci, guint32 start_index)
941 #else /* PORTAUDIO_API_1 */
943 #endif /* PORTAUDIO_API_1 */
945 GtkAllocation widget_alloc;
950 idx = Pa_StreamTime( pa_stream ) - rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
951 #else /* PORTAUDIO_API_1 */
952 idx = ((guint32)(SAMPLE_RATE) * (Pa_GetStreamTime(pa_stream)-rtp_channels->pa_start_time))- rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
953 #endif /* PORTAUDIO_API_1 */
956 /* If we finished playing both channels, then stop them */
957 if ( (rtp_channels && (!rtp_channels->stop) && (!rtp_channels->pause)) && (idx > rtp_channels->max_frame_index) ) {
962 /* If only this channel finished, then return */
963 if (idx > rci->max_frame_index) {
967 gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
968 /* draw the previous saved pixbuf line */
969 if (rci->cursor_pixbuf && (rci->cursor_prev>=0)) {
971 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);
973 gdk_draw_drawable(gtk_widget_get_window(rci->draw_area),
974 gtk_widget_get_style(rci->draw_area)->fg_gc[gtk_widget_get_state(rci->draw_area)],
976 (int) (rci->cursor_prev/MULT), 0,
977 (int) (rci->cursor_prev/MULT), 0,
978 1, widget_alloc.height-HEIGHT_TIME_LABEL);
980 g_object_unref(rci->cursor_pixbuf);
981 rci->cursor_pixbuf = NULL;
984 if (idx>0 && (rci->cursor_prev>=0)) {
985 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);
987 gdk_draw_line(rci->pixmap, gtk_widget_get_style(rci->draw_area)->black_gc,
991 widget_alloc.height-HEIGHT_TIME_LABEL);
993 gdk_draw_drawable(gtk_widget_get_window(rci->draw_area),
994 gtk_widget_get_style(rci->draw_area)->fg_gc[gtk_widget_get_state(rci->draw_area)],
998 1, widget_alloc.height-HEIGHT_TIME_LABEL);
1001 /* Disconnect the scroll bar "value" signal to not be called */
1002 g_signal_handlers_disconnect_by_func(rci->h_scrollbar_adjustment, h_scrollbar_changed, rci);
1004 /* Move the horizontal scroll bar */
1006 if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
1007 (idx/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
1008 for (i=1; i<10; i++) {
1009 rci->h_scrollbar_adjustment->value += rci->h_scrollbar_adjustment->page_size/10;
1010 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1014 if (!rci->cursor_catch) {
1015 if (idx/MULT < gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2) {
1016 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, gtk_adjustment_get_lower(rci->h_scrollbar_adjustment));
1017 } else if (idx/MULT > (gtk_adjustment_get_upper(rci->h_scrollbar_adjustment) - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2)) {
1018 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));
1020 gtk_adjustment_set_value(rci->h_scrollbar_adjustment, idx/MULT - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2);
1023 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1024 } else if ( (rci->cursor_prev/MULT < gtk_adjustment_get_value(rci->h_scrollbar_adjustment)+gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) &&
1025 (idx/MULT >= gtk_adjustment_get_value(rci->h_scrollbar_adjustment) + gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) ){
1026 rci->cursor_catch = FALSE;
1027 for (i=1; i<10; i++) {
1028 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));
1029 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1033 /* Connect back the "value" scroll signal */
1034 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
1037 if (idx/MULT < rci->h_scrollbar_adjustment->page_increment) {
1038 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
1039 } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size + rci->h_scrollbar_adjustment->page_increment)) {
1040 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1042 if ( (idx/MULT < rci->h_scrollbar_adjustment->value) || (idx/MULT > (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
1043 rci->h_scrollbar_adjustment->value = idx/MULT;
1049 if (idx/MULT < rci->h_scrollbar_adjustment->page_size/2) {
1050 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
1051 } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
1052 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1054 rci->h_scrollbar_adjustment->value = idx/MULT - rci->h_scrollbar_adjustment->page_size/2;
1059 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1061 rci->cursor_prev = idx;
1064 /****************************************************************************/
1065 /* Move and draw the cursor in the graph
1068 draw_cursors(gpointer data _U_)
1070 if (!rtp_channels) return FALSE;
1072 /* Draw and move each of the two channels */
1073 draw_channel_cursor(rtp_channels->rci[0], rtp_channels->start_index[0]);
1074 draw_channel_cursor(rtp_channels->rci[1], rtp_channels->start_index[1]);
1076 if ((rtp_channels->stop) || (rtp_channels->pause)) return FALSE;
1081 /****************************************************************************/
1083 init_rtp_channels_vals(void)
1085 rtp_play_channels_t *rpci = rtp_channels;
1087 /* if we only have one channel to play, we just use the info from that channel */
1088 if (rpci->rci[0] == NULL) {
1089 rpci->max_frame_index = rpci->rci[1]->max_frame_index;
1090 rpci->start_index[0] = rpci->max_frame_index;
1091 rpci->start_index[1] = 0;
1092 rpci->end_index[0] = rpci->max_frame_index;
1093 rpci->end_index[1] = rpci->max_frame_index;
1094 } else if (rpci->rci[1] == NULL) {
1095 rpci->max_frame_index = rpci->rci[0]->max_frame_index;
1096 rpci->start_index[1] = rpci->max_frame_index;
1097 rpci->start_index[0] = 0;
1098 rpci->end_index[0] = rpci->max_frame_index;
1099 rpci->end_index[1] = rpci->max_frame_index;
1101 /* if the two channels are to be played, then we need to sync both based on the start/end time of each one */
1103 rpci->max_frame_index = (guint32)(SAMPLE_RATE/1000) * (guint32)(MAX(rpci->rci[0]->end_time, rpci->rci[1]->end_time) -
1104 (guint32)MIN(rpci->rci[0]->start_time, rpci->rci[1]->start_time));
1106 if (rpci->rci[0]->start_time < rpci->rci[1]->start_time) {
1107 rpci->start_index[0] = 0;
1108 rpci->start_index[1] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->start_time - rpci->rci[0]->start_time);
1110 rpci->start_index[1] = 0;
1111 rpci->start_index[0] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->start_time - rpci->rci[1]->start_time);
1114 if (rpci->rci[0]->end_time < rpci->rci[1]->end_time) {
1115 rpci->end_index[0] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->end_time - rpci->rci[0]->end_time));
1116 rpci->end_index[1] = rpci->max_frame_index;
1118 rpci->end_index[1] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->end_time - rpci->rci[1]->end_time));
1119 rpci->end_index[0] = rpci->max_frame_index;
1125 /****************************************************************************/
1126 /* This routine will be called by the PortAudio engine when audio is needed.
1127 * It may called at interrupt level on some machines so don't do anything
1128 * that could mess up the system like calling malloc() or free().
1132 static int paCallback( void *inputBuffer, void *outputBuffer,
1133 unsigned long framesPerBuffer,
1134 PaTimestamp outTime, void *userData)
1136 #else /* PORTAUDIO_API_1 */
1137 static int paCallback( const void *inputBuffer, void *outputBuffer,
1138 unsigned long framesPerBuffer,
1139 const PaStreamCallbackTimeInfo* outTime,
1140 PaStreamCallbackFlags statusFlags _U_,
1143 #endif /* PORTAUDIO_API_1 */
1144 rtp_play_channels_t *rpci = (rtp_play_channels_t *)userData;
1145 SAMPLE *wptr = (SAMPLE*)outputBuffer;
1149 unsigned int framesLeft;
1152 /* if it is pasued, we keep the stream running but with silence only */
1153 if (rtp_channels->pause) {
1154 for(i=0; i<framesPerBuffer; i++ ) {
1158 rtp_channels->pause_duration += framesPerBuffer;
1163 rpci->out_diff_time = outTime - Pa_StreamTime(pa_stream) ;
1164 #else /* PORTAUDIO_API_1 */
1165 rpci->out_diff_time = (guint32)(SAMPLE_RATE) * (outTime->outputBufferDacTime - Pa_GetStreamTime(pa_stream)) ;
1166 #endif /* PORTAUDIO_API_1 */
1169 /* set the values if this is the first time */
1170 if (rpci->max_frame_index == 0) {
1171 init_rtp_channels_vals();
1175 framesLeft = rpci->max_frame_index - rpci->frame_index;
1177 (void) inputBuffer; /* Prevent unused variable warnings. */
1180 if( framesLeft < framesPerBuffer )
1182 framesToPlay = framesLeft;
1187 framesToPlay = framesPerBuffer;
1191 for( i=0; i<(unsigned int)framesToPlay; i++ )
1193 if (rpci->rci[0] && ( (rpci->frame_index >= rpci->start_index[0]) && (rpci->frame_index <= rpci->end_index[0]) )) {
1194 sample = g_array_index(rpci->rci[0]->samples, sample_t, rpci->rci[0]->frame_index++);
1195 *wptr++ = sample.val;
1200 if (rpci->rci[1] && ( (rpci->frame_index >= rpci->start_index[1]) && (rpci->frame_index <= rpci->end_index[1]) )) {
1201 sample = g_array_index(rpci->rci[1]->samples, sample_t, rpci->rci[1]->frame_index++);
1202 *wptr++ = sample.val;
1207 for( ; i<framesPerBuffer; i++ )
1212 rpci->frame_index += framesToPlay;
1217 /****************************************************************************/
1219 on_bt_check_clicked(GtkButton *button _U_, gpointer user_data)
1221 rtp_channel_info_t *rci = user_data;
1223 if (rci->selected) {
1224 if (rtp_channels->rci[0] == rci) {
1225 rtp_channels->rci[0] = NULL;
1226 rtp_channels->channel = 0;
1228 rtp_channels->rci[1] = NULL;
1229 rtp_channels->channel = 1;
1232 /* if there are already both channels selected, unselect the old one */
1233 if (rtp_channels->rci[rtp_channels->channel]) {
1234 /* we disconnect the signal temporarly to avoid been called back */
1235 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1236 gtk_toggle_button_set_active((GtkToggleButton *)rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1237 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1238 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1241 rtp_channels->rci[rtp_channels->channel] = rci;
1242 rtp_channels->channel = !(rtp_channels->channel);
1245 rci->selected = !(rci->selected);
1247 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1248 bt_state(TRUE, TRUE, FALSE, FALSE);
1251 /****************************************************************************/
1252 static void channel_draw(rtp_channel_info_t* rci)
1258 PangoLayout *small_layout;
1259 guint32 label_width, label_height;
1260 char label_string[MAX_TIME_LABEL];
1262 guint32 progbar_nextstep;
1263 int progbar_quantum;
1266 GdkColor red_color = {0, 65535, 0, 0};
1267 GdkColor amber_color = {0, 65535, 49152, 0};
1268 GdkColor white_color = {0, 65535, 65535, 65535};
1269 GdkColor black_color = {0, 0, 0, 0};
1271 GdkColor *draw_color_p;
1273 struct tm *timestamp;
1274 GtkAllocation widget_alloc;
1277 if (GDK_IS_DRAWABLE(rci->pixmap)) {
1278 gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
1279 /* Clear out old plot */
1280 cr = gdk_cairo_create (rci->pixmap);
1281 gdk_cairo_set_source_color (cr, &rci->bg_color[1+rci->call_num%MAX_NUM_COL_CONV]);
1282 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1287 small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
1288 pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
1290 /* calculated the pixel offset to display integer seconds */
1291 offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*SAMPLE_RATE/MULT;
1293 cr = gdk_cairo_create (rci->pixmap);
1294 cairo_set_line_width (cr, 1.0);
1295 cairo_move_to(cr, 0, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1296 cairo_line_to(cr, widget_alloc.width, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
1301 imax = MIN(widget_alloc.width,(gint)(rci->samples->len/MULT));
1303 /* we update the progress bar 100 times */
1305 /* Update the progress bar when it gets to this value. */
1306 progbar_nextstep = 0;
1307 /* When we reach the value that triggers a progress bar update,
1308 bump that value by this amount. */
1309 progbar_quantum = imax/100;
1311 for (i=0; i< imax; i++) {
1317 if (progbar_count >= progbar_nextstep) {
1318 g_assert(total_frames > 0);
1320 progbar_val = (gfloat) i / imax;
1322 update_progress_bar(progbar_val);
1324 progbar_nextstep += progbar_quantum;
1327 for (j=0; j<MULT; j++) {
1328 sample = g_array_index(rci->samples, sample_t, i*MULT+j);
1329 max = MAX(max, sample.val);
1330 min = MIN(min, sample.val);
1331 if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
1332 if (sample.status == S_WRONG_TIMESTAMP) status = S_WRONG_TIMESTAMP;
1333 if (sample.status == S_SILENCE) status = S_SILENCE;
1336 /* Set the line color, default is black */
1337 if (status == S_DROP_BY_JITT) {
1338 draw_color_p = &red_color;
1339 } else if (status == S_WRONG_TIMESTAMP) {
1340 draw_color_p = &amber_color;
1341 } else if (status == S_SILENCE) {
1342 draw_color_p = &white_color;
1344 draw_color_p = &black_color;
1347 /* if silence added by Wireshark, graphically show it with letter to indicate why */
1348 if ((status == S_DROP_BY_JITT) || (status == S_WRONG_TIMESTAMP) || (status == S_SILENCE)) {
1349 cr = gdk_cairo_create (rci->pixmap);
1350 cairo_set_line_width (cr, 1.0);
1351 gdk_cairo_set_source_color (cr, draw_color_p);
1352 cairo_move_to(cr, i+0.5, 0);
1353 cairo_line_to(cr, i+0.5, (widget_alloc.height-HEIGHT_TIME_LABEL)-1);
1358 if (status == S_DROP_BY_JITT) g_snprintf(label_string, MAX_TIME_LABEL,"D");
1359 if (status == S_WRONG_TIMESTAMP) g_snprintf(label_string, MAX_TIME_LABEL, "W");
1360 if (status == S_SILENCE) g_snprintf(label_string, MAX_TIME_LABEL, "S");
1362 pango_layout_set_text(small_layout, label_string, -1);
1363 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1364 cr = gdk_cairo_create (rci->pixmap);
1365 gdk_cairo_set_source_color (cr, draw_color_p);
1366 cairo_move_to (cr, i, 0);
1367 pango_cairo_show_layout (cr, small_layout);
1371 /* Draw a graphical representation of the sample */
1372 cr = gdk_cairo_create (rci->pixmap);
1373 cairo_set_line_width (cr, 1.0);
1374 gdk_cairo_set_source_color (cr, draw_color_p);
1375 cairo_move_to(cr, i+0.5, ( (0x7FFF+min) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1376 cairo_line_to(cr, i+0.5, ( (0x7FFF+max) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
1381 /* Draw the x-axis (seconds since beginning of packet flow for this call) */
1383 /* Draw tick mark and put a number for each whole second */
1384 if ( !((i*MULT)%(SAMPLE_RATE)) ) {
1385 cr = gdk_cairo_create (rci->pixmap);
1386 cairo_set_line_width (cr, 1.0);
1387 cairo_move_to(cr, i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1388 cairo_line_to(cr, i+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+4);
1393 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day))) {
1394 seconds = rci->start_time_abs.secs + i * MULT / SAMPLE_RATE;
1395 timestamp = localtime(&seconds);
1396 g_snprintf(label_string, MAX_TIME_LABEL, "%02d:%02d:%02d", timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec);
1398 g_snprintf(label_string, MAX_TIME_LABEL, "%.0f s", floor(rci->start_time/1000) + i*MULT/SAMPLE_RATE);
1401 pango_layout_set_text(small_layout, label_string, -1);
1402 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1403 cr = gdk_cairo_create (rci->pixmap);
1404 cairo_move_to (cr, i - offset - label_width/2, widget_alloc.height - label_height);
1405 pango_cairo_show_layout (cr, small_layout);
1410 /* Draw only a tick mark for half second intervals */
1411 } else if ( !((i*MULT)%(SAMPLE_RATE/2)) ) {
1412 cr = gdk_cairo_create (rci->pixmap);
1413 cairo_set_line_width (cr, 1.0);
1414 cairo_move_to(cr,i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
1415 cairo_line_to(cr, (i - offset)+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+2);
1426 /****************************************************************************/
1427 static gboolean expose_event_channels(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1429 rtp_channel_info_t *rci = user_data;
1432 if (gtk_widget_is_drawable(widget)){
1433 cr = gdk_cairo_create (gtk_widget_get_window(widget));
1434 gdk_cairo_set_source_pixmap (cr, rci->pixmap, event->area.x, event->area.y);
1435 cairo_rectangle (cr, event->area.x,event->area.y, event->area.width, event->area.height);
1444 /****************************************************************************/
1446 configure_event_channels(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1448 rtp_channel_info_t *rci = user_data;
1450 GtkAllocation widget_alloc;
1453 /* the first color is blue to highlight the selected item
1454 * the other collors are the same as in the Voip Graph analysys
1455 * to match the same calls
1457 static GdkColor col[MAX_NUM_COL_CONV+1] = {
1458 {0, 0x00FF, 0x00FF, 0xFFFF},
1459 {0, 0x90FF, 0xEEFF, 0x90FF},
1460 {0, 0xFFFF, 0xA0FF, 0x7AFF},
1461 {0, 0xFFFF, 0xB6FF, 0xC1FF},
1462 {0, 0xFAFF, 0xFAFF, 0xD2FF},
1463 {0, 0xFFFF, 0xFFFF, 0x33FF},
1464 {0, 0x66FF, 0xCDFF, 0xAAFF},
1465 {0, 0xE0FF, 0xFFFF, 0xFFFF},
1466 {0, 0xB0FF, 0xC4FF, 0xDEFF},
1467 {0, 0x87FF, 0xCEFF, 0xFAFF},
1468 {0, 0xD3FF, 0xD3FF, 0xD3FF}
1472 g_object_unref(rci->pixmap);
1476 gtk_widget_get_allocation(widget, &widget_alloc);
1477 rci->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
1479 widget_alloc.height,
1482 if ( GDK_IS_DRAWABLE(rci->pixmap) ){
1483 cr = gdk_cairo_create (rci->pixmap);
1484 cairo_set_source_rgb (cr, 1, 1, 1);
1485 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
1490 /* create gc's for the background color of each channel */
1491 for (i=0; i<MAX_NUM_COL_CONV+1; i++){
1492 rci->bg_color[i].pixel=col[i].pixel;
1493 rci->bg_color[i].red=col[i].red;
1494 rci->bg_color[i].green=col[i].green;
1495 rci->bg_color[i].blue=col[i].blue;
1504 /****************************************************************************/
1506 button_press_event_channel(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data)
1508 rtp_channel_info_t *rci = user_data;
1512 if (!rci->selected) {
1514 /* only select a new channels if we are in STOP */
1515 if (!rtp_channels->stop) return 0;
1517 /* if there are already both channels selected, unselect the old one */
1518 if (rtp_channels->rci[rtp_channels->channel]) {
1519 /* we disconnect the signal temporarly to avoid been called back */
1520 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1521 gtk_toggle_button_set_active((GtkToggleButton *) rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1522 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1523 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1526 /* we disconnect the signal temporarly to avoid been called back */
1527 g_signal_handlers_disconnect_by_func(rci->check_bt, on_bt_check_clicked, rci);
1528 gtk_toggle_button_set_active((GtkToggleButton *) rci->check_bt, TRUE);
1529 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1531 rtp_channels->rci[rtp_channels->channel] = rci;
1532 rtp_channels->channel = !(rtp_channels->channel);
1533 rci->selected = TRUE;
1535 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1536 bt_state(TRUE, TRUE, FALSE, FALSE);
1539 if (rci == rtp_channels->rci[0]) {
1545 rci->frame_index = (unsigned int) (event->x * MULT);
1547 prev_index = rtp_channels->frame_index;
1548 rtp_channels->frame_index = rtp_channels->start_index[this_channel] + rci->frame_index;
1549 rtp_channels->pause_duration += prev_index - rtp_channels->frame_index;
1553 /* change the index in the other channel if selected, according with the index position */
1554 if (rtp_channels->rci[!this_channel]) {
1555 init_rtp_channels_vals();
1557 if (rtp_channels->frame_index < rtp_channels->start_index[!this_channel]) {
1558 rtp_channels->rci[!this_channel]->frame_index = 0;
1559 } else if (rtp_channels->frame_index > rtp_channels->end_index[!this_channel]) {
1560 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->rci[!this_channel]->max_frame_index;
1562 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->frame_index - rtp_channels->start_index[!this_channel];
1565 init_rtp_channels_vals();
1568 rtp_channels->out_diff_time = 0;
1570 rci->cursor_catch = TRUE;
1572 /* redraw the cusrsor */
1578 /****************************************************************************/
1580 add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, guint *counter _U_ )
1582 GString *label = NULL;
1583 GtkWidget *viewport;
1586 /* create the channel draw area */
1587 rci->draw_area=gtk_drawing_area_new();
1589 rci->scroll_window=gtk_scrolled_window_new(NULL, NULL);
1591 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (rci->scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
1592 rci->h_scrollbar_adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window));
1595 gtk_widget_set_size_request(rci->draw_area, (gint)(rci->samples->len/MULT), CHANNEL_HEIGHT);
1598 viewport = gtk_viewport_new(rci->h_scrollbar_adjustment, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window)));
1599 gtk_container_add(GTK_CONTAINER(viewport), rci->draw_area);
1600 gtk_container_add(GTK_CONTAINER(rci->scroll_window), viewport);
1601 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1602 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1603 #if GTK_CHECK_VERSION(2,18,0)
1604 gtk_widget_set_can_focus(rci->draw_area, TRUE);
1606 GTK_WIDGET_SET_FLAGS(rci->draw_area, GTK_CAN_FOCUS);
1608 gtk_widget_grab_focus(rci->draw_area);
1610 gtk_box_pack_start(GTK_BOX (channels_vb), rci->scroll_window, FALSE, FALSE, 0);
1612 /* signals needed to handle backing pixmap */
1613 g_signal_connect(rci->draw_area, "expose_event", G_CALLBACK(expose_event_channels), rci);
1614 g_signal_connect(rci->draw_area, "configure_event", G_CALLBACK(configure_event_channels), rci);
1615 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1616 g_signal_connect(rci->draw_area, "button_press_event", G_CALLBACK(button_press_event_channel), rci);
1617 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
1620 label = g_string_new("");
1621 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
1622 g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Out of Seq: %d(%.1f%%) Wrong Timestamp: %d(%.1f%%)",
1623 get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
1624 get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1625 (double)rci->samples->len/SAMPLE_RATE,
1626 rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
1627 rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
1629 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%%)",
1630 get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
1631 get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1632 (double)rci->samples->len/SAMPLE_RATE,
1633 rci->drop_by_jitter_buff, (double)rci->drop_by_jitter_buff * 100 / (double)rci->num_packets,
1634 rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
1635 rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
1638 rci->check_bt = gtk_check_button_new_with_label(label->str);
1639 gtk_box_pack_start(GTK_BOX (channels_vb), rci->check_bt, FALSE, FALSE, 1);
1641 /* Create the Separator if it is not the last one */
1643 if (*counter < g_hash_table_size(rtp_channels_hash)) {
1644 rci->separator = gtk_hseparator_new();
1645 gtk_box_pack_start(GTK_BOX (channels_vb), rci->separator, FALSE, FALSE, 5);
1648 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1650 g_string_free(label, TRUE);
1653 /****************************************************************************/
1655 count_channel_frames(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
1657 total_frames += rci->samples->len;
1660 /****************************************************************************/
1667 /* we should never be here if we are in PLAY and !PAUSE */
1668 g_assert(!rtp_channels->stop && !rtp_channels->pause);
1670 /* if we are in PAUSE change the state */
1671 if (rtp_channels->pause) {
1672 rtp_channels->pause = FALSE;
1673 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1674 bt_state(FALSE, FALSE, TRUE, TRUE);
1676 /* if not PAUSE, then start to PLAY */
1679 err = Pa_OpenStream(
1681 paNoDevice, /* default input device */
1683 PA_SAMPLE_TYPE, /* 16 bit Integer input */
1685 Pa_GetDefaultOutputDeviceID(),
1686 NUM_CHANNELS, /* Stereo output */
1687 PA_SAMPLE_TYPE, /* 16 bit Integer output */
1689 SAMPLE_RATE, /* 8 kHz */
1691 0, /* number of buffers, if zero then use default minimum */
1692 paClipOff, /* we won't output out of range samples so don't bother clipping them */
1696 if( err != paNoError ) {
1697 const char *deviceName = "No Device";
1699 PaDeviceID device = Pa_GetDefaultOutputDeviceID();
1701 if (device != paNoDevice)
1703 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
1705 deviceName = deviceInfo->name;
1707 deviceName = "(No device info)";
1710 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1711 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1712 "Got this info from PortAudio Library:\n"
1713 " Default deviceName: %s (%d)", deviceName, device);
1714 gtk_dialog_run (GTK_DIALOG (dialog));
1715 gtk_widget_destroy (dialog);
1717 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1718 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1719 "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1720 gtk_dialog_run (GTK_DIALOG (dialog));
1721 gtk_widget_destroy (dialog);
1724 #else /* PORTAUDIO_API_1 */
1725 if (Pa_GetDefaultOutputDevice() != paNoDevice) {
1726 err = Pa_OpenDefaultStream(
1729 NUM_CHANNELS, /* Stereo output */
1730 PA_SAMPLE_TYPE, /* 16 bit Integer output */
1731 SAMPLE_RATE, /* 8 kHz */
1736 /* If the Default Host API doesn't even provide a device
1737 * we might as well go look for another.
1739 PaHostApiIndex host_api_count = Pa_GetHostApiCount();
1740 PaHostApiIndex default_host_api_index = Pa_GetDefaultHostApi();
1742 PaHostApiIndex host_api_index;
1743 const PaHostApiInfo *host_api_info;
1745 for (host_api_index=0; host_api_index<host_api_count; host_api_index++)
1747 /* Skip the default host API, that didn't work before */
1748 if (host_api_index == default_host_api_index)
1751 /* If we find a host API with a device, then take it. */
1752 host_api_info = Pa_GetHostApiInfo(host_api_index);
1753 if (host_api_info->deviceCount > 0)
1757 if (host_api_index<host_api_count)
1759 PaStreamParameters stream_parameters;
1760 stream_parameters.device = host_api_info->defaultOutputDevice;
1761 stream_parameters.channelCount = NUM_CHANNELS; /* Stereo output */
1762 stream_parameters.sampleFormat = PA_SAMPLE_TYPE; /* 16 bit Integer output */
1763 stream_parameters.suggestedLatency = 0;
1764 stream_parameters.hostApiSpecificStreamInfo = NULL;
1766 g_print("Trying Host API: %s\n", host_api_info->name);
1768 err = Pa_OpenStream(
1770 NULL, /* no input */
1772 SAMPLE_RATE, /* 8 kHz */
1774 paClipOff, /* we won't output out of range samples so don't bother clipping them */
1784 if( err != paNoError ) {
1785 PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
1788 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1789 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1790 "Can not even get the default host API from PortAudio Library.\n Error: %s",
1791 Pa_GetErrorText( hostApi ));
1792 gtk_dialog_run (GTK_DIALOG (dialog));
1793 gtk_widget_destroy (dialog);
1797 const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( hostApi );
1801 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1802 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1803 "Can not even get the host API info from PortAudio Library.");
1804 gtk_dialog_run (GTK_DIALOG (dialog));
1805 gtk_widget_destroy (dialog);
1809 const char *hostApiName = hostApiInfo->name;
1810 const char *deviceName = "No Device";
1812 PaDeviceIndex device = hostApiInfo->defaultOutputDevice;
1814 if (device != paNoDevice)
1816 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
1818 deviceName = deviceInfo->name;
1820 deviceName = "(No device info)";
1823 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1824 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1825 "Got this info from PortAudio Library:\n"
1826 " Default hostApiName: %s\n"
1827 " Default deviceName: %s (%d)", hostApiName, deviceName, device);
1828 gtk_dialog_run (GTK_DIALOG (dialog));
1829 gtk_widget_destroy (dialog);
1833 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1834 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1835 "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1836 gtk_dialog_run (GTK_DIALOG (dialog));
1837 gtk_widget_destroy (dialog);
1842 err = Pa_StartStream( pa_stream );
1843 if( err != paNoError ) {
1844 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1845 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1846 "Can not Start Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1847 gtk_dialog_run (GTK_DIALOG (dialog));
1848 gtk_widget_destroy (dialog);
1851 #if !PORTAUDIO_API_1
1852 rtp_channels->pa_start_time = Pa_GetStreamTime(pa_stream);
1853 #endif /* PORTAUDIO_API_1 */
1855 rtp_channels->stop = FALSE;
1857 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1858 bt_state(FALSE, FALSE, TRUE, TRUE);
1861 /* Draw the cursor in the graph */
1862 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
1866 /****************************************************************************/
1868 pause_channels(void)
1870 rtp_channels->pause = !(rtp_channels->pause);
1872 /* reactivate the cusrosr display if no in pause */
1873 if (!rtp_channels->pause) {
1874 /* Draw the cursor in the graph */
1875 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
1878 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1879 bt_state(FALSE, TRUE, FALSE, TRUE);
1882 /****************************************************************************/
1884 reset_rtp_channels(void)
1886 rtp_channels->channel = 0;
1887 rtp_channels->rci[0] = NULL;
1888 rtp_channels->rci[1] = NULL;
1889 rtp_channels->start_index[0] = 0;
1890 rtp_channels->start_index[1] = 0;
1891 rtp_channels->end_index[0] = 0;
1892 rtp_channels->end_index[1] = 0;
1893 rtp_channels->max_frame_index = 0;
1894 rtp_channels->frame_index = 0;
1895 rtp_channels->pause = FALSE;
1896 rtp_channels->pause_duration = 0;
1897 rtp_channels->stop = TRUE;
1898 rtp_channels->out_diff_time = 10000;
1901 /****************************************************************************/
1903 remove_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
1905 g_object_unref(rci->pixmap);
1906 gtk_widget_destroy(rci->draw_area);
1907 gtk_widget_destroy(rci->scroll_window);
1908 gtk_widget_destroy(rci->check_bt);
1910 gtk_widget_destroy(rci->separator);
1913 /****************************************************************************/
1915 reset_channels(void)
1918 if (rtp_channels_hash) {
1919 /* Remove the channels from the main window if there are there */
1920 g_hash_table_foreach( rtp_channels_hash, (GHFunc)remove_channel_to_window, NULL);
1923 /* destroy the rtp channels hash table */
1924 g_hash_table_destroy(rtp_channels_hash);
1925 rtp_channels_hash = NULL;
1929 reset_rtp_channels();
1933 /****************************************************************************/
1935 reset_rtp_player(void)
1937 /* Destroy the rtp channels */
1940 /* destroy the rtp streams hash table */
1941 if (rtp_streams_hash) {
1942 g_hash_table_destroy(rtp_streams_hash);
1943 rtp_streams_hash = NULL;
1946 /* destroy the rtp streams list */
1947 if (rtp_streams_list) {
1948 g_list_free (rtp_streams_list);
1949 rtp_streams_list = NULL;
1954 /****************************************************************************/
1956 decode_streams(void)
1958 guint statusbar_context;
1961 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1962 bt_state(FALSE, FALSE, FALSE, FALSE);
1966 progress_bar = gtk_progress_bar_new();
1967 gtk_widget_set_size_request(progress_bar, 100, -1);
1968 gtk_box_pack_start(GTK_BOX (stat_hbox), progress_bar, FALSE, FALSE, 2);
1969 gtk_widget_show(progress_bar);
1970 statusbar_context = gtk_statusbar_get_context_id((GtkStatusbar *) info_bar, "main");
1971 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Decoding RTP packets...");
1973 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
1975 /* reset the number of packet to be decoded, this is used for the progress bar */
1977 /* reset the Progress Bar count */
1980 /* Mark the RTP streams to be played using the selected VoipCalls. If voip_calls is NULL
1981 then this was called from "RTP Analysis" so mark all strams */
1982 if (rtp_streams_hash) {
1984 g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_rtp_stream_to_play, NULL);
1986 g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_all_rtp_stream_to_play, NULL);
1989 /* Decode the RTP streams and add them to the RTP channels to be played */
1990 g_list_foreach( rtp_streams_list, (GFunc)decode_rtp_stream, NULL);
1992 /* reset the number of frames to be displayed, this is used for the progress bar */
1994 /* Count the frames in all the RTP channels */
1995 if (rtp_channels_hash)
1996 g_hash_table_foreach( rtp_channels_hash, (GHFunc)count_channel_frames, NULL);
1998 /* reset the Progress Bar count again for the progress of creating the channels view */
2000 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2001 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Creating channels view...");
2003 /* Display the RTP channels in the window */
2005 if (rtp_channels_hash)
2006 g_hash_table_foreach( rtp_channels_hash, (GHFunc)add_channel_to_window, &counter);
2008 /* Resize the main scroll window to display no more than preferred (or default) max channels, scroll bar will be used if needed */
2010 if (prefs.rtp_player_max_visible < 1 || prefs.rtp_player_max_visible > 10)
2011 prefs.rtp_player_max_visible = RTP_PLAYER_DEFAULT_VISIBLE;
2013 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH,
2014 MIN(counter, prefs.rtp_player_max_visible) * (CHANNEL_HEIGHT+60));
2016 gtk_widget_show_all(main_scrolled_window);
2018 gtk_widget_destroy(progress_bar);
2019 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
2020 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2022 /* blank the status label */
2023 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
2025 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2026 bt_state(TRUE, FALSE, FALSE, FALSE);
2028 /* get the static jitter buffer from the spinner gui */
2029 new_jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
2033 /****************************************************************************/
2035 on_cb_view_as_time_of_day_clicked(GtkButton *button _U_, gpointer user_data _U_)
2037 /* Decode the streams again as if the decode button was pushed to update the time display */
2041 /****************************************************************************/
2043 on_cb_use_rtp_clicked(GtkToggleButton *button _U_, gpointer user_data _U_)
2045 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2046 bt_state(TRUE, FALSE, FALSE, FALSE);
2049 /****************************************************************************/
2051 on_bt_decode_clicked(GtkButton *button _U_, gpointer user_data _U_)
2056 /****************************************************************************/
2058 on_bt_play_clicked(GtkButton *button _U_, gpointer user_data _U_)
2063 /****************************************************************************/
2065 on_bt_pause_clicked(GtkButton *button _U_, gpointer user_data _U_)
2070 /****************************************************************************/
2072 on_bt_stop_clicked(GtkButton *button _U_, gpointer user_data _U_)
2077 /****************************************************************************/
2079 rtp_player_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
2084 /* Stop the channels if necesary */
2085 if(rtp_channels && (!rtp_channels->stop)){
2089 /* Destroy the rtp channels */
2092 g_free(rtp_channels);
2093 rtp_channels = NULL;
2095 /* Terminate the use of PortAudio library */
2096 err = Pa_Terminate();
2097 initialized = FALSE;
2098 if( err != paNoError ) {
2099 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2100 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2101 "Can not terminate the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2102 gtk_dialog_run (GTK_DIALOG (dialog));
2103 gtk_widget_destroy (dialog);
2106 gtk_widget_destroy(rtp_player_dlg_w);
2107 main_scrolled_window = NULL;
2108 rtp_player_dlg_w = NULL;
2111 /****************************************************************************/
2113 jitter_spinner_value_changed (GtkSpinButton *spinner _U_, gpointer user_data _U_)
2115 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2116 bt_state(TRUE, TRUE, FALSE, FALSE);
2119 /****************************************************************************/
2121 rtp_player_dlg_create(void)
2124 GtkWidget *hbuttonbox;
2125 GtkWidget *timestamp_hb;
2126 GtkWidget *h_jitter_buttons_box;
2127 GtkWidget *bt_close;
2128 GtkAdjustment *jitter_spinner_adj;
2130 const gchar *title_name_ptr;
2133 title_name_ptr = cf_get_display_name(&cfile);
2134 win_name = g_strdup_printf("%s - VoIP - RTP Player", title_name_ptr);
2136 rtp_player_dlg_w = dlg_window_new(win_name); /* transient_for top_level */
2137 gtk_window_set_destroy_with_parent (GTK_WINDOW(rtp_player_dlg_w), TRUE);
2138 gtk_window_set_position(GTK_WINDOW(rtp_player_dlg_w), GTK_WIN_POS_NONE);
2140 gtk_window_set_default_size(GTK_WINDOW(rtp_player_dlg_w), 400, 50);
2142 main_vb = gtk_vbox_new (FALSE, 0);
2143 gtk_container_add(GTK_CONTAINER(rtp_player_dlg_w), main_vb);
2144 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 2);
2146 main_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
2147 gtk_container_set_border_width (GTK_CONTAINER (main_scrolled_window), 4);
2148 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH, 0);
2150 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2151 gtk_container_add(GTK_CONTAINER(main_vb), main_scrolled_window);
2153 channels_vb = gtk_vbox_new (FALSE, 0);
2154 gtk_container_set_border_width (GTK_CONTAINER (channels_vb), 2);
2155 gtk_scrolled_window_add_with_viewport((GtkScrolledWindow *) main_scrolled_window, channels_vb);
2157 timestamp_hb = gtk_hbox_new(FALSE, 0);
2158 gtk_box_pack_start(GTK_BOX(main_vb), timestamp_hb, FALSE, FALSE, 0);
2159 cb_view_as_time_of_day = gtk_check_button_new_with_label("View as time of day");
2160 gtk_box_pack_start(GTK_BOX(timestamp_hb), cb_view_as_time_of_day, TRUE, FALSE, 0); /* Centered */
2161 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day), FALSE);
2162 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");
2163 g_signal_connect(cb_view_as_time_of_day, "toggled", G_CALLBACK(on_cb_view_as_time_of_day_clicked), NULL);
2165 h_jitter_buttons_box = gtk_hbox_new (FALSE, 0);
2166 gtk_container_set_border_width (GTK_CONTAINER (h_jitter_buttons_box), 10);
2167 gtk_box_pack_start (GTK_BOX(main_vb), h_jitter_buttons_box, FALSE, FALSE, 0);
2168 label = gtk_label_new("Jitter buffer [ms] ");
2169 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), label, FALSE, FALSE, 0);
2171 jitter_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (50, 0, 500, 5, 10, 0);
2172 jitter_spinner = gtk_spin_button_new (jitter_spinner_adj, 5, 0);
2173 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), jitter_spinner, FALSE, FALSE, 0);
2174 gtk_widget_set_tooltip_text (jitter_spinner, "The simulated jitter buffer in [ms]");
2175 g_signal_connect(GTK_OBJECT (jitter_spinner_adj), "value_changed", G_CALLBACK(jitter_spinner_value_changed), NULL);
2177 cb_use_rtp_timestamp = gtk_check_button_new_with_label("Use RTP timestamp");
2178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp), FALSE);
2179 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), cb_use_rtp_timestamp, FALSE, FALSE, 10);
2180 g_signal_connect(cb_use_rtp_timestamp, "toggled", G_CALLBACK(on_cb_use_rtp_clicked), NULL);
2181 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");
2184 hbuttonbox = gtk_hbutton_box_new ();
2185 gtk_box_pack_start (GTK_BOX (h_jitter_buttons_box), hbuttonbox, TRUE, TRUE, 0);
2186 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
2187 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 10);
2189 bt_decode = gtk_button_new_from_stock(WIRESHARK_STOCK_DECODE);
2190 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_decode);
2191 g_signal_connect(bt_decode, "clicked", G_CALLBACK(on_bt_decode_clicked), NULL);
2192 gtk_widget_set_tooltip_text (bt_decode, "Decode the RTP stream(s)");
2194 bt_play = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
2195 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_play);
2196 g_signal_connect(bt_play, "clicked", G_CALLBACK(on_bt_play_clicked), NULL);
2197 gtk_widget_set_tooltip_text (bt_play, "Play the RTP channel(s)");
2199 bt_pause = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE);
2200 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_pause);
2201 g_signal_connect(bt_pause, "clicked", G_CALLBACK(on_bt_pause_clicked), NULL);
2202 gtk_widget_set_tooltip_text (bt_pause, "Pause the RTP channel(s)");
2204 bt_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
2205 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_stop);
2206 g_signal_connect(bt_stop, "clicked", G_CALLBACK(on_bt_stop_clicked), NULL);
2207 gtk_widget_set_tooltip_text (bt_stop, "Stop the RTP channel(s)");
2209 bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2210 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
2211 #if GTK_CHECK_VERSION(2,18,0)
2212 gtk_widget_set_can_default(bt_close, TRUE);
2214 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
2216 gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
2217 window_set_cancel_button(rtp_player_dlg_w, bt_close, window_cancel_button_cb);
2219 g_signal_connect(rtp_player_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2220 g_signal_connect(rtp_player_dlg_w, "destroy", G_CALLBACK(rtp_player_on_destroy), NULL);
2223 hbuttonbox = gtk_hbutton_box_new ();
2225 /* Filter/status hbox */
2226 stat_hbox = gtk_hbox_new(FALSE, 1);
2227 gtk_container_set_border_width(GTK_CONTAINER(stat_hbox), 0);
2230 info_bar = gtk_statusbar_new();
2231 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
2233 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
2235 /* statusbar hbox */
2236 gtk_box_pack_start(GTK_BOX(main_vb), stat_hbox, FALSE, TRUE, 0);
2238 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2239 bt_state(TRUE, FALSE, FALSE, FALSE);
2241 gtk_widget_show_all(rtp_player_dlg_w);
2243 /* Force gtk to redraw the window before starting decoding the packet */
2244 while (g_main_context_iteration(NULL, FALSE));
2249 /****************************************************************************/
2251 rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap)
2256 if (initialized) return;
2259 voip_calls = voip_calls_tap;
2260 err = Pa_Initialize();
2261 if( err != paNoError ) {
2262 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2263 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2264 "Can not Initialize the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2265 gtk_dialog_run (GTK_DIALOG (dialog));
2266 gtk_widget_destroy (dialog);
2267 initialized = FALSE;
2271 new_jitter_buff = -1;
2273 #ifdef HAVE_G729_G723
2274 /* Initialize the G729 and G723 decoders */
2277 #endif /* HAVE_G729_G723 */
2279 if (!rtp_channels) {
2280 rtp_channels = g_malloc(sizeof(rtp_play_channels_t));
2283 reset_rtp_channels();
2286 g_print("Pa_GetHostApiCount() = %d\n", Pa_GetHostApiCount());
2287 g_print("Pa_GetDefaultHostApi() = %d\n", Pa_GetDefaultHostApi());
2289 if ((Pa_GetHostApiCount() >= 0) && (Pa_GetDefaultHostApi() >= 0))
2292 PaHostApiIndex api_index;
2293 const PaHostApiInfo *api_info = Pa_GetHostApiInfo( (unsigned int)Pa_GetDefaultHostApi() );
2294 g_print("Default PaHostApiInfo.type = %d (%s)\n", api_info->type, api_info->name);
2296 for (i=0; i<(unsigned int)Pa_GetHostApiCount(); i++)
2298 api_info = Pa_GetHostApiInfo( i );
2299 g_print("PaHostApiInfo[%u].type = %d (%s)\n", i, api_info->type, api_info->name);
2302 api_index = Pa_HostApiTypeIdToHostApiIndex( paALSA );
2305 g_print("api_index for paALSA not found (%d)\n", api_index);
2309 api_info = Pa_GetHostApiInfo( (unsigned int)api_index );
2310 g_print("This should be ALSA: %s\n", api_info->name);
2315 /* create the dialog window */
2316 rtp_player_dlg_create();
2320 #endif /* HAVE_LIBPORTAUDIO */