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 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() create 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"
87 #define min(a,b) (((a)<(b))?(a):(b))
90 #define max(a,b) (((a)>(b))?(a):(b))
93 /*define this symbol to compile with G729 and G723 codecs*/
94 /*#define HAVE_G729_G723 1*/
97 #include "codecs/G729/G729decode.h"
98 #include "codecs/G723/G723decode.h"
99 #endif /* HAVE_G729_G723 */
101 static gboolean initialized = FALSE;
103 voip_calls_tapinfo_t *voip_calls = NULL;
105 /* Hash table with all the RTP streams */
106 static GHashTable* rtp_streams_hash = NULL;
108 /* List with all the RTP streams (this is used to decode them as it is sorted)*/
109 static GList* rtp_streams_list = NULL;
112 static GtkWidget *rtp_player_dlg_w;
113 static GtkWidget *channels_vb;
114 static GtkWidget *main_scrolled_window = NULL;
115 static GtkWidget *jitter_spinner;
116 static GtkWidget *cb_use_rtp_timestamp;
117 static GtkWidget *bt_decode;
118 static GtkWidget *bt_play;
119 static GtkWidget *bt_pause;
120 static GtkWidget *bt_stop;
121 static GtkWidget *progress_bar;
122 static GtkWidget *info_bar;
123 static GtkWidget *stat_hbox;
125 static guint32 total_packets;
126 static guint32 total_frames;
127 static guint32 progbar_count;
129 static int new_jitter_buff;
131 /* a hash table with the RTP streams to play per audio channel */
132 static GHashTable *rtp_channels_hash = NULL;
134 /* Port Audio staff */
135 #define SAMPLE_RATE (8000)
136 #define NUM_CHANNELS (2)
138 #define PA_SAMPLE_TYPE paInt16
139 typedef gint16 SAMPLE;
140 #define SAMPLE_SILENCE (0)
141 #define FRAMES_PER_BUFFER (512)
143 typedef struct _sample_t {
149 #define S_DROP_BY_JITT 1
150 #define S_WRONG_SEQ 2
152 /* Display channels constants */
154 #define CHANNEL_WIDTH 500
155 #define CHANNEL_HEIGHT 100
156 #define MAX_TIME_LABEL 10
157 #define HEIGHT_TIME_LABEL 18
158 #define MAX_NUM_COL_CONV 10
161 PortAudioStream *pa_stream;
162 #else /* PORTAUDIO_API_1 */
164 #endif /* PORTAUDIO_API_1 */
166 /* defines a RTP stream */
167 typedef struct _rtp_stream_info {
173 guint32 first_frame_number; /* first RTP frame for the stream */
174 double start_time; /* RTP stream start time in ms */
177 GList* rtp_packets_list; /* List of RTP packets in the stream */
182 /* defines the RTP streams to be played in an audio channel */
183 typedef struct _rtp_channel_info {
184 double start_time; /* RTP stream start time in ms */
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 max_frame_index;
194 GtkWidget *separator;
195 GtkWidget *scroll_window;
196 GtkWidget *draw_area;
198 GtkAdjustment *h_scrollbar_adjustment;
199 GdkPixbuf* cursor_pixbuf;
201 PaTimestamp cursor_prev;
202 #else /* PORTAUDIO_API_1 */
204 #endif /* PORTAUDIO_API_1 */
205 GdkGC *bg_gc[MAX_NUM_COL_CONV+1];
206 gboolean cursor_catch;
207 rtp_stream_info_t *first_stream; /* This is the first RTP stream in the channel */
209 } rtp_channel_info_t;
211 /* defines a RTP packet */
212 typedef struct _rtp_packet {
213 struct _rtp_info *info; /* the RTP dissected info */
214 double arrive_offset; /* arrive offset time since the begining of the stream in ms */
215 guint8* payload_data;
218 /* defines the two RTP channels to be played */
219 typedef struct _rtp_play_channles {
220 rtp_channel_info_t* rci[2]; /* Channels to be played */
221 guint32 start_index[2];
222 guint32 end_index[2];
224 guint32 max_frame_index;
228 gint32 pause_duration;
230 PaTimestamp out_diff_time;
231 #else /* PORTAUDIO_API_1 */
232 PaTime out_diff_time;
233 PaTime pa_start_time;
234 #endif /* PORTAUDIO_API_1 */
235 } rtp_play_channels_t;
237 /* The two RTP channles to play */
238 static rtp_play_channels_t *rtp_channels = NULL;
240 typedef struct _rtp_decoder_t {
241 codec_handle_t handle;
246 /****************************************************************************/
248 rtp_key_destroy(gpointer key)
254 /****************************************************************************/
256 rtp_channel_value_destroy(gpointer rci_arg)
258 rtp_channel_info_t *rci = rci_arg;
260 g_array_free(rci->samples, TRUE);
265 /****************************************************************************/
267 rtp_stream_value_destroy(gpointer rsi_arg)
269 rtp_stream_info_t *rsi = rsi_arg;
270 GList* rtp_packets_list;
273 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
274 while (rtp_packets_list)
276 rp = rtp_packets_list->data;
279 g_free(rp->payload_data);
283 rtp_packets_list = g_list_next(rtp_packets_list);
285 g_free((void *)(rsi->src_addr.data));
286 g_free((void *)(rsi->dest_addr.data));
291 /****************************************************************************/
293 rtp_decoder_value_destroy(gpointer dec_arg)
295 rtp_decoder_t *dec = dec_arg;
298 codec_release(dec->handle, dec->context);
302 /****************************************************************************/
304 set_sensitive_check_bt(gchar *key _U_ , rtp_channel_info_t *rci, guint *stop _U_ )
306 gtk_widget_set_sensitive(rci->check_bt, !(*stop));
309 /****************************************************************************/
311 bt_state(gboolean decode, gboolean play, gboolean pause, gboolean stop)
313 gboolean new_jitter_value = FALSE;
314 gboolean false_val = FALSE;
316 gtk_widget_set_sensitive(bt_decode, decode);
317 gtk_widget_set_sensitive(cb_use_rtp_timestamp, decode);
318 if (GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp)->active) {
319 gtk_widget_set_sensitive(jitter_spinner, FALSE);
321 gtk_widget_set_sensitive(jitter_spinner, decode);
324 if (new_jitter_buff != (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner)) {
325 new_jitter_value = TRUE;
328 /* set the sensitive state of play only if there is a channel selected */
329 if ( play && (rtp_channels->rci[0] || rtp_channels->rci[1]) && !new_jitter_value) {
330 gtk_widget_set_sensitive(bt_play, TRUE);
332 gtk_widget_set_sensitive(bt_play, FALSE);
335 if (!new_jitter_value) {
336 gtk_widget_set_sensitive(bt_pause, pause);
337 gtk_widget_set_sensitive(bt_stop, stop);
339 /* Set sensitive to the check buttons based on the STOP state */
340 if (rtp_channels_hash)
341 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &stop);
343 gtk_widget_set_sensitive(bt_pause, FALSE);
344 gtk_widget_set_sensitive(bt_stop, FALSE);
346 if (rtp_channels_hash)
347 g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &false_val);
351 /****************************************************************************/
353 add_rtp_packet(const struct _rtp_info *rtp_info, packet_info *pinfo)
355 rtp_stream_info_t *stream_info = NULL;
356 rtp_packet_t *new_rtp_packet;
357 GString *key_str = NULL;
359 /* create the the streams hash if it doen't exist */
360 if (!rtp_streams_hash)
361 rtp_streams_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_stream_value_destroy);
363 /* Create a hash key to lookup in the RTP streams hash table
364 * uses: src_ip:src_port dst_ip:dst_port ssrc
366 key_str = g_string_new("");
367 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(pinfo->src)),
368 pinfo->srcport, get_addr_name(&(pinfo->dst)),
369 pinfo->destport, rtp_info->info_sync_src );
371 /* lookup for this rtp packet in the stream hash table*/
372 stream_info = g_hash_table_lookup( rtp_streams_hash, key_str->str);
374 /* if it is not in the hash table, create a new stream */
375 if (stream_info==NULL) {
376 stream_info = g_malloc(sizeof(rtp_stream_info_t));
377 COPY_ADDRESS(&(stream_info->src_addr), &(pinfo->src));
378 stream_info->src_port = pinfo->srcport;
379 COPY_ADDRESS(&(stream_info->dest_addr), &(pinfo->dst));
380 stream_info->dest_port = pinfo->destport;
381 stream_info->ssrc = rtp_info->info_sync_src;
382 stream_info->rtp_packets_list = NULL;
383 stream_info->first_frame_number = pinfo->fd->num;
384 stream_info->start_time = nstime_to_msec(&pinfo->fd->rel_ts);
385 stream_info->call_num = 0;
386 stream_info->play = FALSE;
387 stream_info->num_packets = 0;
389 g_hash_table_insert(rtp_streams_hash, g_strdup(key_str->str), stream_info);
391 /* Add the element to the List too. The List is used to decode the packets because it is sorted */
392 rtp_streams_list = g_list_append(rtp_streams_list, stream_info);
395 /* increment the number of packets in this stream, this is used for the progress bar and statistics*/
396 stream_info->num_packets++;
398 /* Add the RTP packet to the list */
399 new_rtp_packet = g_malloc(sizeof(rtp_packet_t));
400 new_rtp_packet->info = g_malloc(sizeof(struct _rtp_info));
402 memcpy(new_rtp_packet->info, rtp_info, sizeof(struct _rtp_info));
403 new_rtp_packet->arrive_offset = nstime_to_msec(&pinfo->fd->rel_ts) - stream_info->start_time;
404 /* copy the RTP payload to the rtp_packet to be decoded later */
405 if (rtp_info->info_payload_len) {
406 new_rtp_packet->payload_data = g_malloc(rtp_info->info_payload_len);
407 memcpy(new_rtp_packet->payload_data, &(rtp_info->info_data[rtp_info->info_payload_offset]), rtp_info->info_payload_len);
409 new_rtp_packet->payload_data = NULL;
412 stream_info->rtp_packets_list = g_list_append(stream_info->rtp_packets_list, new_rtp_packet);
414 g_string_free(key_str, TRUE);
417 /****************************************************************************/
418 /* Mark the RTP stream to be played. Use the voip_calls graph to see if the
419 * setup_frame is there and then if the associated voip_call is selected.
422 mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
425 graph_analysis_item_t *graph_item;
426 GList* voip_calls_list;
427 voip_calls_info_t *tmp_voip_call;
429 /* Reset the "to be play" value because the user can close and reopen the RTP Player window
430 * and the streams are nor reset in that case
434 /* and associate the RTP stream with a call using the first RTP in the stream*/
435 graph_list = g_list_first(voip_calls->graph_analysis->list);
438 graph_item = graph_list->data;
439 if (rsi->first_frame_number == graph_item->frame_num) {
440 rsi->call_num = graph_item->conv_num;
441 /* if it is in the graph list, then check if the voip_call is selected */
442 voip_calls_list = g_list_first(voip_calls->callsinfo_list);
443 while (voip_calls_list)
445 tmp_voip_call = voip_calls_list->data;
446 if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
448 total_packets += rsi->num_packets;
451 voip_calls_list = g_list_next(voip_calls_list);
455 graph_list = g_list_next(graph_list);
460 /****************************************************************************/
461 /* Decode a RTP packet
462 * Return the number of decoded bytes
465 decode_rtp_packet(rtp_packet_t *rp, SAMPLE **out_buff, GHashTable *decoders_hash)
467 unsigned int payload_type;
469 rtp_decoder_t *decoder;
470 SAMPLE *tmp_buff = NULL;
472 int decoded_bytes = 0;
474 if ((rp->payload_data == NULL) || (rp->info->info_payload_len == 0) ) {
478 payload_type = rp->info->info_payload_type;
480 /* Look for registered codecs */
481 decoder = g_hash_table_lookup(decoders_hash, GUINT_TO_POINTER(payload_type));
482 if (!decoder) { /* Put either valid or empty decoder into the hash table */
483 decoder = g_malloc(sizeof(rtp_decoder_t));
484 decoder->handle = NULL;
485 decoder->context = NULL;
486 p = match_strval(payload_type, rtp_payload_type_short_vals);
488 decoder->handle = find_codec(p);
490 decoder->context = codec_init(decoder->handle);
492 g_hash_table_insert(decoders_hash, GUINT_TO_POINTER(payload_type), decoder);
494 if (decoder->handle) { /* Decode with registered codec */
495 tmp_buff_len = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, NULL, NULL);
496 tmp_buff = g_malloc(tmp_buff_len);
497 decoded_bytes = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, tmp_buff, &tmp_buff_len);
498 *out_buff = tmp_buff;
499 return decoded_bytes;
502 /* Try to decode with built-in codec */
504 switch (payload_type) {
506 case PT_PCMU: /* G.711 u-law */
507 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
508 decodeG711u(rp->payload_data, rp->info->info_payload_len,
509 tmp_buff, &decoded_bytes);
512 case PT_PCMA: /* G.711 A-law */
513 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
514 decodeG711a(rp->payload_data, rp->info->info_payload_len,
515 tmp_buff, &decoded_bytes);
518 #ifdef HAVE_G729_G723
519 case PT_G729: /* G.729 */
520 /* G729 8kbps => 64kbps/8kbps = 8 */
521 /* Compensate for possible 2 octet SID frame (G.729B) */
522 tmp_buff = g_malloc(sizeof(SAMPLE) * ((rp->info->info_payload_len + 8) / 10) * 80);
523 decodeG729(rp->payload_data, rp->info->info_payload_len,
524 tmp_buff, &decoded_bytes);
527 case PT_G723: /* G.723 */
528 if (rp->info->info_payload_len%24 == 0) /* G723 High 6.4kbps */
529 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 10); /* G723 High 64kbps/6.4kbps = 10 */
530 else if (rp->info->info_payload_len%20 == 0) /* G723 Low 5.3kbps */
531 tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 13); /* G723 High 64kbps/5.3kbps = 13 */
535 decodeG723(rp->payload_data, rp->info->info_payload_len,
536 tmp_buff, &decoded_bytes);
538 #endif /* HAVE_G729_G723 */
542 * XXX - return an error here, so the user gets told that
543 * we don't support this codec!
548 *out_buff = tmp_buff;
549 return decoded_bytes;
552 /****************************************************************************/
554 update_progress_bar(gfloat percentage)
557 gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), percentage);
559 /* Force gtk to redraw the window before starting decoding the packet */
560 while (gtk_events_pending())
561 gtk_main_iteration();
564 /****************************************************************************/
565 /* Decode the RTP streams and add them to the RTP channels struct
568 decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr _U_)
570 GString *key_str = NULL;
571 rtp_channel_info_t *rci;
572 gboolean first = TRUE;
573 GList* rtp_packets_list;
578 double rtp_time_prev;
580 double arrive_time_prev;
582 double start_rtp_time = 0;
586 double total_time_prev;
587 gint32 silence_frames;
594 int decoded_bytes_prev;
596 SAMPLE *out_buff = NULL;
600 guint32 start_timestamp;
601 GHashTable *decoders_hash = NULL;
603 guint32 progbar_nextstep;
608 silence.status = S_NORMAL;
610 /* skip it if we are not going to play it */
611 if (rsi->play == FALSE) {
615 /* get the static jitter buffer from the spinner gui */
616 jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
618 /* Create a hash key to lookup in the RTP channels hash
619 * uses: src_ip:src_port dst_ip:dst_port call_num
621 key_str = g_string_new("");
622 g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(rsi->src_addr)),
623 rsi->src_port, get_addr_name(&(rsi->dest_addr)),
624 rsi->dest_port, rsi->call_num );
626 /* create the rtp_channels_hash table if it doesn't exist */
627 if (!rtp_channels_hash) {
628 rtp_channels_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_channel_value_destroy);
631 /* lookup for this stream in the channel hash table */
632 rci = g_hash_table_lookup( rtp_channels_hash, key_str->str);
634 /* ..if it is not in the hash, create an entry */
636 rci = g_malloc(sizeof(rtp_channel_info_t));
637 rci->call_num = rsi->call_num;
638 rci->start_time = rsi->start_time;
639 rci->end_time = rsi->start_time;
640 rci->selected = FALSE;
641 rci->frame_index = 0;
642 rci->drop_by_jitter_buff = 0;
644 rci->max_frame_index = 0;
645 rci->samples = g_array_new (FALSE, FALSE, sizeof(sample_t));
646 rci->check_bt = NULL;
647 rci->separator = NULL;
648 rci->draw_area = NULL;
650 rci->h_scrollbar_adjustment = NULL;
651 rci->cursor_pixbuf = NULL;
652 rci->cursor_prev = 0;
653 rci->cursor_catch = FALSE;
654 rci->first_stream = rsi;
655 rci->num_packets = rsi->num_packets;
656 g_hash_table_insert(rtp_channels_hash, g_strdup(key_str->str), rci);
658 /* Add silence between the two streams if needed */
659 silence_frames = (gint32)( ((rsi->start_time - rci->end_time)/1000)*SAMPLE_RATE );
660 for (i = 0; i< silence_frames; i++) {
661 g_array_append_val(rci->samples, silence);
663 rci->num_packets += rsi->num_packets;
666 /* decode the RTP stream */
671 decoded_bytes_prev = 0;
673 arrive_time = start_time = 0;
674 arrive_time_prev = 0;
684 decoders_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rtp_decoder_value_destroy);
686 /* we update the progress bar 100 times */
688 /* Update the progress bar when it gets to this value. */
689 progbar_nextstep = 0;
690 /* When we reach the value that triggers a progress bar update,
691 bump that value by this amount. */
692 progbar_quantum = total_packets/100;
696 rtp_packets_list = g_list_first(rsi->rtp_packets_list);
697 while (rtp_packets_list)
700 if (progbar_count >= progbar_nextstep) {
701 g_assert(total_packets > 0);
703 progbar_val = (gfloat) progbar_count / total_packets;
705 update_progress_bar(progbar_val);
707 progbar_nextstep += progbar_quantum;
711 rp = rtp_packets_list->data;
713 start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
715 rtp_time_prev = start_rtp_time;
717 seq = rp->info->info_seq_num - 1;
720 decoded_bytes = decode_rtp_packet(rp, &out_buff, decoders_hash);
721 if (decoded_bytes == 0) {
722 seq = rp->info->info_seq_num;
725 rtp_time = (double)(rp->info->info_timestamp-start_timestamp)/SAMPLE_RATE - start_rtp_time;
727 if (GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp)->active) {
728 arrive_time = rtp_time;
730 arrive_time = (double)rp->arrive_offset/1000 - start_time;
733 if (rp->info->info_seq_num != seq+1){
735 status = S_WRONG_SEQ;
737 seq = rp->info->info_seq_num;
739 diff = arrive_time - rtp_time;
741 delay = diff - prev_diff;
743 if (delay<0) delay = -delay;
745 if (diff<0) diff = -diff;
747 total_time = (double)rp->arrive_offset/1000;
749 printf("seq = %d arr = %f abs_diff = %f index = %d tim = %f ji=%d jb=%f\n",rp->info->info_seq_num,
750 total_time, diff, rci->samples->len, ((double)rci->samples->len/8000 - total_time)*1000, 0,
751 (mean_delay + 4*variation)*1000);
754 /* if the jitter buffer was exceeded */
755 if ( diff*1000 > jitter_buff ) {
757 printf("Packet drop by jitter buffer exceeded\n");
759 rci->drop_by_jitter_buff++;
760 status = S_DROP_BY_JITT;
762 /* if there was a silence period (more than two packetization period) resync the source */
763 if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
765 printf("Resync...\n");
768 silence_frames = (gint32)((arrive_time - arrive_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
769 for (i = 0; i< silence_frames; i++) {
770 silence.status = status;
771 g_array_append_val(rci->samples, silence);
773 /* only mark the fisrt in the silence that has the previos problem (S_DROP_BY_JITT or S_WRONG_SEQ ) */
777 decoded_bytes_prev = 0;
778 start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
780 start_time = (double)rp->arrive_offset/1000;
784 /* Add silence if it is necessary */
785 silence_frames = (gint32)((rtp_time - rtp_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
786 for (i = 0; i< silence_frames; i++) {
787 silence.status = status;
788 g_array_append_val(rci->samples, silence);
790 /* only mark the fisrt in the silence that has the previos problem (S_DROP_BY_JITT or S_WRONG_SEQ ) */
797 for (i = 0; i< (decoded_bytes/2); i++) {
798 sample.val = out_buff[i];
799 sample.status = status;
800 g_array_append_val(rci->samples, sample);
803 rtp_time_prev = rtp_time;
804 pack_period = (double)(decoded_bytes/2)/SAMPLE_RATE;
805 decoded_bytes_prev = decoded_bytes;
806 arrive_time_prev = arrive_time;
814 rtp_packets_list = g_list_next (rtp_packets_list);
817 rci->max_frame_index = rci->samples->len;
818 rci->end_time = rci->start_time + ((double)rci->samples->len/SAMPLE_RATE)*1000;
820 g_string_free(key_str, TRUE);
821 g_hash_table_destroy(decoders_hash);
824 /****************************************************************************/
826 h_scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
828 rtp_channel_info_t *rci = (rtp_channel_info_t *)user_data;
829 rci->cursor_catch = TRUE;
833 static gboolean draw_cursors(gpointer data);
835 /****************************************************************************/
842 /* we should never be here if we are already in STOP */
843 if(rtp_channels->stop){
847 rtp_channels->stop = TRUE;
848 /* force a draw_cursor to stop it */
851 err = Pa_StopStream(pa_stream);
853 if( err != paNoError ) {
854 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
855 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
856 "Can not Stop Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
857 gtk_dialog_run (GTK_DIALOG (dialog));
858 gtk_widget_destroy (dialog);
862 err = Pa_CloseStream(pa_stream);
863 if( err != paNoError ) {
864 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
865 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
866 "Can not Close Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
867 gtk_dialog_run (GTK_DIALOG (dialog));
868 gtk_widget_destroy (dialog);
871 pa_stream = NULL; /* to catch errors better */
873 rtp_channels->start_index[0] = 0;
874 rtp_channels->start_index[1] = 0;
875 rtp_channels->end_index[0] = 0;
876 rtp_channels->end_index[1] = 0;
877 rtp_channels->max_frame_index = 0;
878 rtp_channels->frame_index = 0;
879 rtp_channels->pause = FALSE;
880 rtp_channels->pause_duration = 0;
881 rtp_channels->stop = TRUE;
882 rtp_channels->out_diff_time = 10000;
884 if (rtp_channels->rci[0]) rtp_channels->rci[0]->frame_index = 0;
885 if (rtp_channels->rci[1]) rtp_channels->rci[1]->frame_index = 0;
887 /* set the sensitive state of the buttons (decode, play, pause, stop) */
888 bt_state(TRUE, TRUE, FALSE, FALSE);
892 /****************************************************************************/
893 /* Draw a cursor in a channel graph
896 draw_channel_cursor(rtp_channel_info_t *rci, guint32 start_index)
900 #else /* PORTAUDIO_API_1 */
902 #endif /* PORTAUDIO_API_1 */
908 index = Pa_StreamTime( pa_stream ) - rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
909 #else /* PORTAUDIO_API_1 */
910 index = ((guint32)(SAMPLE_RATE) * (Pa_GetStreamTime(pa_stream)-rtp_channels->pa_start_time))- rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
911 #endif /* PORTAUDIO_API_1 */
914 /* If we finished playing both channels, then stop them */
915 if ( (rtp_channels && (!rtp_channels->stop) && (!rtp_channels->pause)) && (index > rtp_channels->max_frame_index) ) {
920 /* If only this channel finished, then return */
921 if (index > rci->max_frame_index) {
925 /* draw the previous saved pixbuf line */
926 if (rci->cursor_pixbuf && (rci->cursor_prev>=0)) {
928 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);
930 gdk_draw_drawable(rci->draw_area->window,
931 rci->draw_area->style->fg_gc[GTK_WIDGET_STATE(rci->draw_area)],
933 (int) (rci->cursor_prev/MULT), 0,
934 (int) (rci->cursor_prev/MULT), 0,
935 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
937 g_object_unref(rci->cursor_pixbuf);
938 rci->cursor_pixbuf = NULL;
941 if (index>0 && (rci->cursor_prev>=0)) {
942 rci->cursor_pixbuf = gdk_pixbuf_get_from_drawable(NULL, rci->pixmap, NULL, (int) (index/MULT), 0, 0, 0, 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
944 gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
948 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
950 gdk_draw_drawable(rci->draw_area->window,
951 rci->draw_area->style->fg_gc[GTK_WIDGET_STATE(rci->draw_area)],
953 (int) (index/MULT), 0,
954 (int) (index/MULT), 0,
955 1, rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
958 /* Disconnect the scroll bar "value" signal to not be called */
959 g_signal_handlers_disconnect_by_func(rci->h_scrollbar_adjustment, h_scrollbar_changed, rci);
961 /* Move the horizontal scroll bar */
962 /* if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
963 (index/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
964 for (i=1; i<10; i++) {
965 rci->h_scrollbar_adjustment->value += rci->h_scrollbar_adjustment->page_size/10;
966 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
971 if (!rci->cursor_catch) {
972 if (index/MULT < rci->h_scrollbar_adjustment->page_size/2) {
973 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
974 } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
975 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
977 rci->h_scrollbar_adjustment->value = index/MULT - rci->h_scrollbar_adjustment->page_size/2;
980 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
981 } else if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
982 (index/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
983 rci->cursor_catch = FALSE;
984 for (i=1; i<10; i++) {
985 rci->h_scrollbar_adjustment->value = min(rci->h_scrollbar_adjustment->upper-rci->h_scrollbar_adjustment->page_size, rci->h_scrollbar_adjustment->value + (rci->h_scrollbar_adjustment->page_size/20));
986 gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
992 /* Connect back the "value" scroll signal */
993 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
996 /* if (index/MULT < rci->h_scrollbar_adjustment->page_increment) {
997 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
998 } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size + rci->h_scrollbar_adjustment->page_increment)) {
999 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1001 if ( (index/MULT < rci->h_scrollbar_adjustment->value) || (index/MULT > (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
1002 rci->h_scrollbar_adjustment->value = index/MULT;
1007 /* if (index/MULT < rci->h_scrollbar_adjustment->page_size/2) {
1008 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
1009 } else if (index/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
1010 rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
1012 rci->h_scrollbar_adjustment->value = index/MULT - rci->h_scrollbar_adjustment->page_size/2;
1015 /* gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
1017 rci->cursor_prev = index;
1020 /****************************************************************************/
1021 /* Move and draw the cursor in the graph
1024 draw_cursors(gpointer data _U_)
1026 if (!rtp_channels) return FALSE;
1028 /* Draw and move each of the two channels */
1029 draw_channel_cursor(rtp_channels->rci[0], rtp_channels->start_index[0]);
1030 draw_channel_cursor(rtp_channels->rci[1], rtp_channels->start_index[1]);
1032 if ((rtp_channels->stop) || (rtp_channels->pause)) return FALSE;
1037 /****************************************************************************/
1039 init_rtp_channels_vals(void)
1041 rtp_play_channels_t *rpci = rtp_channels;
1043 /* if we only have one channel to play, we just use the info from that channel */
1044 if (rpci->rci[0] == NULL) {
1045 rpci->max_frame_index = rpci->rci[1]->max_frame_index;
1046 rpci->start_index[0] = rpci->max_frame_index;
1047 rpci->start_index[1] = 0;
1048 rpci->end_index[0] = rpci->max_frame_index;
1049 rpci->end_index[1] = rpci->max_frame_index;
1050 } else if (rpci->rci[1] == NULL) {
1051 rpci->max_frame_index = rpci->rci[0]->max_frame_index;
1052 rpci->start_index[1] = rpci->max_frame_index;
1053 rpci->start_index[0] = 0;
1054 rpci->end_index[0] = rpci->max_frame_index;
1055 rpci->end_index[1] = rpci->max_frame_index;
1057 /* if the two channels are to be played, then we need to sync both based on the start/end time of each one */
1059 rpci->max_frame_index = (guint32)(SAMPLE_RATE/1000) * (guint32)(max(rpci->rci[0]->end_time, rpci->rci[1]->end_time) -
1060 (guint32)min(rpci->rci[0]->start_time, rpci->rci[1]->start_time));
1062 if (rpci->rci[0]->start_time < rpci->rci[1]->start_time) {
1063 rpci->start_index[0] = 0;
1064 rpci->start_index[1] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->start_time - rpci->rci[0]->start_time);
1066 rpci->start_index[1] = 0;
1067 rpci->start_index[0] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->start_time - rpci->rci[1]->start_time);
1070 if (rpci->rci[0]->end_time < rpci->rci[1]->end_time) {
1071 rpci->end_index[0] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->end_time - rpci->rci[0]->end_time));
1072 rpci->end_index[1] = rpci->max_frame_index;
1074 rpci->end_index[1] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->end_time - rpci->rci[1]->end_time));
1075 rpci->end_index[0] = rpci->max_frame_index;
1081 /****************************************************************************/
1082 /* This routine will be called by the PortAudio engine when audio is needed.
1083 * It may called at interrupt level on some machines so don't do anything
1084 * that could mess up the system like calling malloc() or free().
1088 static int paCallback( void *inputBuffer, void *outputBuffer,
1089 unsigned long framesPerBuffer,
1090 PaTimestamp outTime, void *userData)
1092 #else /* PORTAUDIO_API_1 */
1093 static int paCallback( const void *inputBuffer, void *outputBuffer,
1094 unsigned long framesPerBuffer,
1095 const PaStreamCallbackTimeInfo* outTime,
1096 PaStreamCallbackFlags statusFlags _U_,
1099 #endif /* PORTAUDIO_API_1 */
1100 rtp_play_channels_t *rpci = (rtp_play_channels_t *)userData;
1101 SAMPLE *wptr = (SAMPLE*)outputBuffer;
1105 unsigned int framesLeft;
1108 /* if it is pasued, we keep the stream running but with silence only */
1109 if (rtp_channels->pause) {
1110 for(i=0; i<framesPerBuffer; i++ ) {
1114 rtp_channels->pause_duration += framesPerBuffer;
1119 rpci->out_diff_time = outTime - Pa_StreamTime(pa_stream) ;
1120 #else /* PORTAUDIO_API_1 */
1121 rpci->out_diff_time = (guint32)(SAMPLE_RATE) * (outTime->outputBufferDacTime - Pa_GetStreamTime(pa_stream)) ;
1122 #endif /* PORTAUDIO_API_1 */
1125 /* set the values if this is the first time */
1126 if (rpci->max_frame_index == 0) {
1127 init_rtp_channels_vals();
1131 framesLeft = rpci->max_frame_index - rpci->frame_index;
1133 (void) inputBuffer; /* Prevent unused variable warnings. */
1136 if( framesLeft < framesPerBuffer )
1138 framesToPlay = framesLeft;
1143 framesToPlay = framesPerBuffer;
1147 for( i=0; i<(unsigned int)framesToPlay; i++ )
1149 if (rpci->rci[0] && ( (rpci->frame_index >= rpci->start_index[0]) && (rpci->frame_index <= rpci->end_index[0]) )) {
1150 sample = g_array_index(rpci->rci[0]->samples, sample_t, rpci->rci[0]->frame_index++);
1151 *wptr++ = sample.val;
1156 if (rpci->rci[1] && ( (rpci->frame_index >= rpci->start_index[1]) && (rpci->frame_index <= rpci->end_index[1]) )) {
1157 sample = g_array_index(rpci->rci[1]->samples, sample_t, rpci->rci[1]->frame_index++);
1158 *wptr++ = sample.val;
1163 for( ; i<framesPerBuffer; i++ )
1168 rpci->frame_index += framesToPlay;
1173 /****************************************************************************/
1175 on_bt_check_clicked(GtkButton *button _U_, gpointer user_data _U_)
1177 rtp_channel_info_t *rci = user_data;
1179 if (rci->selected) {
1180 if (rtp_channels->rci[0] == rci) {
1181 rtp_channels->rci[0] = NULL;
1182 rtp_channels->channel = 0;
1184 rtp_channels->rci[1] = NULL;
1185 rtp_channels->channel = 1;
1188 /* if there are already both channels selected, unselect the old one */
1189 if (rtp_channels->rci[rtp_channels->channel]) {
1190 /* we disconnect the signal temporarly to avoid been called back */
1191 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1192 gtk_toggle_button_set_active((GtkToggleButton *)rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1193 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1194 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1197 rtp_channels->rci[rtp_channels->channel] = rci;
1198 rtp_channels->channel = !(rtp_channels->channel);
1201 rci->selected = !(rci->selected);
1203 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1204 bt_state(TRUE, TRUE, FALSE, FALSE);
1207 /****************************************************************************/
1208 static void channel_draw(rtp_channel_info_t* rci)
1214 PangoLayout *small_layout;
1215 guint32 label_width, label_height;
1216 char label_string[MAX_TIME_LABEL];
1218 guint32 progbar_nextstep;
1219 int progbar_quantum;
1224 GdkColor red_color = {0, 65535, 0, 0};
1226 if (GDK_IS_DRAWABLE(rci->pixmap)) {
1227 /* Clear out old plot */
1228 gdk_draw_rectangle(rci->pixmap,
1229 rci->bg_gc[1+rci->call_num%MAX_NUM_COL_CONV],
1232 rci->draw_area->allocation.width,
1233 rci->draw_area->allocation.height);
1235 small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
1236 pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
1238 /* calculated the pixel offset to display integer seconds */
1239 offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*SAMPLE_RATE/MULT;
1241 gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
1243 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
1244 rci->draw_area->allocation.width,
1245 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL);
1247 imax = min(rci->draw_area->allocation.width,(gint)(rci->samples->len/MULT));
1249 /* we update the progress bar 100 times */
1251 /* Update the progress bar when it gets to this value. */
1252 progbar_nextstep = 0;
1253 /* When we reach the value that triggers a progress bar update,
1254 bump that value by this amount. */
1255 progbar_quantum = imax/100;
1257 red_gc = gdk_gc_new(rci->draw_area->window);
1258 gdk_gc_set_rgb_fg_color(red_gc, &red_color);
1260 for (i=0; i< imax; i++) {
1266 if (progbar_count >= progbar_nextstep) {
1267 g_assert(total_frames > 0);
1269 progbar_val = (gfloat) i / imax;
1271 update_progress_bar(progbar_val);
1273 progbar_nextstep += progbar_quantum;
1276 for (j=0; j<MULT; j++) {
1277 sample = g_array_index(rci->samples, sample_t, i*MULT+j);
1278 max = max(max, sample.val);
1279 min = min(min, sample.val);
1280 if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
1283 if (status == S_DROP_BY_JITT) {
1286 gc = rci->draw_area->style->black_gc;
1289 gdk_draw_line(rci->pixmap, gc,
1291 (gint)(( (0x7FFF+min) * (rci->draw_area->allocation.height-HEIGHT_TIME_LABEL))/0xFFFF),
1293 (gint)(( (0x7FFF+max) * (rci->draw_area->allocation.height-HEIGHT_TIME_LABEL))/0xFFFF));
1295 /*draw the time label and grid */
1296 if ( !((i*MULT)%(SAMPLE_RATE)) ) {
1297 gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
1299 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
1301 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL+4);
1303 g_snprintf(label_string, MAX_TIME_LABEL, "%.0f", floor(rci->start_time/1000) + i*MULT/SAMPLE_RATE);
1305 pango_layout_set_text(small_layout, label_string, -1);
1306 pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
1307 gdk_draw_layout(rci->pixmap,
1308 rci->draw_area->style->black_gc,
1309 (int) (i - offset - label_width/2),
1310 rci->draw_area->allocation.height - label_height,
1312 /* draw the 1/2 sec grid */
1313 } else if ( !((i*MULT)%(SAMPLE_RATE/2)) ) {
1314 gdk_draw_line(rci->pixmap, rci->draw_area->style->black_gc,
1316 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL,
1318 rci->draw_area->allocation.height-HEIGHT_TIME_LABEL+2);
1327 /****************************************************************************/
1328 static gint expose_event_channels(GtkWidget *widget, GdkEventExpose *event)
1330 rtp_channel_info_t *rci;
1332 rci=(rtp_channel_info_t *)g_object_get_data(G_OBJECT(widget), "rtp_channel_info_t");
1337 if (GDK_IS_DRAWABLE(widget->window))
1338 gdk_draw_drawable(widget->window,
1339 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1341 event->area.x, event->area.y,
1342 event->area.x, event->area.y,
1343 event->area.width, event->area.height);
1348 /****************************************************************************/
1350 configure_event_channels(GtkWidget *widget, GdkEventConfigure *event _U_)
1352 rtp_channel_info_t *rci;
1355 /* the first calor is blue to highlight the selected item
1356 * the other collors are the same as in the Voip Graph analysys
1357 * to match the same calls
1359 static GdkColor col[MAX_NUM_COL_CONV+1] = {
1360 {0, 0x00FF, 0x00FF, 0xFFFF},
1361 {0, 0x33FF, 0xFFFF, 0x33FF},
1362 {0, 0x00FF, 0xCCFF, 0xCCFF},
1363 {0, 0x66FF, 0xFFFF, 0xFFFF},
1364 {0, 0x99FF, 0x66FF, 0xFFFF},
1365 {0, 0xFFFF, 0xFFFF, 0x33FF},
1366 {0, 0xCCFF, 0x99FF, 0xFFFF},
1367 {0, 0xCCFF, 0xFFFF, 0x33FF},
1368 {0, 0xFFFF, 0xCCFF, 0xCCFF},
1369 {0, 0xFFFF, 0x99FF, 0x66FF},
1370 {0, 0xFFFF, 0xFFFF, 0x99FF}
1373 rci=(rtp_channel_info_t *)g_object_get_data(G_OBJECT(widget), "rtp_channel_info_t");
1379 g_object_unref(rci->pixmap);
1383 rci->pixmap = gdk_pixmap_new(widget->window,
1384 widget->allocation.width,
1385 widget->allocation.height,
1388 if ( GDK_IS_DRAWABLE(rci->pixmap) )
1389 gdk_draw_rectangle(rci->pixmap,
1390 widget->style->white_gc,
1393 widget->allocation.width,
1394 widget->allocation.height);
1396 /* create gcs for the background color of each channel */
1397 for (i=0; i<MAX_NUM_COL_CONV+1; i++){
1398 rci->bg_gc[i]=gdk_gc_new(rci->pixmap);
1399 gdk_gc_set_rgb_fg_color(rci->bg_gc[i], &col[i]);
1407 /****************************************************************************/
1409 button_press_event_channel(GtkWidget *widget, GdkEventButton *event _U_)
1411 rtp_channel_info_t *rci;
1415 rci=(rtp_channel_info_t *)g_object_get_data(G_OBJECT(widget), "rtp_channel_info_t");
1420 if (!rci->selected) {
1422 /* only select a new channels if we are in STOP */
1423 if (!rtp_channels->stop) return 0;
1425 /* if there are already both channels selected, unselect the old one */
1426 if (rtp_channels->rci[rtp_channels->channel]) {
1427 /* we disconnect the signal temporarly to avoid been called back */
1428 g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
1429 gtk_toggle_button_set_active((GtkToggleButton *) rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
1430 g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
1431 rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
1434 /* we disconnect the signal temporarly to avoid been called back */
1435 g_signal_handlers_disconnect_by_func(rci->check_bt, on_bt_check_clicked, rci);
1436 gtk_toggle_button_set_active((GtkToggleButton *) rci->check_bt, TRUE);
1437 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1439 rtp_channels->rci[rtp_channels->channel] = rci;
1440 rtp_channels->channel = !(rtp_channels->channel);
1441 rci->selected = TRUE;
1443 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1444 bt_state(TRUE, TRUE, FALSE, FALSE);
1447 if (rci == rtp_channels->rci[0]) {
1453 rci->frame_index = (unsigned int) (event->x * MULT);
1455 prev_index = rtp_channels->frame_index;
1456 rtp_channels->frame_index = rtp_channels->start_index[this_channel] + rci->frame_index;
1457 rtp_channels->pause_duration += prev_index - rtp_channels->frame_index;
1461 /* change the index in the other channel if selected, according with the index position */
1462 if (rtp_channels->rci[!this_channel]) {
1463 init_rtp_channels_vals();
1465 if (rtp_channels->frame_index < rtp_channels->start_index[!this_channel]) {
1466 rtp_channels->rci[!this_channel]->frame_index = 0;
1467 } else if (rtp_channels->frame_index > rtp_channels->end_index[!this_channel]) {
1468 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->rci[!this_channel]->max_frame_index;
1470 rtp_channels->rci[!this_channel]->frame_index = rtp_channels->frame_index - rtp_channels->start_index[!this_channel];
1473 init_rtp_channels_vals();
1476 rtp_channels->out_diff_time = 0;
1478 rci->cursor_catch = TRUE;
1480 /* redraw the cusrsor */
1486 /****************************************************************************/
1488 add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, guint *counter _U_ )
1490 GString *label = NULL;
1491 GtkWidget *viewport;
1494 /* create the channel draw area */
1495 rci->draw_area=gtk_drawing_area_new();
1497 rci->scroll_window=gtk_scrolled_window_new(NULL, NULL);
1499 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (rci->scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
1500 rci->h_scrollbar_adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window));
1503 gtk_widget_set_size_request(rci->draw_area, (gint)(rci->samples->len/MULT), CHANNEL_HEIGHT);
1506 viewport = gtk_viewport_new(rci->h_scrollbar_adjustment, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window)));
1507 gtk_container_add(GTK_CONTAINER(viewport), rci->draw_area);
1508 gtk_container_add(GTK_CONTAINER(rci->scroll_window), viewport);
1509 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1510 g_object_set_data(G_OBJECT(rci->draw_area), "rtp_channel_info_t", rci);
1511 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1512 GTK_WIDGET_SET_FLAGS(rci->draw_area, GTK_CAN_FOCUS);
1513 gtk_widget_grab_focus(rci->draw_area);
1515 gtk_box_pack_start(GTK_BOX (channels_vb), rci->scroll_window, FALSE, FALSE, 0);
1517 /* signals needed to handle backing pixmap */
1518 g_signal_connect(rci->draw_area, "expose_event", G_CALLBACK(expose_event_channels), NULL);
1519 g_signal_connect(rci->draw_area, "configure_event", G_CALLBACK(configure_event_channels), rci);
1520 gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
1521 g_signal_connect(rci->draw_area, "button_press_event", G_CALLBACK(button_press_event_channel), rci);
1522 g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
1525 label = g_string_new("");
1526 if (GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp)->active) {
1527 g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Out of Seq: %d(%.1f%%)", get_addr_name(&(rci->first_stream->src_addr)),
1528 rci->first_stream->src_port, get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1529 (double)rci->samples->len/SAMPLE_RATE, rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets);
1531 g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Drop by Jitter Buff:%d(%.1f%%) Out of Seq: %d(%.1f%%)", get_addr_name(&(rci->first_stream->src_addr)),
1532 rci->first_stream->src_port, get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
1533 (double)rci->samples->len/SAMPLE_RATE, rci->drop_by_jitter_buff, (double)rci->drop_by_jitter_buff * 100 / (double)rci->num_packets
1534 , rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets);
1537 rci->check_bt = gtk_check_button_new_with_label(label->str);
1538 gtk_box_pack_start(GTK_BOX (channels_vb), rci->check_bt, FALSE, FALSE, 1);
1540 /* Create the Separator if it is not the last one */
1542 if (*counter < g_hash_table_size(rtp_channels_hash)) {
1543 rci->separator = gtk_hseparator_new();
1544 gtk_box_pack_start(GTK_BOX (channels_vb), rci->separator, FALSE, FALSE, 5);
1547 g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
1549 g_string_free(label, TRUE);
1552 /****************************************************************************/
1554 count_channel_frames(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
1556 total_frames += rci->samples->len;
1559 /****************************************************************************/
1566 /* we should never be here if we are in PLAY and !PAUSE */
1567 if(!rtp_channels->stop && !rtp_channels->pause){
1571 /* if we are in PAUSE change the sate */
1572 if (rtp_channels->pause) {
1573 rtp_channels->pause = FALSE;
1574 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1575 bt_state(FALSE, FALSE, TRUE, TRUE);
1577 /* if not PAUSE, then start to PLAY */
1580 err = Pa_OpenStream(
1582 paNoDevice, /* default input device */
1584 PA_SAMPLE_TYPE, /* 16 bit Integer input */
1586 Pa_GetDefaultOutputDeviceID(),
1587 NUM_CHANNELS, /* Stereo output */
1588 PA_SAMPLE_TYPE, /* 16 bit Integer output */
1592 0, /* number of buffers, if zero then use default minimum */
1593 paClipOff, /* we won't output out of range samples so don't bother clipping them */
1596 #else /* PORTAUDIO_API_1 */
1597 err = Pa_OpenDefaultStream(
1606 #endif /* PORTAUDIO_API_1 */
1608 if( err != paNoError ) {
1610 const char *deviceName = "No Device";
1612 PaDeviceID device = Pa_GetDefaultOutputDeviceID();
1614 if (device != paNoDevice)
1616 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
1618 deviceName = deviceInfo->name;
1620 deviceName = "(No device info)";
1623 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1624 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1625 "Got this info from PortAudio Library:\n"
1626 " Default deviceName: %s (%d)", deviceName, device);
1627 gtk_dialog_run (GTK_DIALOG (dialog));
1628 gtk_widget_destroy (dialog);
1630 PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
1633 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1634 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1635 "Can not even get the default host API from PortAudio Library.\n Error: %s",
1636 Pa_GetErrorText( hostApi ));
1637 gtk_dialog_run (GTK_DIALOG (dialog));
1638 gtk_widget_destroy (dialog);
1642 const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( hostApi );
1646 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1647 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1648 "Can not even get the host API info from PortAudio Library.");
1649 gtk_dialog_run (GTK_DIALOG (dialog));
1650 gtk_widget_destroy (dialog);
1654 const char *hostApiName = hostApiInfo->name;
1655 const char *deviceName = "No Device";
1657 PaDeviceIndex device = hostApiInfo->defaultOutputDevice;
1659 if (device != paNoDevice)
1661 const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
1663 deviceName = deviceInfo->name;
1665 deviceName = "(No device info)";
1668 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1669 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1670 "Got this info from PortAudio Library:\n"
1671 " Default hostApiName: %s\n"
1672 " Default deviceName: %s (%d)", hostApiName, deviceName, device);
1673 gtk_dialog_run (GTK_DIALOG (dialog));
1674 gtk_widget_destroy (dialog);
1678 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1679 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1680 "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1681 gtk_dialog_run (GTK_DIALOG (dialog));
1682 gtk_widget_destroy (dialog);
1686 err = Pa_StartStream( pa_stream );
1687 if( err != paNoError ) {
1688 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1689 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1690 "Can not Start Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1691 gtk_dialog_run (GTK_DIALOG (dialog));
1692 gtk_widget_destroy (dialog);
1695 #if !PORTAUDIO_API_1
1696 rtp_channels->pa_start_time = Pa_GetStreamTime(pa_stream);
1697 #endif /* PORTAUDIO_API_1 */
1699 rtp_channels->stop = FALSE;
1701 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1702 bt_state(FALSE, FALSE, TRUE, TRUE);
1705 /* Draw the cursor in the graph */
1706 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
1710 /****************************************************************************/
1712 pause_channels(void)
1714 rtp_channels->pause = !(rtp_channels->pause);
1716 /* reactivate the cusrosr display if no in pause */
1717 if (!rtp_channels->pause) {
1718 /* Draw the cursor in the graph */
1719 g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
1722 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1723 bt_state(FALSE, TRUE, FALSE, TRUE);
1726 /****************************************************************************/
1728 reset_rtp_channels(void)
1730 rtp_channels->channel = 0;
1731 rtp_channels->rci[0] = NULL;
1732 rtp_channels->rci[1] = NULL;
1733 rtp_channels->start_index[0] = 0;
1734 rtp_channels->start_index[1] = 0;
1735 rtp_channels->end_index[0] = 0;
1736 rtp_channels->end_index[1] = 0;
1737 rtp_channels->max_frame_index = 0;
1738 rtp_channels->frame_index = 0;
1739 rtp_channels->pause = FALSE;
1740 rtp_channels->pause_duration = 0;
1741 rtp_channels->stop = TRUE;
1742 rtp_channels->out_diff_time = 10000;
1745 /****************************************************************************/
1747 remove_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
1749 g_object_unref(rci->pixmap);
1750 gtk_widget_destroy(rci->draw_area);
1751 gtk_widget_destroy(rci->scroll_window);
1752 gtk_widget_destroy(rci->check_bt);
1754 gtk_widget_destroy(rci->separator);
1757 /****************************************************************************/
1759 reset_channels(void)
1762 if (rtp_channels_hash) {
1763 /* Remove the channels from the main window if there are there */
1764 g_hash_table_foreach( rtp_channels_hash, (GHFunc)remove_channel_to_window, NULL);
1767 /* destroy the rtp channels hash table */
1768 g_hash_table_destroy(rtp_channels_hash);
1769 rtp_channels_hash = NULL;
1773 reset_rtp_channels();
1777 /****************************************************************************/
1779 reset_rtp_player(void)
1781 /* Destroy the rtp channels */
1784 /* destroy the rtp streams hash table */
1785 if (rtp_streams_hash) {
1786 g_hash_table_destroy(rtp_streams_hash);
1787 rtp_streams_hash = NULL;
1790 /* destroy the rtp streams list */
1791 if (rtp_streams_list) {
1792 g_list_free (rtp_streams_list);
1793 rtp_streams_list = NULL;
1798 /****************************************************************************/
1800 decode_streams(void)
1802 guint statusbar_context;
1805 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1806 bt_state(FALSE, FALSE, FALSE, FALSE);
1810 progress_bar = gtk_progress_bar_new();
1811 gtk_widget_set_size_request(progress_bar, 100, -1);
1812 gtk_box_pack_start(GTK_BOX (stat_hbox), progress_bar, FALSE, FALSE, 2);
1813 gtk_widget_show(progress_bar);
1814 statusbar_context = gtk_statusbar_get_context_id((GtkStatusbar *) info_bar, "main");
1815 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Decoding RTP packets...");
1817 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
1819 /* reset the number of packet to be decoded, this is used for the progress bar */
1821 /* reset the Progress Bar count */
1824 /* Mark the RTP streams to be played using the selected VoipCalls*/
1825 if (rtp_streams_hash)
1826 g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_rtp_stream_to_play, NULL);
1828 /* Decode the RTP streams and add them to the RTP channels to be played */
1829 g_list_foreach( rtp_streams_list, (GFunc)decode_rtp_stream, NULL);
1831 /* reset the number of frames to be displayed, this is used for the progress bar */
1833 /* Count the frames in all the RTP channels */
1834 if (rtp_channels_hash)
1835 g_hash_table_foreach( rtp_channels_hash, (GHFunc)count_channel_frames, NULL);
1837 /* reset the Progress Bar count again for the progress of creating the channels view */
1839 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
1840 gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Creating channels view...");
1842 /* Display the RTP channels in the window */
1844 if (rtp_channels_hash)
1845 g_hash_table_foreach( rtp_channels_hash, (GHFunc)add_channel_to_window, &counter);
1847 /* Resize the main scroll window to display no more than preferred (or default) max channels, scroll bar will be used if needed */
1849 if (prefs.rtp_player_max_visible < 1 || prefs.rtp_player_max_visible > 10)
1850 prefs.rtp_player_max_visible = RTP_PLAYER_DEFAULT_VISIBLE;
1852 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH,
1853 min(counter, prefs.rtp_player_max_visible) * (CHANNEL_HEIGHT+60));
1855 gtk_widget_show_all(main_scrolled_window);
1857 gtk_widget_destroy(progress_bar);
1858 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
1859 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
1861 /* blank the status label */
1862 gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
1864 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1865 bt_state(TRUE, FALSE, FALSE, FALSE);
1867 /* get the static jitter buffer from the spinner gui */
1868 new_jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
1872 /****************************************************************************/
1874 on_cb_use_rtp_clicked(GtkToggleButton *button _U_, gpointer user_data _U_)
1876 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1877 bt_state(TRUE, FALSE, FALSE, FALSE);
1880 /****************************************************************************/
1882 on_bt_decode_clicked(GtkButton *button _U_, gpointer user_data _U_)
1887 /****************************************************************************/
1889 on_bt_play_clicked(GtkButton *button _U_, gpointer user_data _U_)
1894 /****************************************************************************/
1896 on_bt_pause_clicked(GtkButton *button _U_, gpointer user_data _U_)
1901 /****************************************************************************/
1903 on_bt_stop_clicked(GtkButton *button _U_, gpointer user_data _U_)
1908 /****************************************************************************/
1910 rtp_player_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
1915 /* Stop the channels if necesary */
1916 if(rtp_channels && (!rtp_channels->stop)){
1920 /* Destroy the rtp channels */
1923 g_free(rtp_channels);
1924 rtp_channels = NULL;
1926 /* Terminate the use of PortAudio library */
1927 err = Pa_Terminate();
1928 initialized = FALSE;
1929 if( err != paNoError ) {
1930 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
1931 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
1932 "Can not terminate the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
1933 gtk_dialog_run (GTK_DIALOG (dialog));
1934 gtk_widget_destroy (dialog);
1937 gtk_widget_destroy(rtp_player_dlg_w);
1938 main_scrolled_window = NULL;
1939 rtp_player_dlg_w = NULL;
1942 /****************************************************************************/
1944 jitter_spinner_value_changed (GtkSpinButton *spinner _U_, gpointer user_data _U_)
1946 /* set the sensitive state of the buttons (decode, play, pause, stop) */
1947 bt_state(TRUE, TRUE, FALSE, FALSE);
1950 /****************************************************************************/
1952 rtp_player_dlg_create(void)
1955 GtkWidget *hbuttonbox;
1956 GtkWidget *h_jitter_buttons_box;
1957 GtkWidget *bt_close;
1958 GtkAdjustment *jitter_spinner_adj;
1960 const gchar *title_name_ptr;
1963 GtkTooltips *tooltips = gtk_tooltips_new();
1965 rtp_player_dlg_w=gtk_window_new(GTK_WINDOW_TOPLEVEL);
1967 title_name_ptr = cf_get_display_name(&cfile);
1968 win_name = g_strdup_printf("%s - VoIP - RTP Player", title_name_ptr);
1969 gtk_window_set_title(GTK_WINDOW(rtp_player_dlg_w), win_name);
1970 gtk_window_set_position(GTK_WINDOW(rtp_player_dlg_w), GTK_WIN_POS_NONE);
1972 gtk_window_set_default_size(GTK_WINDOW(rtp_player_dlg_w), 400, 50);
1974 main_vb = gtk_vbox_new (FALSE, 0);
1975 gtk_container_add(GTK_CONTAINER(rtp_player_dlg_w), main_vb);
1976 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 2);
1978 main_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
1979 gtk_container_set_border_width (GTK_CONTAINER (main_scrolled_window), 4);
1980 gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH, 0);
1982 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1983 gtk_container_add(GTK_CONTAINER(main_vb), main_scrolled_window);
1985 channels_vb = gtk_vbox_new (FALSE, 0);
1986 gtk_container_set_border_width (GTK_CONTAINER (channels_vb), 2);
1987 gtk_scrolled_window_add_with_viewport((GtkScrolledWindow *) main_scrolled_window, channels_vb);
1989 h_jitter_buttons_box = gtk_hbox_new (FALSE, 0);
1990 gtk_container_set_border_width (GTK_CONTAINER (h_jitter_buttons_box), 10);
1991 gtk_box_pack_start (GTK_BOX(main_vb), h_jitter_buttons_box, FALSE, FALSE, 0);
1992 label = gtk_label_new("Jitter buffer [ms] ");
1993 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), label, FALSE, FALSE, 0);
1995 jitter_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (50, 0, 500, 5, 10, 10);
1996 jitter_spinner = gtk_spin_button_new (jitter_spinner_adj, 5, 0);
1997 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), jitter_spinner, FALSE, FALSE, 0);
1998 gtk_tooltips_set_tip (tooltips, jitter_spinner, "The simulated jitter buffer in [ms]", NULL);
1999 g_signal_connect(GTK_OBJECT (jitter_spinner_adj), "value_changed", G_CALLBACK(jitter_spinner_value_changed), NULL);
2002 cb_use_rtp_timestamp = gtk_check_button_new_with_label("Use RTP timestamp");
2003 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp), FALSE);
2004 gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), cb_use_rtp_timestamp, FALSE, FALSE, 10);
2005 g_signal_connect(cb_use_rtp_timestamp, "toggled", G_CALLBACK(on_cb_use_rtp_clicked), NULL);
2006 gtk_tooltips_set_tip (tooltips, 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", NULL);
2009 hbuttonbox = gtk_hbutton_box_new ();
2010 gtk_box_pack_start (GTK_BOX (h_jitter_buttons_box), hbuttonbox, TRUE, TRUE, 0);
2011 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
2012 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 10);
2014 bt_decode = gtk_button_new_with_label("Decode");
2015 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_decode);
2016 g_signal_connect(bt_decode, "clicked", G_CALLBACK(on_bt_decode_clicked), NULL);
2017 gtk_tooltips_set_tip (tooltips, bt_decode, "Decode the RTP stream(s)", NULL);
2019 bt_play = gtk_button_new_with_label("Play");
2020 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_play);
2021 g_signal_connect(bt_play, "clicked", G_CALLBACK(on_bt_play_clicked), NULL);
2022 gtk_tooltips_set_tip (tooltips, bt_play, "Play the RTP channel(s)", NULL);
2024 bt_pause = gtk_button_new_with_label("Pause");
2025 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_pause);
2026 g_signal_connect(bt_pause, "clicked", G_CALLBACK(on_bt_pause_clicked), NULL);
2027 gtk_tooltips_set_tip (tooltips, bt_pause, "Pause the RTP channel(s)", NULL);
2029 bt_stop = gtk_button_new_with_label("Stop");
2030 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_stop);
2031 g_signal_connect(bt_stop, "clicked", G_CALLBACK(on_bt_stop_clicked), NULL);
2032 gtk_tooltips_set_tip (tooltips, bt_stop, "Stop the RTP channel(s)", NULL);
2034 bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2035 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
2036 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
2037 gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
2039 g_signal_connect(bt_close, "clicked", G_CALLBACK(rtp_player_on_destroy), NULL);
2040 g_signal_connect(rtp_player_dlg_w, "destroy", G_CALLBACK(rtp_player_on_destroy), NULL);
2043 hbuttonbox = gtk_hbutton_box_new ();
2045 /* Filter/status hbox */
2046 stat_hbox = gtk_hbox_new(FALSE, 1);
2047 gtk_container_set_border_width(GTK_CONTAINER(stat_hbox), 0);
2050 info_bar = gtk_statusbar_new();
2051 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
2053 gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
2055 /* statusbar hbox */
2056 gtk_box_pack_start(GTK_BOX(main_vb), stat_hbox, FALSE, TRUE, 0);
2058 /* set the sensitive state of the buttons (decode, play, pause, stop) */
2059 bt_state(TRUE, FALSE, FALSE, FALSE);
2061 gtk_widget_show_all(rtp_player_dlg_w);
2063 /* Force gtk to redraw the window before starting decoding the packet */
2064 while (g_main_context_iteration(NULL, FALSE));
2069 /****************************************************************************/
2071 rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap)
2076 if (initialized) return;
2079 voip_calls = voip_calls_tap;
2080 err = Pa_Initialize();
2081 if( err != paNoError ) {
2082 dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
2083 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
2084 "Can not Initialize the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
2085 gtk_dialog_run (GTK_DIALOG (dialog));
2086 gtk_widget_destroy (dialog);
2087 initialized = FALSE;
2091 new_jitter_buff = -1;
2093 #ifdef HAVE_G729_G723
2094 /* Initialize the G729 and G723 decoders */
2097 #endif /* HAVE_G729_G723 */
2099 if (!rtp_channels) {
2100 rtp_channels = g_malloc(sizeof(rtp_play_channels_t));
2103 reset_rtp_channels();
2105 /* create the dialog window */
2106 rtp_player_dlg_create();
2110 #endif /* HAVE_LIBPORTAUDIO */