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