Qt: Initial RTP playback.

Note the "initial". This is woefully incomplete.  See the "to do" lists
below and in the code.

This differs a bit from the GTK+ version in that you specify one or more
streams to be decoded.

Instead of showing waveforms in individual widgets, add them all to a
single QCustomPlot. This conserves screen real estate and lets us more
easily take advantage of the QCP API. It also looks better IMHO.

Change a bunch of checks for QtMultimediaWidgets to QtMultimedia. We
probably won't use the widgets until we make 5.0 our minimum Qt
version and plain old QtMultimedia lets us support Qt 4 more easily
(in theory at least).

Add resampling code from libspeex. I initially used this to resample
each packet to match the preferred rate of our output device, but this
resulted in poorer audio quality than expected. Leave it in and use to
create visual samples for QCP and to match rates any time the rate
changes. The latter is currently untested.

Add some debugging macros.

Note that both the RTP player and RTP analysis dialogs decode audio data
using different code.

Note that voip_calls_packet and voip_calls_init_tap appear to be dead
code.

To do:

- Add silence frames where needed.
- Implement the jitter buffer.
- Implement the playback timing controls.
- Tapping / scanning streams might be too slow.

Change-Id: I20dd3b66d3df53c9b1f3501262dc01458849f6b4
Bug: 9007
Reviewed-on: https://code.wireshark.org/review/10458
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2014-12-12 16:51:40 -08:00
parent fd5eafa50a
commit 3687d39304
41 changed files with 3925 additions and 74 deletions

View File

@ -661,7 +661,7 @@ if(BUILD_wireshark)
set(PACKAGELIST ${PACKAGELIST}
Qt5Core
Qt5LinguistTools
Qt5MultimediaWidgets
Qt5Multimedia
Qt5PrintSupport
Qt5Widgets
)
@ -699,7 +699,7 @@ if(ENABLE_KERBEROS)
set(PACKAGELIST ${PACKAGELIST} KERBEROS)
endif()
# Portable audio
# Portable audio (GTK+ only)
if(ENABLE_PORTAUDIO AND BUILD_wireshark_gtk)
set(PACKAGELIST ${PACKAGELIST} PORTAUDIO)
endif()
@ -879,10 +879,10 @@ if (Qt5Widgets_FOUND)
endif()
set (QT_FOUND ON)
set (QT_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5PrintSupport_LIBRARIES})
if(Qt5MultimediaWidgets_FOUND)
set (QT_LIBRARIES ${QT_LIBRARIES} ${Qt5MultimediaWidgets_LIBRARIES})
if(Qt5Multimedia_FOUND)
set (QT_LIBRARIES ${QT_LIBRARIES} ${Qt5Multimedia_LIBRARIES})
# That's the name autofoo uses
set(QT_MULTIMEDIAWIDGETS_LIB 1)
set(QT_MULTIMEDIA_LIB 1)
endif()
if(Qt5MacExtras_FOUND)
set (QT_LIBRARIES ${QT_LIBRARIES} ${Qt5MacExtras_LIBRARIES})

View File

@ -325,7 +325,7 @@ $(PROGRAM_NAME_GTK).exe : $(LIBS_CHECK) config.h $(wireshark_gtk_OBJECTS) capchi
mt.exe -nologo -manifest "wireshark.exe.manifest" -outputresource:$(PROGRAM_NAME).exe;1
!ENDIF
$(PROGRAM_NAME).exe : install-generated-files $(LIBS_CHECK) config.h capchild caputils epan ui qt wsutil\libwsutil.lib wiretap\wiretap-$(WTAP_VERSION).lib plugins
$(PROGRAM_NAME).exe : install-generated-files $(LIBS_CHECK) config.h capchild caputils codecs epan ui qt wsutil\libwsutil.lib wiretap\wiretap-$(WTAP_VERSION).lib plugins
tshark.exe : $(LIBS_CHECK) config.h $(tshark_OBJECTS) capchild caputils epan ui cli image\tshark.res wsutil\libwsutil.lib wiretap\wiretap-$(WTAP_VERSION).lib plugins
@echo Linking $@

View File

@ -2133,11 +2133,13 @@ AC_DEFUN([AC_WIRESHARK_QT_CHECK],
AC_WIRESHARK_QT_MODULE_CHECK(PrintSupport, $1, $qt_version_to_check)
#
# Qt 5.0 added multimedia widgets in the Qt
# MultimediaWidgets module.
# Qt 5.0 added multimedia in the Qt
# Multimedia module.
#
AC_WIRESHARK_QT_MODULE_CHECK(MultimediaWidgets, $1, $qt_version_to_check,
AC_DEFINE(QT_MULTIMEDIAWIDGETS_LIB, 1, [Define if we have QtMultimediaWidgets]))
have_qt_multimedia_lib=no
AC_WIRESHARK_QT_MODULE_CHECK(Multimedia, $1, $qt_version_to_check,
have_qt_multimedia_lib=yes
AC_DEFINE(QT_MULTIMEDIA_LIB, 1, [Define if we have QtMultimedia]))
#
# While we're at it, look for QtMacExtras. (Presumably

View File

@ -381,8 +381,8 @@
/* Define if we are using version of of the Portaudio library API */
#cmakedefine PORTAUDIO_API_1 1
/* Define if we have QtMultimediaWidgets */
#cmakedefine QT_MULTIMEDIAWIDGETS_LIB 1
/* Define if we have QtMultimedia */
#cmakedefine QT_MULTIMEDIA_LIB 1
/* Define if we have QtMacExtras */
#cmakedefine QT_MACEXTRAS_LIB 1

View File

@ -27,6 +27,17 @@ set(CODECS_FILES
# handle or define.
# G722/G722decode.c
# G726/G726decode.c
speex/resample.c
)
# Enables visibility in IDEs
file(GLOB EXTRA_CODEC_HEADERS
codecs.h
G711a/G711adecode.h G711a/G711atable.h
G711u/G711udecode.h G711u/G711utable.h
speex/arch.h
speex/speex_resampler.h
speex/stack_alloc.h
)
if(SBC_FOUND)

View File

@ -27,7 +27,8 @@ LIBCODEC_SRC = \
G711u/G711udecode.c \
G722/G722decode.c \
G726/G726decode.c \
sbc/sbc.c
sbc/sbc.c \
speex/resample.c
noinst_HEADERS = \
codecs.h \
@ -35,5 +36,8 @@ noinst_HEADERS = \
G711u/G711udecode.h G711u/G711utable.h \
G722/G722decode.h \
G726/G726decode.h \
sbc/sbc_private.h
sbc/sbc_private.h \
speex/arch.h \
speex/speex_resampler.h \
speex/stack_alloc.h

View File

@ -10,6 +10,9 @@ include ..\Makefile.nmake.inc
CFLAGS=/I.. $(WARNINGS_ARE_ERRORS) $(STANDARD_CFLAGS) \
$(GLIB_CFLAGS)
DIRTY_CFLAGS=/I.. $(STANDARD_CFLAGS) \
$(GLIB_CFLAGS)
.c.obj::
$(CC) $(CFLAGS) -Fd.\ -c $<
@ -26,6 +29,7 @@ LIBCODEC_OBJECTS= \
G711adecode.obj \
G722decode.obj \
G726decode.obj \
resample.obj \
sbc.obj
codecs.lib : $(LIBCODEC_OBJECTS)
@ -46,6 +50,9 @@ G722decode.obj: G722\G722decode.c G722\G722decode.h
G726decode.obj: G726\G726decode.c G726\G726decode.h
$(CC) $(CFLAGS) -Fd.\ -c G726\G726decode.c /Fo%|fF.obj
resample.obj: speex\resample.c speex\arch.h speex\speex_resampler.h speex\stack_alloc.h
$(CC) $(DIRTY_CFLAGS) -Fd.\ -c speex\resample.c /Fo%|fF.obj
sbc.obj: sbc\sbc.c sbc\sbc_private.h
$(CC) $(CFLAGS) -Fd.\ -c sbc\sbc.c /Fo%|fF.obj

2
codecs/speex/README.txt Normal file
View File

@ -0,0 +1,2 @@
Copied from http://git.xiph.org/speexdsp.git c470e2e89a6ca75b507437467692cd684b71a526
and modified to compile in the Wireshark source tree.

235
codecs/speex/arch.h Normal file
View File

@ -0,0 +1,235 @@
/* Copyright (C) 2003 Jean-Marc Valin */
/**
@file arch.h
@brief Various architecture definitions Speex
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCH_H
#define ARCH_H
/* A couple test to catch stupid option combinations */
#ifdef FIXED_POINT
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
#error Vorbis-psy model currently not implemented in fixed-point
#endif
#else
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
#endif
#ifndef OUTSIDE_SPEEX
#include "speex/speexdsp_types.h"
#endif
#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#ifdef FIXED_POINT
typedef spx_int16_t spx_word16_t;
typedef spx_int32_t spx_word32_t;
typedef spx_word32_t spx_mem_t;
typedef spx_word16_t spx_coef_t;
typedef spx_word16_t spx_lsp_t;
typedef spx_word32_t spx_sig_t;
#define Q15ONE 32767
#define LPC_SCALING 8192
#define SIG_SCALING 16384
#define LSP_SCALING 8192.
#define GAMMA_SCALING 32768.
#define GAIN_SCALING 64
#define GAIN_SCALING_1 0.015625
#define LPC_SHIFT 13
#define LSP_SHIFT 13
#define SIG_SHIFT 14
#define GAIN_SHIFT 6
#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
#define VERY_SMALL 0
#define VERY_LARGE32 ((spx_word32_t)2147483647)
#define VERY_LARGE16 ((spx_word16_t)32767)
#define Q15_ONE ((spx_word16_t)32767)
#ifdef FIXED_DEBUG
#include "fixed_debug.h"
#else
#include "fixed_generic.h"
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#include "fixed_bfin.h"
#endif
#endif
#else
typedef float spx_mem_t;
typedef float spx_coef_t;
typedef float spx_lsp_t;
typedef float spx_sig_t;
typedef float spx_word16_t;
typedef float spx_word32_t;
#define Q15ONE 1.0f
#define LPC_SCALING 1.f
#define SIG_SCALING 1.f
#define LSP_SCALING 1.f
#define GAMMA_SCALING 1.f
#define GAIN_SCALING 1.f
#define GAIN_SCALING_1 1.f
#define VERY_SMALL 1e-15f
#define VERY_LARGE32 1e15f
#define VERY_LARGE16 1e15f
#define Q15_ONE ((spx_word16_t)1.f)
#define QCONST16(x,bits) (x)
#define QCONST32(x,bits) (x)
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define EXTRACT16(x) (x)
#define EXTEND32(x) (x)
#define SHR16(a,shift) (a)
#define SHL16(a,shift) (a)
#define SHR32(a,shift) (a)
#define SHL32(a,shift) (a)
#define PSHR16(a,shift) (a)
#define PSHR32(a,shift) (a)
#define VSHR32(a,shift) (a)
#define SATURATE16(x,a) (x)
#define SATURATE32(x,a) (x)
#define SATURATE32PSHR(x,shift,a) (x)
#define PSHR(a,shift) (a)
#define SHR(a,shift) (a)
#define SHL(a,shift) (a)
#define SATURATE(x,a) (x)
#define ADD16(a,b) ((a)+(b))
#define SUB16(a,b) ((a)-(b))
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
#define MULT16_16_Q11_32(a,b) ((a)*(b))
#define MULT16_16_Q13(a,b) ((a)*(b))
#define MULT16_16_Q14(a,b) ((a)*(b))
#define MULT16_16_Q15(a,b) ((a)*(b))
#define MULT16_16_P15(a,b) ((a)*(b))
#define MULT16_16_P13(a,b) ((a)*(b))
#define MULT16_16_P14(a,b) ((a)*(b))
#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2
#define BITS_PER_CHAR 16
#define LOG2_BITS_PER_CHAR 4
#else
#define BYTES_PER_CHAR 1
#define BITS_PER_CHAR 8
#define LOG2_BITS_PER_CHAR 3
#endif
#ifdef FIXED_DEBUG
extern long long spx_mips;
#endif
#endif

1203
codecs/speex/resample.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,344 @@
/* Copyright (C) 2007 Jean-Marc Valin
File: speex_resampler.h
Resampling code
The design goals of this code are:
- Very fast algorithm
- Low memory requirement
- Good *perceptual* quality (and not best SNR)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPEEX_RESAMPLER_H
#define SPEEX_RESAMPLER_H
#define OUTSIDE_SPEEX 1
#define RANDOM_PREFIX ws_codec
#include "ws_symbol_export.h"
#define EXPORT
#ifdef OUTSIDE_SPEEX
/********* WARNING: MENTAL SANITY ENDS HERE *************/
/* If the resampler is defined outside of Speex, we change the symbol names so that
there won't be any clash if linking with Speex later on. */
/* #define RANDOM_PREFIX your software name here */
#ifndef RANDOM_PREFIX
#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
#endif
#define CAT_PREFIX2(a,b) a ## b
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
#define spx_int16_t short
#define spx_int32_t int
#define spx_uint16_t unsigned short
#define spx_uint32_t unsigned int
#else /* OUTSIDE_SPEEX */
#include "speexdsp_types.h"
#endif /* OUTSIDE_SPEEX */
#ifdef __cplusplus
extern "C" {
#endif
#define SPEEX_RESAMPLER_QUALITY_MAX 10
#define SPEEX_RESAMPLER_QUALITY_MIN 0
#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
enum {
RESAMPLER_ERR_SUCCESS = 0,
RESAMPLER_ERR_ALLOC_FAILED = 1,
RESAMPLER_ERR_BAD_STATE = 2,
RESAMPLER_ERR_INVALID_ARG = 3,
RESAMPLER_ERR_PTR_OVERLAP = 4,
RESAMPLER_ERR_MAX_ERROR
};
struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState;
/** Create a new resampler with integer input and output rates.
* @param nb_channels Number of channels to be processed
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Create a new resampler with fractional input/output rates. The sampling
* rate ratio is an arbitrary rational number with both the numerator and
* denominator being 32-bit integers.
* @param nb_channels Number of channels to be processed
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Destroy a resampler state.
* @param st Resampler state
*/
void speex_resampler_destroy(SpeexResamplerState *st);
/** Resample a float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the
* number of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_float(SpeexResamplerState *st,
spx_uint32_t channel_index,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_int(SpeexResamplerState *st,
spx_uint32_t channel_index,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Set (change) the input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
*/
int speex_resampler_set_rate(SpeexResamplerState *st,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz) copied.
* @param out_rate Output sampling rate (integer number of Hz) copied.
*/
void speex_resampler_get_rate(SpeexResamplerState *st,
spx_uint32_t *in_rate,
spx_uint32_t *out_rate);
/** Set (change) the input/output sampling rates and resampling ratio
* (fractional values in Hz supported).
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
*/
int speex_resampler_set_rate_frac(SpeexResamplerState *st,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current resampling ratio. This will be reduced to the least
* common denominator.
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio copied
* @param ratio_den Denominator of the sampling rate ratio copied
*/
void speex_resampler_get_ratio(SpeexResamplerState *st,
spx_uint32_t *ratio_num,
spx_uint32_t *ratio_den);
/** Set (change) the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
int speex_resampler_set_quality(SpeexResamplerState *st,
int quality);
/** Get the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
void speex_resampler_get_quality(SpeexResamplerState *st,
int *quality);
/** Set (change) the input stride.
* @param st Resampler state
* @param stride Input stride
*/
void speex_resampler_set_input_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the input stride.
* @param st Resampler state
* @param stride Input stride copied
*/
void speex_resampler_get_input_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Set (change) the output stride.
* @param st Resampler state
* @param stride Output stride
*/
void speex_resampler_set_output_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the output stride.
* @param st Resampler state copied
* @param stride Output stride
*/
void speex_resampler_get_output_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Get the latency introduced by the resampler measured in input samples.
* @param st Resampler state
*/
int speex_resampler_get_input_latency(SpeexResamplerState *st);
/** Get the latency introduced by the resampler measured in output samples.
* @param st Resampler state
*/
int speex_resampler_get_output_latency(SpeexResamplerState *st);
/** Make sure that the first samples to go out of the resamplers don't have
* leading zeros. This is only useful before starting to use a newly created
* resampler. It is recommended to use that when resampling an audio file, as
* it will generate a file with the same length. For real-time processing,
* it is probably easier not to use this call (so that the output duration
* is the same for the first frame).
* @param st Resampler state
*/
int speex_resampler_skip_zeros(SpeexResamplerState *st);
/** Reset a resampler so a new (unrelated) stream can be processed.
* @param st Resampler state
*/
int speex_resampler_reset_mem(SpeexResamplerState *st);
/** Returns the English meaning for an error code
* @param err Error code
* @return English string
*/
const char *speex_resampler_strerror(int err);
#ifdef __cplusplus
}
#endif
#endif

115
codecs/speex/stack_alloc.h Normal file
View File

@ -0,0 +1,115 @@
/* Copyright (C) 2002 Jean-Marc Valin */
/**
@file stack_alloc.h
@brief Temporary memory allocation on stack
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef STACK_ALLOC_H
#define STACK_ALLOC_H
#ifdef USE_ALLOCA
# ifdef WIN32
# include <malloc.h>
# else
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# else
# include <stdlib.h>
# endif
# endif
#endif
/**
* @def ALIGN(stack, size)
*
* Aligns the stack to a 'size' boundary
*
* @param stack Stack
* @param size New size boundary
*/
/**
* @def PUSH(stack, size, type)
*
* Allocates 'size' elements of type 'type' on the stack
*
* @param stack Stack
* @param size Number of elements
* @param type Type of element
*/
/**
* @def VARDECL(var)
*
* Declare variable on stack
*
* @param var Variable to declare
*/
/**
* @def ALLOC(var, size, type)
*
* Allocate 'size' elements of 'type' on stack
*
* @param var Name of variable to allocate
* @param size Number of elements
* @param type Type of element
*/
#ifdef ENABLE_VALGRIND
#include <valgrind/memcheck.h>
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#else
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#endif
#if defined(VAR_ARRAYS)
#define VARDECL(var)
#define ALLOC(var, size, type) type var[size]
#elif defined(USE_ALLOCA)
#define VARDECL(var) var
#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
#else
#define VARDECL(var) var
#define ALLOC(var, size, type) var = PUSH(stack, size, type)
#endif
#endif

View File

@ -2462,11 +2462,11 @@ AC_SUBST(LUA_CFLAGS)
dnl portaudio check
AC_MSG_CHECKING(whether to use libportaudio for the rtp_player)
AC_MSG_CHECKING(whether to use libportaudio for the GTK+ RTP player)
AC_ARG_WITH(portaudio,
AC_HELP_STRING( [--with-portaudio@<:@=DIR@:>@],
[use libportaudio (located in directory DIR, if supplied) for the rtp_player @<:@default=yes, if available@:>@]),
[use libportaudio (located in directory DIR, if supplied) for the GTK+ RTP player @<:@default=yes, if available@:>@]),
[
if test $withval = no
then
@ -2491,7 +2491,7 @@ else
AC_MSG_RESULT(yes)
AC_WIRESHARK_LIBPORTAUDIO_CHECK
if test "x$want_portaudio" = "xno" ; then
AC_MSG_RESULT(libportaudio not found - disabling support for the rtp_player)
AC_MSG_RESULT(libportaudio not found - disabling support for the GTK+ RTP player)
fi
fi
AM_CONDITIONAL(HAVE_LIBPORTAUDIO, test x$want_portaudio = xyes)
@ -3310,6 +3310,12 @@ else
lua_message="no"
fi
if test "x$have_qt_multimedia_lib" = "xyes" ; then
qt_multimedia_message="yes"
else
qt_multimedia_message="no"
fi
if test "x$want_portaudio" = "xyes" ; then
portaudio_message="yes"
else
@ -3382,7 +3388,8 @@ echo " Use dumpcap group : $dumpcap_group_message"
echo " Use plugins : $have_plugins"
echo " Use external capture sources : $have_extcap"
echo " Use Lua library : $lua_message"
echo " Build rtp_player : $portaudio_message"
echo " Build Qt RTP player : $qt_multimedia_message"
echo " Build GTK+ RTP player : $portaudio_message"
echo " Build profile binaries : $enable_profile_build"
echo " Use pcap library : $want_pcap"
echo " Use zlib library : $zlib_message"

View File

@ -52,7 +52,16 @@ streams of a selected IAX2 call along with a graph.
The VoIP Calls window shows a list of all detected VoIP calls in the captured
traffic. It finds calls by their signaling.
More details are described at the
More details can be found on the
link:wireshark-wiki-site:[]VoIP_calls[wireshark-wiki-site:[]VoIP_calls] page.
[[ChTelRtpPlayer]]
The RTP Player window lets you play back RTP audio data. In order to use
this feature your version of Wireshark must support audio and the codecs
used by each RTP stream.
More details can be found on the
link:wireshark-wiki-site:[]VoIP_calls[wireshark-wiki-site:[]VoIP_calls] page.
[[ChTelLTEMACTraffic]]

View File

@ -42,8 +42,8 @@ struct _rtp_info {
guint32 info_sync_src;
guint info_data_len; /* length of raw rtp data as reported */
gboolean info_all_data_present; /* FALSE if data is cut off */
guint info_payload_offset; /* start of payload relative to info_data */
guint info_payload_len; /* length of payload (incl padding) */
size_t info_payload_offset; /* start of payload relative to info_data */
size_t info_payload_len; /* length of payload (incl padding) */
gboolean info_is_srtp;
guint32 info_setup_frame_num; /* the frame num of the packet that set this RTP connection */
const guint8* info_data; /* pointer to raw rtp data */

View File

@ -785,7 +785,7 @@ rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
return 0;
}
saveinfo->count += (rtpinfo->info_payload_len - rtpinfo->info_padding_count);
saveinfo->count += ((int)rtpinfo->info_payload_len - rtpinfo->info_padding_count);
fflush(saveinfo->fp);
saveinfo->saved = TRUE;

View File

@ -189,7 +189,7 @@ typedef struct _rtp_channel_info {
} rtp_channel_info_t;
/* defines the two RTP channels to be played */
typedef struct _rtp_play_channles {
typedef struct _rtp_play_channels {
rtp_channel_info_t* rci[2]; /* Channels to be played */
guint32 start_index[2];
guint32 end_index[2];
@ -392,6 +392,8 @@ mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U
*/
rsi->decode = FALSE;
/* RTP_STREAM_DEBUG("call num: %u, start frame: %u, ssrc: 0x%x, packets: %u, %d voip calls", rsi->call_num, rsi->start_fd->num, rsi->ssrc, rsi->packet_count, voip_calls->ncalls); */
/* and associate the RTP stream with a call using the first RTP packet in the stream */
graph_list = g_queue_peek_nth_link(voip_calls->graph_analysis->items, 0);
while (graph_list)
@ -405,6 +407,7 @@ mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U
{
tmp_voip_call = (voip_calls_info_t *)voip_calls_list->data;
if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
/* RTP_STREAM_DEBUG("decoding call %u", rsi->call_num); */
rsi->decode = TRUE;
total_packets += rsi->packet_count;
break;
@ -510,6 +513,7 @@ decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr)
rsi->dest_port, rsi->call_num, info->current_channel);
wmem_free(NULL, src_addr);
wmem_free(NULL, dst_addr);
/* RTP_STREAM_DEBUG("decoding stream %s, ssrc 0x%x, %d packets", key_str->str, rsi->ssrc, rsi->packet_count); */
/* create the rtp_channels_hash table if it doesn't exist */
if (!rtp_channels_hash) {
@ -2148,6 +2152,7 @@ decode_streams(void)
g_list_foreach(rtp_streams_list, (GFunc) decode_rtp_stream, &info);
}
/* RTP_STREAM_DEBUG("decoded %d streams", g_list_length(rtp_streams_list)); */
/* 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 */

View File

@ -404,6 +404,7 @@ voip_calls_mark_selected(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter
gtk_tree_model_get(model, iter, CALL_COL_DATA, &strinfo, -1);
strinfo->selected = gtk_tree_selection_iter_is_selected(selection, iter);
/* VOIP_CALLS_DEBUG("selected call %u (%s), frame %u: %d", strinfo->call_num, strinfo->call_id, strinfo->start_fd->num, strinfo->selected); */
return FALSE;
}

View File

@ -351,6 +351,10 @@ topic_action_url(topic_action_e action)
case(HELP_IAX2_ANALYSIS_DIALOG):
url = user_guide_url("ChTelIAX2Analysis.html");
break;
case(HELP_TELEPHONY_RTP_PLAYER_DIALOG):
url = user_guide_url("ChTelRtpPlayer.html");
break;
case(TOPIC_ACTION_NONE):
default:
g_assert_not_reached();

View File

@ -118,7 +118,8 @@ typedef enum {
HELP_TELEPHONY_VOIP_CALLS_DIALOG,
HELP_RTP_ANALYSIS_DIALOG,
HELP_NEW_PACKET_DIALOG,
HELP_IAX2_ANALYSIS_DIALOG
HELP_IAX2_ANALYSIS_DIALOG,
HELP_TELEPHONY_RTP_PLAYER_DIALOG
} topic_action_e;
/** Given a filename return a filesystem URL. Relative paths are prefixed with

View File

@ -114,6 +114,8 @@ set(WIRESHARK_QT_HEADERS
response_time_delay_dialog.h
rpc_service_response_time_dialog.h
rtp_analysis_dialog.h
rtp_audio_stream.h
rtp_player_dialog.h
rtp_stream_dialog.h
sctp_all_assocs_dialog.h
sctp_assoc_analyse_dialog.h
@ -256,6 +258,11 @@ set(WIRESHARK_QT_SRC
response_time_delay_dialog.cpp
rpc_service_response_time_dialog.cpp
rtp_analysis_dialog.cpp
rtp_audio_stream.cpp
rtp_player_dialog.cpp
rtp_stream_dialog.cpp
sctp_all_assocs_dialog.cpp
sctp_assoc_analyse_dialog.cpp
sctp_chunk_statistics_dialog.cpp
sctp_graph_dialog.cpp
sctp_graph_arwnd_dialog.cpp
@ -376,6 +383,7 @@ set(WIRESHARK_QT_UI
protocol_hierarchy_dialog.ui
resolved_addresses_dialog.ui
rtp_analysis_dialog.ui
rtp_player_dialog.ui
rtp_stream_dialog.ui
sctp_all_assocs_dialog.ui
sctp_assoc_analyse_dialog.ui

View File

@ -244,6 +244,8 @@ resolved_addresses_dialog.$(OBJEXT): ui_resolved_addresses_dialog.h
rtp_analysis_dialog.$(OBJEXT): ui_rtp_analysis_dialog.h
rtp_player_dialog.$(OBJEXT): ui_rtp_player_dialog.h
rtp_stream_dialog.$(OBJEXT): ui_rtp_stream_dialog.h
search_frame.$(OBJEXT): ui_search_frame.h

View File

@ -87,6 +87,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_remote_settings_dialog.h \
ui_resolved_addresses_dialog.h \
ui_rtp_analysis_dialog.h \
ui_rtp_player_dialog.h \
ui_rtp_stream_dialog.h \
ui_sctp_all_assocs_dialog.h \
ui_sctp_assoc_analyse_dialog.h \
@ -235,9 +236,12 @@ MOC_HDRS = \
response_time_delay_dialog.h \
rpc_service_response_time_dialog.h \
rtp_analysis_dialog.h \
rtp_audio_stream.h \
rtp_player_dialog.h \
rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
search_frame.h \
sctp_chunk_statistics_dialog.h \
sctp_graph_dialog.h \
sctp_graph_arwnd_dialog.h \
@ -326,6 +330,7 @@ UI_FILES = \
remote_settings_dialog.ui \
resolved_addresses_dialog.ui \
rtp_analysis_dialog.ui \
rtp_player_dialog.ui \
rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
@ -486,6 +491,11 @@ WIRESHARK_QT_SRC = \
response_time_delay_dialog.cpp \
rpc_service_response_time_dialog.cpp \
rtp_analysis_dialog.cpp \
rtp_audio_stream.cpp \
rtp_player_dialog.cpp \
rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \
sctp_chunk_statistics_dialog.cpp \
sctp_graph_dialog.cpp \
sctp_graph_arwnd_dialog.cpp \

View File

@ -26,7 +26,7 @@
isEqual(QT_MAJOR_VERSION, 4) {
QT += core gui
} else {
QT += core widgets printsupport multimediawidgets
QT += core widgets printsupport multimedia
}
isEqual(QT_MAJOR_VERSION, 5): greaterThan(QT_MINOR_VERSION, 1): win32 {
@ -265,6 +265,7 @@ FORMS += \
remote_settings_dialog.ui \
resolved_addresses_dialog.ui \
rtp_analysis_dialog.ui \
rtp_player_dialog.ui \
rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
@ -350,6 +351,8 @@ HEADERS += $$HEADERS_WS_C \
remote_settings_dialog.h \
resolved_addresses_dialog.h \
rtp_analysis_dialog.h \
rtp_audio_stream.h \
rtp_player_dialog.h \
rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
@ -490,7 +493,7 @@ win32 {
EXTRA_DLLS = QtCored4 QtGuid4
} else: lessThan(QT_MINOR_VERSION, 3) {
# The QT lib parts are copied by windeployqt post 5.3
EXTRA_DLLS = Qt5Cored Qt5Guid Qt5Widgetsd Qt5PrintSupportd Qt5MultimediaWidgetsd
EXTRA_DLLS = Qt5Cored Qt5Guid Qt5Widgetsd Qt5PrintSupportd Qt5Multimediad
EXTRA_PLATFORM_DLLS = qwindowsd
QMAKE_POST_LINK +=$$quote($(CHK_DIR_EXISTS) $${PLATFORM_DLL_DIR} $(MKDIR) $${PLATFORM_DLL_DIR}$$escape_expand(\\n\\t))
}
@ -500,7 +503,7 @@ win32 {
EXTRA_DLLS = QtCore4 QtGui4
} else: lessThan(QT_MINOR_VERSION, 3) {
# The QT lib parts are copied by windeployqt post 5.3
EXTRA_DLLS = Qt5Core Qt5Gui Qt5Widgets Qt5PrintSupport Qt5MultimediaWidgets
EXTRA_DLLS = Qt5Core Qt5Gui Qt5Widgets Qt5PrintSupport Qt5Multimedia
EXTRA_PLATFORM_DLLS = qwindows
QMAKE_POST_LINK +=$$quote($(CHK_DIR_EXISTS) $${PLATFORM_DLL_DIR} $(MKDIR) $${PLATFORM_DLL_DIR}$$escape_expand(\\n\\t))
}
@ -756,6 +759,8 @@ SOURCES += \
resolved_addresses_dialog.cpp \
rpc_service_response_time_dialog.cpp \
rtp_analysis_dialog.cpp \
rtp_audio_stream.cpp \
rtp_player_dialog.cpp \
rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \

View File

@ -89,8 +89,7 @@ PacketDialog::PacketDialog(QWidget &parent, CaptureFile &cf, frame_data *fdata)
}
byte_view_tab_->setCurrentIndex(0);
ui->packetSplitter->setStretchFactor(0, 5);
ui->packetSplitter->setStretchFactor(1, 1);
ui->packetSplitter->setStretchFactor(1, 0);
QStringList col_parts;
for (int i = 0; i < cap_file_.capFile()->cinfo.num_cols; ++i) {

396
ui/qt/rtp_audio_stream.cpp Normal file
View File

@ -0,0 +1,396 @@
/* rtp_audio_frame.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "rtp_audio_stream.h"
#ifdef QT_MULTIMEDIA_LIB
#include <codecs/speex/speex_resampler.h>
#include <epan/rtp_pt.h>
#include <epan/dissectors/packet-rtp.h>
#include <ui/rtp_media.h>
#include <ui/rtp_stream.h>
#include <wsutil/nstime.h>
#include <QAudioFormat>
#include <QAudioOutput>
#include <QDir>
#include <QTemporaryFile>
static spx_int16_t default_audio_sample_rate_ = 8000;
static const spx_int16_t visual_sample_rate_ = 1000;
RtpAudioStream::RtpAudioStream(QObject *parent, _rtp_stream_info *rtp_stream) :
QObject(parent),
decoders_hash_(rtp_decoder_hash_table_new()),
global_start_rel_time_(0.0),
start_abs_offset_(0.0),
start_rel_time_(0.0),
stop_rel_time_(0.0),
audio_out_rate_(0),
audio_resampler_(0),
audio_output_(0),
max_sample_val_(1)
{
copy_address(&src_addr_, &rtp_stream->src_addr);
src_port_ = rtp_stream->src_port;
copy_address(&dst_addr_, &rtp_stream->dest_addr);
dst_port_ = rtp_stream->dest_port;
ssrc_ = rtp_stream->ssrc;
// We keep visual samples in memory. Make fewer of them.
visual_resampler_ = ws_codec_resampler_init(1, default_audio_sample_rate_,
visual_sample_rate_, SPEEX_RESAMPLER_QUALITY_MIN, NULL);
ws_codec_resampler_skip_zeros(visual_resampler_);
QString tempname = QString("%1/wireshark_rtp_stream").arg(QDir::tempPath());
tempfile_ = new QTemporaryFile(tempname, this);
tempfile_->open();
// RTP_STREAM_DEBUG("Writing to %s", tempname.toUtf8().constData());
}
RtpAudioStream::~RtpAudioStream()
{
g_hash_table_destroy(decoders_hash_);
if (audio_resampler_) ws_codec_resampler_destroy (audio_resampler_);
ws_codec_resampler_destroy (visual_resampler_);
}
bool RtpAudioStream::isMatch(const _rtp_stream_info *rtp_stream) const
{
if (rtp_stream
&& addresses_equal(&rtp_stream->src_addr, &src_addr_)
&& rtp_stream->src_port == src_port_
&& addresses_equal(&rtp_stream->dest_addr, &dst_addr_)
&& rtp_stream->dest_port == dst_port_
&& rtp_stream->ssrc == ssrc_)
return true;
return false;
}
bool RtpAudioStream::isMatch(const _packet_info *pinfo, const _rtp_info *rtp_info) const
{
if (pinfo && rtp_info
&& addresses_equal(&pinfo->src, &src_addr_)
&& pinfo->srcport == src_port_
&& addresses_equal(&pinfo->dst, &dst_addr_)
&& pinfo->destport == dst_port_
&& rtp_info->info_sync_src == ssrc_)
return true;
return false;
}
// XXX We add multiple RTP streams here because that's what the GTK+ UI does.
// Should we make these distinct, with their own waveforms? It seems like
// that would simplify a lot of things.
void RtpAudioStream::addRtpStream(const _rtp_stream_info *rtp_stream)
{
if (!rtp_stream) return;
// RTP_STREAM_DEBUG("added %d:%u packets", g_list_length(rtp_stream->rtp_packet_list), rtp_stream->packet_count);
rtp_streams_ << rtp_stream;
double stream_srt = nstime_to_sec(&rtp_stream->start_rel_time);
if (rtp_streams_.length() < 2 || stream_srt > start_rel_time_) {
start_rel_time_ = stop_rel_time_ = stream_srt;
start_abs_offset_ = nstime_to_sec(&rtp_stream->start_fd->abs_ts) - start_rel_time_;
}
}
static const int max_payload_len_ = 1000; // Arbitrary.
static const int sample_bytes_ = sizeof(SAMPLE) / sizeof(char);
void RtpAudioStream::addRtpPacket(const struct _packet_info *pinfo, const _rtp_info *rtp_info)
{
if (!rtp_info) return;
// Combination of gtk/rtp_player.c:decode_rtp_stream + decode_rtp_packet
// XXX This is more messy than it should be.
size_t decoded_bytes;
SAMPLE *decode_buff = NULL;
SAMPLE *resample_buff = NULL;
spx_uint32_t cur_in_rate, visual_out_rate;
char *write_buff;
qint64 write_bytes;
unsigned channels;
unsigned sample_rate;
rtp_packet_t rtp_packet;
stop_rel_time_ = nstime_to_sec(&pinfo->rel_ts);
ws_codec_resampler_get_rate(visual_resampler_, &cur_in_rate, &visual_out_rate);
QString payload_name;
if (rtp_info->info_payload_type_str) {
payload_name = rtp_info->info_payload_type_str;
} else {
payload_name = try_val_to_str_ext(rtp_info->info_payload_type, &rtp_payload_type_short_vals_ext);
}
if (!payload_name.isEmpty()) {
payload_names_ << payload_name;
}
// First, decode the payload.
rtp_packet.info = (_rtp_info *) g_memdup(rtp_info, sizeof(struct _rtp_info));
rtp_packet.arrive_offset = start_rel_time_;
if (rtp_info->info_all_data_present && (rtp_info->info_payload_len != 0)) {
rtp_packet.payload_data = (guint8 *)g_malloc(rtp_info->info_payload_len);
memcpy(rtp_packet.payload_data, rtp_info->info_data + rtp_info->info_payload_offset, rtp_info->info_payload_len);
} else {
rtp_packet.payload_data = NULL;
}
decoded_bytes = decode_rtp_packet(&rtp_packet, &decode_buff, decoders_hash_, &channels, &sample_rate);
write_buff = (char *) decode_buff;
write_bytes = rtp_info->info_payload_len * sample_bytes_;
if (tempfile_->pos() == 0) {
// First packet. Let it determine our sample rate.
audio_out_rate_ = sample_rate;
last_sequence_ = rtp_info->info_seq_num - 1;
// Prepend silence to match our sibling streams.
int prepend_samples = (start_rel_time_ - global_start_rel_time_) * audio_out_rate_;
if (prepend_samples > 0) {
int prepend_bytes = prepend_samples * sample_bytes_;
char *prepend_buff = (char *) g_malloc(prepend_bytes);
SAMPLE silence = 0;
memccpy(prepend_buff, &silence, prepend_samples, sample_bytes_);
tempfile_->write(prepend_buff, prepend_bytes);
}
} else if (audio_out_rate_ != sample_rate) {
// Resample the audio to match our previous output rate.
if (!audio_resampler_) {
audio_resampler_ = ws_codec_resampler_init(1, sample_rate, audio_out_rate_, 10, NULL);
ws_codec_resampler_skip_zeros(audio_resampler_);
// RTP_STREAM_DEBUG("Started resampling from %u to (out) %u Hz.", sample_rate, audio_out_rate_);
} else {
spx_uint32_t audio_out_rate;
ws_codec_resampler_get_rate(audio_resampler_, &cur_in_rate, &audio_out_rate);
// Adjust rates if needed.
if (sample_rate != cur_in_rate) {
ws_codec_resampler_set_rate(audio_resampler_, sample_rate, audio_out_rate);
ws_codec_resampler_set_rate(visual_resampler_, sample_rate, visual_out_rate);
// RTP_STREAM_DEBUG("Changed input rate from %u to %u Hz. Out is %u.", cur_in_rate, sample_rate, audio_out_rate_);
}
}
spx_uint32_t in_len = (spx_uint32_t)rtp_info->info_payload_len;
spx_uint32_t out_len = (audio_out_rate_ * (spx_uint32_t)rtp_info->info_payload_len / sample_rate) + (audio_out_rate_ % sample_rate != 0);
resample_buff = (SAMPLE *) g_malloc(out_len * sample_bytes_);
ws_codec_resampler_process_int(audio_resampler_, 0, decode_buff, &in_len, resample_buff, &out_len);
write_buff = (char *) decode_buff;
write_bytes = out_len * sample_bytes_;
}
if (rtp_info->info_seq_num != last_sequence_+1) {
out_of_seq_timestamps_.append(stop_rel_time_);
// XXX Add silence to tempfile_ and visual_samples_
}
last_sequence_ = rtp_info->info_seq_num;
// Write the decoded, possibly-resampled audio to our temp file.
tempfile_->write(write_buff, write_bytes);
// Collect our visual samples.
spx_uint32_t in_len = (spx_uint32_t)rtp_info->info_payload_len;
spx_uint32_t out_len = (visual_out_rate * in_len / sample_rate) + (visual_out_rate % sample_rate != 0);
resample_buff = (SAMPLE *) g_realloc(resample_buff, out_len * sizeof(SAMPLE));
ws_codec_resampler_process_int(visual_resampler_, 0, decode_buff, &in_len, resample_buff, &out_len);
for (unsigned i = 0; i < out_len; i++) {
packet_timestamps_[stop_rel_time_ + (double) i / visual_out_rate] = pinfo->fd->num;
if (qAbs(resample_buff[i]) > max_sample_val_) max_sample_val_ = qAbs(resample_buff[i]);
visual_samples_.append(resample_buff[i]);
}
// Finally, write the resampled audio to our temp file and clean up.
g_free(rtp_packet.payload_data);
g_free(decode_buff);
g_free(resample_buff);
}
void RtpAudioStream::reset(double start_rel_time)
{
last_sequence_ = 0;
global_start_rel_time_ = start_rel_time;
stop_rel_time_ = start_rel_time_;
audio_out_rate_ = 0;
max_sample_val_ = 1;
packet_timestamps_.clear();
visual_samples_.clear();
out_of_seq_timestamps_.clear();
if (audio_resampler_) {
ws_codec_resampler_reset_mem(audio_resampler_);
}
if (visual_resampler_) {
ws_codec_resampler_reset_mem(visual_resampler_);
}
tempfile_->seek(0);
}
const QStringList RtpAudioStream::payloadNames() const
{
QStringList payload_names = payload_names_.toList();
payload_names.sort();
return payload_names;
}
const QVector<double> RtpAudioStream::visualTimestamps(bool relative)
{
QVector<double> ts_keys = packet_timestamps_.keys().toVector();
if (relative) return ts_keys;
QVector<double> adj_timestamps;
for (int i = 0; i < ts_keys.length(); i++) {
adj_timestamps.append(ts_keys[i] + start_abs_offset_);
}
return adj_timestamps;
}
// Scale the height of the waveform (max_sample_val_) and adjust its Y
// offset so that they overlap slightly (stack_offset_).
// XXX This means that waveforms can be misleading with respect to relative
// amplitude. We might want to add a "global" max_sample_val_.
static const double stack_offset_ = G_MAXINT16 / 3;
const QVector<double> RtpAudioStream::visualSamples(int y_offset)
{
QVector<double> adj_samples;
double scaled_offset = y_offset * stack_offset_;
for (int i = 0; i < visual_samples_.length(); i++) {
adj_samples.append(((double)visual_samples_[i] * G_MAXINT16 / max_sample_val_) + scaled_offset);
}
return adj_samples;
}
const QVector<double> RtpAudioStream::outOfSequenceTimestamps(bool relative)
{
if (relative) return out_of_seq_timestamps_;
QVector<double> adj_timestamps;
for (int i = 0; i < out_of_seq_timestamps_.length(); i++) {
adj_timestamps.append(out_of_seq_timestamps_[i] + start_abs_offset_);
}
return adj_timestamps;
}
const QVector<double> RtpAudioStream::outOfSequenceSamples(int y_offset)
{
QVector<double> adj_samples;
double scaled_offset = y_offset * stack_offset_;
for (int i = 0; i < out_of_seq_timestamps_.length(); i++) {
adj_samples.append(scaled_offset);
}
return adj_samples;
}
quint32 RtpAudioStream::nearestPacket(double timestamp, bool is_relative)
{
if (packet_timestamps_.keys().count() < 1) return 0;
if (!is_relative) timestamp -= start_abs_offset_;
QMap<double, quint32>::const_iterator it = packet_timestamps_.lowerBound(timestamp);
if (it == packet_timestamps_.begin()) return 0;
return it.value();
}
QAudio::State RtpAudioStream::outputState() const
{
if (!audio_output_) return QAudio::IdleState;
return audio_output_->state();
}
void RtpAudioStream::startPlaying()
{
if (audio_output_) return;
QAudioFormat format;
format.setSampleRate(audio_out_rate_);
format.setSampleSize(sample_bytes_ * 8); // bits
format.setSampleType(QAudioFormat::SignedInt);
format.setChannelCount(1);
format.setCodec("audio/pcm");
// RTP_STREAM_DEBUG("playing %s %d samples @ %u Hz",
// tempfile_->fileName().toUtf8().constData(),
// (int) tempfile_->size(), audio_out_rate_);
audio_output_ = new QAudioOutput(format, this);
audio_output_->setNotifyInterval(65); // ~15 fps
connect(audio_output_, SIGNAL(stateChanged(QAudio::State)), this, SLOT(outputStateChanged()));
connect(audio_output_, SIGNAL(notify()), this, SLOT(outputNotify()));
tempfile_->seek(0);
audio_output_->start(tempfile_);
emit startedPlaying();
}
void RtpAudioStream::stopPlaying()
{
if (audio_output_) {
audio_output_->stop();
delete audio_output_;
audio_output_ = NULL;
}
emit finishedPlaying();
}
void RtpAudioStream::outputStateChanged()
{
if (!audio_output_) return;
if (audio_output_->state() == QAudio::IdleState) {
// RTP_STREAM_DEBUG("stopped %f", audio_output_->processedUSecs() / 100000.0);
delete audio_output_;
audio_output_ = NULL;
emit finishedPlaying();
}
}
void RtpAudioStream::outputNotify()
{
if (!audio_output_) return;
emit processedSecs(audio_output_->processedUSecs() / 1000000.0);
}
#endif // QT_MULTIMEDIA_LIB
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

151
ui/qt/rtp_audio_stream.h Normal file
View File

@ -0,0 +1,151 @@
/* rtp_audio_stream.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef RTPAUDIOSTREAM_H
#define RTPAUDIOSTREAM_H
#include "config.h"
#ifdef QT_MULTIMEDIA_LIB
#include <glib.h>
#include <epan/address.h>
#include <QAudio>
#include <QColor>
#include <QMap>
#include <QObject>
#include <QSet>
#include <QVector>
class QAudioOutput;
class QTemporaryFile;
struct _rtp_stream_info;
struct _rtp_sample;
class RtpAudioStream : public QObject
{
Q_OBJECT
public:
explicit RtpAudioStream(QObject *parent, struct _rtp_stream_info *rtp_stream);
~RtpAudioStream();
bool isMatch(const struct _rtp_stream_info *rtp_stream) const;
bool isMatch(const struct _packet_info *pinfo, const struct _rtp_info *rtp_info) const;
void addRtpStream(const struct _rtp_stream_info *rtp_stream);
void addRtpPacket(const struct _packet_info *pinfo, const struct _rtp_info *rtp_info);
void reset(double start_rel_time);
double startRelTime() const { return start_rel_time_; }
double stopRelTime() const { return stop_rel_time_; }
unsigned sampleRate() const { return audio_out_rate_; }
const QStringList payloadNames() const;
/**
* @brief Return a list of visual timestamps.
* @return A set of timestamps suitable for passing to QCPGraph::setData.
*/
const QVector<double> visualTimestamps(bool relative = true);
/**
* @brief Return a list of visual samples. There will be fewer visual samples
* per second (1000) than the actual audio.
* @param y_offset Y axis offset to be used for stacking graphs.
* @return A set of values suitable for passing to QCPGraph::setData.
*/
const QVector<double> visualSamples(int y_offset = 0);
/**
* @brief Return a list of out-of-sequence timestamps.
* @return A set of timestamps suitable for passing to QCPGraph::setData.
*/
const QVector<double> outOfSequenceTimestamps(bool relative = true);
int outOfSequence() { return out_of_seq_timestamps_.length(); }
/**
* @brief Return a list of out-of-sequence samples. Y value is constant.
* @param y_offset Y axis offset to be used for stacking graphs.
* @return A set of values suitable for passing to QCPGraph::setData.
*/
const QVector<double> outOfSequenceSamples(int y_offset = 0);
quint32 nearestPacket(double timestamp, bool is_relative = true);
QRgb color() { return color_; }
void setColor(QRgb color) { color_ = color; }
QAudio::State outputState() const;
signals:
void startedPlaying();
void processedSecs(double secs);
void finishedPlaying();
public slots:
void startPlaying();
void stopPlaying();
private:
address src_addr_;
quint16 src_port_;
address dst_addr_;
quint16 dst_port_;
quint32 ssrc_;
int last_sequence_;
QTemporaryFile *tempfile_;
struct _GHashTable *decoders_hash_;
QList<const struct _rtp_stream_info *>rtp_streams_;
double global_start_rel_time_;
double start_abs_offset_;
double start_rel_time_;
double stop_rel_time_;
quint32 audio_out_rate_;
QSet<QString> payload_names_;
struct SpeexResamplerState_ *audio_resampler_;
struct SpeexResamplerState_ *visual_resampler_;
QAudioOutput *audio_output_;
QMap<double, quint32> packet_timestamps_;
QVector<qint16> visual_samples_;
QVector<double> out_of_seq_timestamps_;
qint16 max_sample_val_;
QRgb color_;
private slots:
void outputStateChanged();
void outputNotify();
};
#endif // QT_MULTIMEDIA_LIB
#endif // RTPAUDIOSTREAM_H
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

656
ui/qt/rtp_player_dialog.cpp Normal file
View File

@ -0,0 +1,656 @@
/* rtp_player_dialog.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "rtp_player_dialog.h"
#include "ui_rtp_player_dialog.h"
#ifdef QT_MULTIMEDIA_LIB
#include <epan/dissectors/packet-rtp.h>
#include <ui/utf8_entities.h>
#include "color_utils.h"
#include "qcustomplot.h"
#include "qt_ui_utils.h"
#include "rtp_audio_stream.h"
#include "stock_icon.h"
#include "tango_colors.h"
#include <QAudio>
#include <QFrame>
#include <QMenu>
#include <QVBoxLayout>
Q_DECLARE_METATYPE(RtpAudioStream *)
Q_DECLARE_METATYPE(QCPGraph *)
#endif // QT_MULTIMEDIA_LIB
#include "wireshark_application.h"
// To do:
// - Fully implement shorcuts (drag, go to packet, etc.)
// - Figure out selection and highlighting.
// - Make streams checkable.
// - Add silence, drop & jitter indicators to the graph.
// - How to handle multiple channels?
// - Threaded decoding?
// - Play MP3s. As per Zawinski's Law we already read emails.
// - RTP audio streams are currently keyed on src addr + src port + dst addr
// + dst port + ssrc. This means that we can have multiple rtp_stream_info
// structs per RtpAudioStream. Should we make them 1:1 instead?
// Current RTP player bugs:
// Bug 3368 - The timestamp line in a RTP or RTCP packet display's "Not Representable"
// Bug 3952 - VoIP Call RTP Player: audio played is corrupted when RFC2833 packets are present
// Bug 4960 - RTP Player: Audio and visual feedback get rapidly out of sync
// Bug 5527 - Adding arbitrary value to x-axis RTP player
// Bug 7935 - Wrong Timestamps in RTP Player-Decode
// Bug 8007 - UI gets confused on playing decoded audio in rtp_player
// Bug 9007 - Switching SSRC values in RTP stream
// Bug 10613 - RTP audio player crashes
// Bug 11125 - RTP Player does not show progress in selected stream in Window 7
// Bug 11409 - Wireshark crashes when using RTP player
// XXX It looks like we duplicate some functionality here and in the RTP
// analysis code, which has its own routines for writing audio data to a
// file.
// In some places we match by conv/call number, in others we match by first frame.
enum {
src_addr_col_,
src_port_col_,
dst_addr_col_,
dst_port_col_,
ssrc_col_,
first_pkt_col_,
num_pkts_col_,
time_span_col_,
sample_rate_col_,
payload_col_,
stream_data_col_ = src_addr_col_, // RtpAudioStream
graph_data_col_ = src_port_col_ // QCPGraph
};
static const double wf_graph_normal_width_ = 0.5;
static const double wf_graph_selected_width_ = 2.0;
RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
WiresharkDialog(parent, cf)
#ifdef QT_MULTIMEDIA_LIB
, ui(new Ui::RtpPlayerDialog)
, start_rel_time_(0.0)
#endif // QT_MULTIMEDIA_LIB
{
ui->setupUi(this);
setWindowTitle(wsApp->windowTitleString(tr("RTP Player")));
resize(parent.size());
#ifdef QT_MULTIMEDIA_LIB
ui->splitter->setStretchFactor(0, 3);
ui->splitter->setStretchFactor(1, 1);
ctx_menu_ = new QMenu(this);
ctx_menu_->addAction(ui->actionZoomIn);
ctx_menu_->addAction(ui->actionZoomOut);
ctx_menu_->addAction(ui->actionReset);
ctx_menu_->addSeparator();
ctx_menu_->addAction(ui->actionMoveRight10);
ctx_menu_->addAction(ui->actionMoveLeft10);
ctx_menu_->addAction(ui->actionMoveRight1);
ctx_menu_->addAction(ui->actionMoveLeft1);
ctx_menu_->addSeparator();
ctx_menu_->addAction(ui->actionGoToPacket);
ctx_menu_->addSeparator();
ctx_menu_->addAction(ui->actionDragZoom);
ctx_menu_->addAction(ui->actionToggleTimeOrigin);
ctx_menu_->addAction(ui->actionCrosshairs);
connect(ui->audioPlot, SIGNAL(mouseMove(QMouseEvent*)),
this, SLOT(mouseMoved(QMouseEvent*)));
connect(ui->audioPlot, SIGNAL(mousePress(QMouseEvent*)),
this, SLOT(graphClicked(QMouseEvent*)));
cur_play_pos_ = new QCPItemStraightLine(ui->audioPlot);
ui->audioPlot->addItem(cur_play_pos_);
cur_play_pos_->setVisible(false);
ui->audioPlot->xAxis->setNumberFormat("gb");
ui->audioPlot->xAxis->setNumberPrecision(3);
ui->audioPlot->xAxis->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss.zzz");
ui->audioPlot->yAxis->setVisible(false);
ui->playButton->setIcon(StockIcon("media-playback-start"));
ui->stopButton->setIcon(StockIcon("media-playback-stop"));
ui->audioPlot->setMouseTracking(true);
ui->audioPlot->setEnabled(true);
ui->audioPlot->setInteractions(
QCP::iRangeDrag |
QCP::iRangeZoom
);
ui->audioPlot->setFocus();
QTimer::singleShot(0, this, SLOT(retapPackets()));
#endif // QT_MULTIMEDIA_LIB
}
#ifdef QT_MULTIMEDIA_LIB
RtpPlayerDialog::~RtpPlayerDialog()
{
delete ui;
}
void RtpPlayerDialog::retapPackets(bool rescale_axes)
{
int row_count = ui->streamTreeWidget->topLevelItemCount();
// Clear existing graphs and reset stream values
for (int row = 0; row < row_count; row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
audio_stream->reset(start_rel_time_);
ti->setData(graph_data_col_, Qt::UserRole, QVariant());
}
ui->audioPlot->clearGraphs();
register_tap_listener("rtp", this, NULL, 0, NULL, tapPacket, NULL);
cap_file_.retapPackets();
remove_tap_listener(this);
bool show_legend = false;
bool relative_timestamps = !ui->todCheckBox->isChecked();
ui->audioPlot->xAxis->setTickLabelType(relative_timestamps ? QCPAxis::ltNumber : QCPAxis::ltDateTime);
for (int row = 0; row < row_count; row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
int y_offset = row_count - row - 1;
// Waveform
QCPGraph *audio_graph = ui->audioPlot->addGraph();
QPen wf_pen(audio_stream->color());
wf_pen.setWidthF(wf_graph_normal_width_);
audio_graph->setPen(wf_pen);
wf_pen.setWidthF(wf_graph_selected_width_);
audio_graph->setSelectedPen(wf_pen);
audio_graph->setSelectable(false);
audio_graph->setData(audio_stream->visualTimestamps(relative_timestamps), audio_stream->visualSamples(y_offset));
audio_graph->removeFromLegend();
ti->setData(graph_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(audio_graph));
// RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->data()->keys().length());
QString span_str = QString("%1 - %2 (%3)")
.arg(QString::number(audio_stream->startRelTime(), 'g', 3))
.arg(QString::number(audio_stream->stopRelTime(), 'g', 3))
.arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'g', 3));
ti->setText(time_span_col_, span_str);
ti->setText(sample_rate_col_, QString::number(audio_stream->sampleRate()));
ti->setText(payload_col_, audio_stream->payloadNames().join(", "));
if (audio_stream->outOfSequence() > 0) {
// Sequence numbers
QCPGraph *seq_graph = ui->audioPlot->addGraph();
seq_graph->setLineStyle(QCPGraph::lsNone);
seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssSquare, tango_aluminium_6, Qt::white, 4)); // Arbitrary
seq_graph->setSelectable(false);
seq_graph->setData(audio_stream->outOfSequenceTimestamps(relative_timestamps), audio_stream->outOfSequenceSamples(y_offset));
if (row < 1) {
seq_graph->setName(tr("Out of Sequence"));
show_legend = true;
} else {
seq_graph->removeFromLegend();
}
}
}
ui->audioPlot->legend->setVisible(show_legend);
for (int col = 0; col < ui->streamTreeWidget->columnCount() - 1; col++) {
ui->streamTreeWidget->resizeColumnToContents(col);
}
ui->audioPlot->replot();
if (rescale_axes) resetXAxis();
updateWidgets();
}
void RtpPlayerDialog::addRtpStream(struct _rtp_stream_info *rtp_stream)
{
if (!rtp_stream) return;
// Find the RTP streams associated with this conversation.
// gtk/rtp_player.c:mark_rtp_stream_to_play does this differently.
RtpAudioStream *audio_stream = NULL;
int tli_count = ui->streamTreeWidget->topLevelItemCount();
for (int row = 0; row < tli_count; row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
if (row_stream->isMatch(rtp_stream)) {
audio_stream = row_stream;
break;
}
}
if (!audio_stream) {
audio_stream = new RtpAudioStream(this, rtp_stream);
audio_stream->setColor(ColorUtils::graph_colors_[tli_count % ColorUtils::graph_colors_.length()]);
QTreeWidgetItem *ti = new QTreeWidgetItem(ui->streamTreeWidget);
ti->setText(src_addr_col_, address_to_qstring(&rtp_stream->src_addr));
ti->setText(src_port_col_, QString::number(rtp_stream->src_port));
ti->setText(dst_addr_col_, address_to_qstring(&rtp_stream->dest_addr));
ti->setText(dst_port_col_, QString::number(rtp_stream->dest_port));
ti->setText(ssrc_col_, int_to_qstring(rtp_stream->ssrc, 8, 16));
ti->setText(first_pkt_col_, QString::number(rtp_stream->setup_frame_number));
ti->setText(num_pkts_col_, QString::number(rtp_stream->packet_count));
ti->setData(stream_data_col_, Qt::UserRole, QVariant::fromValue(audio_stream));
for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) {
ti->setTextColor(col, audio_stream->color());
}
connect(ui->playButton, SIGNAL(clicked(bool)), audio_stream, SLOT(startPlaying()));
connect(ui->stopButton, SIGNAL(clicked(bool)), audio_stream, SLOT(stopPlaying()));
connect(audio_stream, SIGNAL(startedPlaying()), this, SLOT(updateWidgets()));
connect(audio_stream, SIGNAL(finishedPlaying()), this, SLOT(updateWidgets()));
connect(audio_stream, SIGNAL(processedSecs(double)), this, SLOT(setPlayPosition(double)));
}
audio_stream->addRtpStream(rtp_stream);
double start_rel_time = nstime_to_sec(&rtp_stream->start_rel_time);
if (tli_count < 2) {
start_rel_time_ = start_rel_time;
} else {
start_rel_time_ = qMin(start_rel_time_, start_rel_time);
}
// RTP_STREAM_DEBUG("adding stream %s to layout, %u packets, start %u", stream_key.toUtf8().constData(), rtp_stream->packet_count, rtp_stream->start_fd->num);
}
void RtpPlayerDialog::showEvent(QShowEvent *)
{
QList<int> split_sizes = ui->splitter->sizes();
int tot_size = split_sizes[0] + split_sizes[1];
int plot_size = tot_size * 3 / 4;
split_sizes.clear();
split_sizes << plot_size << tot_size - plot_size;
ui->splitter->setSizes(split_sizes);
}
void RtpPlayerDialog::keyPressEvent(QKeyEvent *event)
{
int pan_secs = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
switch(event->key()) {
case Qt::Key_Minus:
case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
case Qt::Key_O: // GTK+
case Qt::Key_R:
on_actionZoomOut_triggered();
break;
case Qt::Key_Plus:
case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
case Qt::Key_I: // GTK+
on_actionZoomIn_triggered();
break;
case Qt::Key_Right:
case Qt::Key_L:
panXAxis(pan_secs);
break;
case Qt::Key_Left:
case Qt::Key_H:
panXAxis(-1 * pan_secs);
break;
case Qt::Key_Space:
// toggleTracerStyle();
break;
case Qt::Key_0:
case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
case Qt::Key_Home:
on_actionReset_triggered();
break;
case Qt::Key_G:
on_actionGoToPacket_triggered();
break;
case Qt::Key_T:
// on_actionToggleTimeOrigin_triggered();
break;
case Qt::Key_Z:
// on_actionDragZoom_triggered();
break;
}
QDialog::keyPressEvent(event);
}
void RtpPlayerDialog::updateWidgets()
{
bool enable_play = true;
bool enable_stop = false;
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *audio_stream = ti->data(src_addr_col_, Qt::UserRole).value<RtpAudioStream*>();
if (audio_stream->outputState() != QAudio::IdleState) {
enable_play = false;
enable_stop = true;
}
}
ui->playButton->setEnabled(enable_play);
ui->stopButton->setEnabled(enable_stop);
cur_play_pos_->setVisible(enable_stop);
ui->audioPlot->replot();
}
void RtpPlayerDialog::graphClicked(QMouseEvent *event)
{
updateWidgets();
if (event->button() == Qt::RightButton) {
ctx_menu_->exec(event->globalPos());
}
ui->audioPlot->setFocus();
}
void RtpPlayerDialog::mouseMoved(QMouseEvent *)
{
int packet_num = getHoveredPacket();
QString hint = "<small><i>";
if (packet_num > 0) {
hint += tr("%1. Press \"G\" to to to packet %2")
.arg(getHoveredTime())
.arg(packet_num);
}
hint += "</i></small>";
ui->hintLabel->setText(hint);
}
void RtpPlayerDialog::resetXAxis()
{
QCustomPlot *ap = ui->audioPlot;
QCPRange x_range = ap->xAxis->range();
double pixel_pad = 10.0; // per side
ap->rescaleAxes(true);
double axis_pixels = ap->xAxis->axisRect()->width();
ap->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center());
axis_pixels = ap->yAxis->axisRect()->height();
ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center());
ap->replot();
}
void RtpPlayerDialog::setPlayPosition(double secs)
{
secs+= ui->audioPlot->xAxis->range().lower;
double cur_secs = cur_play_pos_->point1->key();
if (secs > cur_secs) {
cur_play_pos_->point1->setCoords(secs, 0.0);
cur_play_pos_->point2->setCoords(secs, 1.0);
ui->audioPlot->replot();
}
}
gboolean RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr)
{
RtpPlayerDialog *rtp_player_dialog = dynamic_cast<RtpPlayerDialog *>((RtpPlayerDialog*)tapinfo_ptr);
if (!rtp_player_dialog) return FALSE;
const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr;
if (!rtpinfo) return FALSE;
/* we ignore packets that are not displayed */
if (pinfo->fd->flags.passed_dfilter == 0)
return FALSE;
/* also ignore RTP Version != 2 */
else if (rtpinfo->info_version != 2)
return FALSE;
rtp_player_dialog->addPacket(pinfo, rtpinfo);
return FALSE;
}
void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo)
{
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
if (row_stream->isMatch(pinfo, rtpinfo)) {
row_stream->addRtpPacket(pinfo, rtpinfo);
return;
}
}
// qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst);
}
void RtpPlayerDialog::zoomXAxis(bool in)
{
QCustomPlot *ap = ui->audioPlot;
double h_factor = ap->axisRect()->rangeZoomFactor(Qt::Horizontal);
if (!in) {
h_factor = pow(h_factor, -1);
}
ap->xAxis->scaleRange(h_factor, ap->xAxis->range().center());
ap->replot();
}
// XXX I tried using seconds but pixels make more sense at varying zoom
// levels.
void RtpPlayerDialog::panXAxis(int x_pixels)
{
QCustomPlot *ap = ui->audioPlot;
double h_pan;
h_pan = ap->xAxis->range().size() * x_pixels / ap->xAxis->axisRect()->width();
if (x_pixels) {
ap->xAxis->moveRange(h_pan);
ap->replot();
}
}
void RtpPlayerDialog::on_playButton_clicked()
{
double left = ui->audioPlot->xAxis->range().lower;
cur_play_pos_->point1->setCoords(left, 0.0);
cur_play_pos_->point2->setCoords(left, 1.0);
cur_play_pos_->setVisible(true);
ui->audioPlot->replot();
}
void RtpPlayerDialog::on_stopButton_clicked()
{
cur_play_pos_->setVisible(false);
}
void RtpPlayerDialog::on_actionReset_triggered()
{
resetXAxis();
}
void RtpPlayerDialog::on_actionZoomIn_triggered()
{
zoomXAxis(true);
}
void RtpPlayerDialog::on_actionZoomOut_triggered()
{
zoomXAxis(false);
}
void RtpPlayerDialog::on_actionMoveLeft10_triggered()
{
panXAxis(-10);
}
void RtpPlayerDialog::on_actionMoveRight10_triggered()
{
panXAxis(10);
}
void RtpPlayerDialog::on_actionMoveLeft1_triggered()
{
panXAxis(-1);
}
void RtpPlayerDialog::on_actionMoveRight1_triggered()
{
panXAxis(1);
}
void RtpPlayerDialog::on_actionGoToPacket_triggered()
{
int packet_num = getHoveredPacket();
if (packet_num > 0) emit goToPacket(packet_num);
}
// XXX Make waveform graphs selectable and update the treewidget selection accordingly.
void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged()
{
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
QCPGraph *audio_graph = ti->data(graph_data_col_, Qt::UserRole).value<QCPGraph*>();
if (audio_graph) {
audio_graph->setSelected(ti->isSelected());
}
}
ui->audioPlot->replot();
}
double RtpPlayerDialog::getLowestTimestamp()
{
double lowest = QCPRange::maxRange;
for (int i = 0; i < ui->audioPlot->graphCount(); i++) {
QCPGraph *graph = ui->audioPlot->graph(i);
if (!graph->visible()) continue;
QCPDataMap *dm = graph->data();
if (dm->keys().length() < 1) continue;
lowest = qMin(lowest, dm->keys().first());
}
return lowest;
}
const QString RtpPlayerDialog::getHoveredTime()
{
QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
if (!ti) return tr("Unknown");
QString time_str;
double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(QCursor::pos()).x());
if (ui->todCheckBox->isChecked()) {
QDateTime date_time = QDateTime::fromMSecsSinceEpoch(ts * 1000.0);
time_str = date_time.toString("yyyy-MM-dd hh:mm:ss.zzz");
} else {
time_str = QString::number(ts, 'f', 3);
time_str += " s";
}
return time_str;
}
int RtpPlayerDialog::getHoveredPacket()
{
QTreeWidgetItem *ti = ui->streamTreeWidget->currentItem();
if (!ti) return 0;
RtpAudioStream *audio_stream = ti->data(src_addr_col_, Qt::UserRole).value<RtpAudioStream*>();
double ts = ui->audioPlot->xAxis->pixelToCoord(ui->audioPlot->mapFromGlobal(QCursor::pos()).x());
return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked());
}
void RtpPlayerDialog::on_todCheckBox_toggled(bool)
{
QCPAxis *x_axis = ui->audioPlot->xAxis;
double old_lowest = getLowestTimestamp();
retapPackets(false);
x_axis->moveRange(getLowestTimestamp() - old_lowest);
ui->audioPlot->replot();
}
void RtpPlayerDialog::on_buttonBox_helpRequested()
{
wsApp->helpTopicAction(HELP_TELEPHONY_RTP_PLAYER_DIALOG);
}
#if 0
// This also serves as a title in RtpAudioFrame.
static const QString stream_key_tmpl_ = "%1:%2 " UTF8_RIGHTWARDS_ARROW " %3:%4 0x%5";
const QString RtpPlayerDialog::streamKey(const struct _rtp_stream_info *rtp_stream)
{
const QString stream_key = QString(stream_key_tmpl_)
.arg(address_to_display_qstring(&rtp_stream->src_addr))
.arg(rtp_stream->src_port)
.arg(address_to_display_qstring(&rtp_stream->dest_addr))
.arg(rtp_stream->dest_port)
.arg(rtp_stream->ssrc, 0, 16);
return stream_key;
}
const QString RtpPlayerDialog::streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo)
{
const QString stream_key = QString(stream_key_tmpl_)
.arg(address_to_display_qstring(&pinfo->src))
.arg(pinfo->srcport)
.arg(address_to_display_qstring(&pinfo->dst))
.arg(pinfo->destport)
.arg(rtpinfo->info_sync_src, 0, 16);
return stream_key;
}
#endif
#endif // QT_MULTIMEDIA_LIB
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

136
ui/qt/rtp_player_dialog.h Normal file
View File

@ -0,0 +1,136 @@
/* rtp_player_dialog.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef RTP_PLAYER_DIALOG_H
#define RTP_PLAYER_DIALOG_H
#include "config.h"
#include <glib.h>
#include "ui/rtp_stream.h"
#include "wireshark_dialog.h"
#include <QMap>
namespace Ui {
class RtpPlayerDialog;
}
struct _rtp_stream_info;
class QCPItemStraightLine;
class QMenu;
class RtpAudioStream;
class RtpPlayerDialog : public WiresharkDialog
{
Q_OBJECT
public:
explicit RtpPlayerDialog(QWidget &parent, CaptureFile &cf);
#ifdef QT_MULTIMEDIA_LIB
~RtpPlayerDialog();
/** Add an RTP stream to play.
* MUST be called before exec().
*
* @param rtp_stream
*/
void addRtpStream(struct _rtp_stream_info *rtp_stream);
public slots:
signals:
void goToPacket(int packet_num);
protected:
virtual void showEvent(QShowEvent *);
virtual void keyPressEvent(QKeyEvent *event);
private slots:
/** Retap the capture file, adding RTP packets that match the
* streams added using ::addRtpStream and display the dialog.
*/
void retapPackets(bool rescale_axes = true);
void updateWidgets();
void graphClicked(QMouseEvent *event);
void mouseMoved(QMouseEvent *);
void resetXAxis();
void setPlayPosition(double secs);
void on_playButton_clicked();
void on_stopButton_clicked();
void on_actionReset_triggered();
void on_actionZoomIn_triggered();
void on_actionZoomOut_triggered();
void on_actionMoveLeft10_triggered();
void on_actionMoveRight10_triggered();
void on_actionMoveLeft1_triggered();
void on_actionMoveRight1_triggered();
void on_actionGoToPacket_triggered();
void on_streamTreeWidget_itemSelectionChanged();
void on_todCheckBox_toggled(bool checked);
void on_buttonBox_helpRequested();
private:
Ui::RtpPlayerDialog *ui;
QMenu *ctx_menu_;
double start_rel_time_;
QCPItemStraightLine *cur_play_pos_;
// const QString streamKey(const struct _rtp_stream_info *rtp_stream);
// const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo);
// Tap callbacks
// static void tapReset(void *tapinfo_ptr);
static gboolean tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr);
static void tapDraw(void *tapinfo_ptr);
void addPacket(packet_info *pinfo, const struct _rtp_info *rtpinfo);
void zoomXAxis(bool in);
void panXAxis(int x_pixels);
double getLowestTimestamp();
const QString getHoveredTime();
int getHoveredPacket();
#else // QT_MULTIMEDIA_LIB
private:
Ui::RtpPlayerDialog *ui;
#endif // QT_MULTIMEDIA_LIB
};
#endif // RTP_PLAYER_DIALOG_H
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

429
ui/qt/rtp_player_dialog.ui Normal file
View File

@ -0,0 +1,429 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RtpPlayerDialog</class>
<widget class="QDialog" name="RtpPlayerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>708</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>RTP Player</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QCustomPlot" name="audioPlot" native="true"/>
<widget class="QTreeWidget" name="streamTreeWidget">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Source Address</string>
</property>
</column>
<column>
<property name="text">
<string>Source Port</string>
</property>
</column>
<column>
<property name="text">
<string>Destination Address</string>
</property>
</column>
<column>
<property name="text">
<string>Destination Port</string>
</property>
</column>
<column>
<property name="text">
<string>SSRC</string>
</property>
</column>
<column>
<property name="text">
<string>First Packet</string>
</property>
</column>
<column>
<property name="text">
<string>Packets</string>
</property>
</column>
<column>
<property name="text">
<string>Time Span (s)</string>
</property>
</column>
<column>
<property name="text">
<string>Sample Rate (Hz)</string>
</property>
</column>
<column>
<property name="text">
<string>Payloads</string>
</property>
</column>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="hintLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
&lt;h3&gt;Valuable and amazing time-saving keyboard shortcuts&lt;/h3&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;+&lt;/th&gt;&lt;td&gt;Zoom in&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;-&lt;/th&gt;&lt;td&gt;Zoom out&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;→&lt;/th&gt;&lt;td&gt;Move right 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;←&lt;/th&gt;&lt;td&gt;Move left 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;→&lt;/th&gt;&lt;td&gt;Move right 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;←&lt;/th&gt;&lt;td&gt;Move left 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>&lt;small&gt;&lt;i&gt;No audio&lt;/i&gt;&lt;/small&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0,0,0,1,0">
<item>
<widget class="QToolButton" name="playButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="stopButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Jitter Buffer:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="toolTip">
<string>The simulated jitter buffer in milliseconds.</string>
</property>
<property name="maximum">
<double>500.000000000000000</double>
</property>
<property name="singleStep">
<double>5.000000000000000</double>
</property>
<property name="value">
<double>50.000000000000000</double>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Playback Timing:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="toolTip">
<string>&lt;strong&gt;Jitter Buffer&lt;/strong&gt;: Use jitter buffer to simulate the RTP stream as heard by the end user.
&lt;br/&gt;
&lt;strong&gt;RTP Timestamp&lt;/strong&gt;: 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.
&lt;br/&gt;
&lt;strong&gt;Uniterrupted Mode&lt;/strong&gt;: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing.</string>
</property>
<item>
<property name="text">
<string>Jitter Buffer</string>
</property>
</item>
<item>
<property name="text">
<string>RTP Timestamp</string>
</property>
</item>
<item>
<property name="text">
<string>Uninterrupted Mode</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="todCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Time of Day</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
</layout>
<action name="actionReset">
<property name="text">
<string>Reset Graph</string>
</property>
<property name="toolTip">
<string>Reset the graph to its initial state.</string>
</property>
<property name="shortcut">
<string>0</string>
</property>
</action>
<action name="actionZoomIn">
<property name="text">
<string>Zoom In</string>
</property>
<property name="toolTip">
<string>Zoom In</string>
</property>
<property name="shortcut">
<string>+</string>
</property>
</action>
<action name="actionZoomOut">
<property name="text">
<string>Zoom Out</string>
</property>
<property name="toolTip">
<string>Zoom Out</string>
</property>
<property name="shortcut">
<string>-</string>
</property>
</action>
<action name="actionMoveLeft10">
<property name="text">
<string>Move Left 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move Left 10 Pixels</string>
</property>
<property name="shortcut">
<string>Left</string>
</property>
</action>
<action name="actionMoveRight10">
<property name="text">
<string>Move Right 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move Right 10 Pixels</string>
</property>
<property name="shortcut">
<string>Right</string>
</property>
</action>
<action name="actionMoveLeft1">
<property name="text">
<string>Move Left 1 Pixels</string>
</property>
<property name="toolTip">
<string>Move Left 1 Pixels</string>
</property>
<property name="shortcut">
<string>Shift+Left</string>
</property>
</action>
<action name="actionMoveRight1">
<property name="text">
<string>Move Right 1 Pixels</string>
</property>
<property name="toolTip">
<string>Move Right 1 Pixels</string>
</property>
<property name="shortcut">
<string>Shift+Right</string>
</property>
</action>
<action name="actionGoToPacket">
<property name="text">
<string>Go To Packet Under Cursor</string>
</property>
<property name="toolTip">
<string>Go to packet currently under the cursor</string>
</property>
<property name="shortcut">
<string>G</string>
</property>
</action>
<action name="actionDragZoom">
<property name="text">
<string>Drag / Zoom</string>
</property>
<property name="toolTip">
<string>Toggle mouse drag / zoom behavior</string>
</property>
<property name="shortcut">
<string>Z</string>
</property>
</action>
<action name="actionToggleTimeOrigin">
<property name="text">
<string>Capture / Session Time Origin</string>
</property>
<property name="toolTip">
<string>Toggle capture / session time origin</string>
</property>
<property name="shortcut">
<string>T</string>
</property>
</action>
<action name="actionCrosshairs">
<property name="text">
<string>Crosshairs</string>
</property>
<property name="toolTip">
<string>Toggle crosshairs</string>
</property>
<property name="shortcut">
<string>Space</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header>qcustomplot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RtpPlayerDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RtpPlayerDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -27,9 +27,11 @@
#include "epan/addr_resolv.h"
#include "epan/dissectors/packet-h225.h"
#include "ui/rtp_stream.h"
#include "ui/utf8_entities.h"
#include "qt_ui_utils.h"
#include "rtp_player_dialog.h"
#include "sequence_dialog.h"
#include "stock_icon.h"
#include "wireshark_application.h"
@ -62,37 +64,47 @@ const int comments_col_ = 8;
Q_DECLARE_METATYPE(voip_calls_info_t*)
enum { voip_calls_type_ = 1000 };
class VoipCallsTreeWidgetItem : public QTreeWidgetItem
{
public:
VoipCallsTreeWidgetItem(QTreeWidget *tree, voip_calls_info_t *call_info) : QTreeWidgetItem(tree) {
setData(0, Qt::UserRole, qVariantFromValue(call_info));
VoipCallsTreeWidgetItem(QTreeWidget *tree, voip_calls_info_t *call_info) :
QTreeWidgetItem(tree, voip_calls_type_),
call_info_(call_info)
{
drawData();
}
voip_calls_info_t *callInfo() {
// XXX Not needed? We explicitly pass selected conversations to RtpPlayerDialog.
// call_info_->selected = isSelected() ? TRUE : FALSE;
return call_info_;
}
void drawData() {
voip_calls_info_t *call_info = data(0, Qt::UserRole).value<voip_calls_info_t*>();
if (!call_info) {
if (!call_info_) {
setText(start_time_col_, QObject::tr("Error"));
return;
}
// XXX Pull digit count from capture file precision
setText(start_time_col_, QString::number(nstime_to_sec(&(call_info->start_rel_ts)), 'f', 6));
setText(stop_time_col_, QString::number(nstime_to_sec(&(call_info->stop_rel_ts)), 'f', 6));
setText(initial_speaker_col_, address_to_display_qstring(&(call_info->initial_speaker)));
setText(from_col_, call_info->from_identity);
setText(to_col_, call_info->to_identity);
setText(protocol_col_, ((call_info->protocol == VOIP_COMMON) && call_info->protocol_name) ?
call_info->protocol_name : voip_protocol_name[call_info->protocol]);
setText(packets_col_, QString::number(call_info->npackets));
setText(state_col_, voip_call_state_name[call_info->call_state]);
setText(start_time_col_, QString::number(nstime_to_sec(&(call_info_->start_rel_ts)), 'f', 6));
setText(stop_time_col_, QString::number(nstime_to_sec(&(call_info_->stop_rel_ts)), 'f', 6));
setText(initial_speaker_col_, address_to_display_qstring(&(call_info_->initial_speaker)));
setText(from_col_, call_info_->from_identity);
setText(to_col_, call_info_->to_identity);
setText(protocol_col_, ((call_info_->protocol == VOIP_COMMON) && call_info_->protocol_name) ?
call_info_->protocol_name : voip_protocol_name[call_info_->protocol]);
setText(packets_col_, QString::number(call_info_->npackets));
setText(state_col_, voip_call_state_name[call_info_->call_state]);
/* Add comments based on the protocol */
QString call_comments;
switch (call_info->protocol) {
switch (call_info_->protocol) {
case VOIP_ISUP:
{
isup_calls_info_t *isup_info = (isup_calls_info_t *)call_info->prot_info;
isup_calls_info_t *isup_info = (isup_calls_info_t *)call_info_->prot_info;
call_comments = QString("%1-%2 %3 %4-%5")
.arg(isup_info->ni)
.arg(isup_info->opc)
@ -103,11 +115,11 @@ public:
break;
case VOIP_H323:
{
h323_calls_info_t *h323_info = (h323_calls_info_t *)call_info->prot_info;
h323_calls_info_t *h323_info = (h323_calls_info_t *)call_info_->prot_info;
gboolean flag = FALSE;
static const QString on_str = QObject::tr("On");
static const QString off_str = QObject::tr("Off");
if (call_info->call_state == VOIP_CALL_SETUP) {
if (call_info_->call_state == VOIP_CALL_SETUP) {
flag = h323_info->is_faststart_Setup;
} else {
if ((h323_info->is_faststart_Setup) && (h323_info->is_faststart_Proc)) {
@ -121,7 +133,7 @@ public:
break;
case VOIP_COMMON:
default:
call_comments = call_info->call_comment;
call_comments = call_info_->call_comment;
break;
}
setText(comments_col_, call_comments);
@ -129,24 +141,25 @@ public:
bool operator< (const QTreeWidgetItem &other) const
{
voip_calls_info_t *this_call_info = data(0, Qt::UserRole).value<voip_calls_info_t*>();
voip_calls_info_t *other_call_info = other.data(0, Qt::UserRole).value<voip_calls_info_t*>();
if (!this_call_info || !other_call_info) {
return false;
if (other.type() != voip_calls_type_) return QTreeWidgetItem::operator< (other);
const VoipCallsTreeWidgetItem *other_row = static_cast<const VoipCallsTreeWidgetItem *>(&other);
if (!call_info_ || !other_row->call_info_) {
return QTreeWidgetItem::operator< (other);
}
switch (treeWidget()->sortColumn()) {
case start_time_col_:
return nstime_cmp(&(this_call_info->start_rel_ts), &(other_call_info->start_rel_ts)) < 0;
return nstime_cmp(&(call_info_->start_rel_ts), &(other_row->call_info_->start_rel_ts)) < 0;
break;
case stop_time_col_:
return nstime_cmp(&(this_call_info->stop_rel_ts), &(other_call_info->stop_rel_ts)) < 0;
return nstime_cmp(&(call_info_->stop_rel_ts), &(other_row->call_info_->stop_rel_ts)) < 0;
break;
case initial_speaker_col_:
return cmp_address(&(this_call_info->initial_speaker), &(other_call_info->initial_speaker)) < 0;
return cmp_address(&(call_info_->initial_speaker), &(other_row->call_info_->initial_speaker)) < 0;
break;
case packets_col_:
return this_call_info->npackets < other_call_info->npackets;
return call_info_->npackets < other_row->call_info_->npackets;
break;
default:
break;
@ -155,7 +168,8 @@ public:
// Fall back to string comparison
return QTreeWidgetItem::operator <(other);
}
private:
voip_calls_info_t *call_info_;
};
VoipCallsDialog::VoipCallsDialog(QWidget &parent, CaptureFile &cf, bool all_flows) :
@ -240,7 +254,7 @@ void VoipCallsDialog::changeEvent(QEvent *event)
gboolean VoipCallsDialog::tapPacket(void *, packet_info *, epan_dissect_t *, const void *)
{
#ifdef QT_MULTIMEDIAWIDGETS_LIB
#ifdef QT_MULTIMEDIA_LIB
// voip_calls_tapinfo_t *tapinfo = (voip_calls_tapinfo_t *) tapinfo_ptr;
// add_rtp_packet for voip player.
// return TRUE;
@ -256,6 +270,19 @@ void VoipCallsDialog::tapDraw(void *tapinfo_ptr)
return;
}
GList *graph_item = g_queue_peek_nth_link(tapinfo->graph_analysis->items, 0);
for (; graph_item; graph_item = g_list_next(graph_item)) {
for (GList *rsi_entry = g_list_first(tapinfo->rtp_stream_list); rsi_entry; rsi_entry = g_list_next(rsi_entry)) {
seq_analysis_item_t * sai = (seq_analysis_item_t *)graph_item->data;
rtp_stream_info_t *rsi = (rtp_stream_info_t *)rsi_entry->data;
if (rsi->start_fd->num == sai->fd->num) {
rsi->call_num = sai->conv_num;
// VOIP_CALLS_DEBUG("setting conv num %u for frame %u", sai->conv_num, sai->fd->num);
}
}
}
VoipCallsDialog *voip_calls_dialog = static_cast<VoipCallsDialog *>(tapinfo->tap_data);
if (voip_calls_dialog) {
voip_calls_dialog->updateCalls();
@ -306,7 +333,7 @@ void VoipCallsDialog::updateWidgets()
}
prepare_button_->setEnabled(selected && have_ga_items);
sequence_button_->setEnabled(selected && have_ga_items);
#if defined(QT_MULTIMEDIAWIDGETS_LIB) && 0 // We don't have a playback dialog yet.
#if defined(QT_MULTIMEDIA_LIB)
player_button_->setEnabled(selected && have_ga_items);
#else
player_button_->setEnabled(false);
@ -456,6 +483,35 @@ void VoipCallsDialog::showSequence()
sequence_dialog->show();
}
void VoipCallsDialog::showPlayer()
{
#ifdef QT_MULTIMEDIA_LIB
RtpPlayerDialog rtp_player_dialog(*this, cap_file_);
foreach (QTreeWidgetItem *ti, ui->callTreeWidget->selectedItems()) {
VoipCallsTreeWidgetItem *vc_ti = static_cast<VoipCallsTreeWidgetItem *>(ti);
for (GList *rsi_entry = g_list_first(tapinfo_.rtp_stream_list); rsi_entry; rsi_entry = g_list_next(rsi_entry)) {
rtp_stream_info_t *rsi = (rtp_stream_info_t *)rsi_entry->data;
if (!rsi) continue;
//VOIP_CALLS_DEBUG("checking call %u, start frame %u == stream call %u, start frame %u, setup frame %u",
// vc_ti->callInfo()->call_num, vc_ti->callInfo()->start_fd->num,
// rsi->call_num, rsi->start_fd->num, rsi->setup_frame_number);
if (vc_ti->callInfo()->call_num == rsi->call_num) {
//VOIP_CALLS_DEBUG("adding call number %u", vc_ti->callInfo()->call_num);
rtp_player_dialog.addRtpStream(rsi);
}
}
}
connect(&rtp_player_dialog, SIGNAL(goToPacket(int)), this, SIGNAL(goToPacket(int)));
// XXX This retaps the packet list. We still have our own tap listener
// registered at this point.
rtp_player_dialog.exec();
#endif // QT_MULTIMEDIA_LIB
}
void VoipCallsDialog::on_callTreeWidget_itemActivated(QTreeWidgetItem *item, int)
{
voip_calls_info_t *call_info = item->data(0, Qt::UserRole).value<voip_calls_info_t*>();
@ -481,6 +537,8 @@ void VoipCallsDialog::on_buttonBox_clicked(QAbstractButton *button)
prepareFilter();
} else if (button == sequence_button_) {
showSequence();
} else if (button == player_button_) {
showPlayer();
}
}

View File

@ -82,6 +82,7 @@ private:
void updateWidgets();
void prepareFilter();
void showSequence();
void showPlayer();
private slots:
void captureFileClosing();

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>VoIP Calls</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View File

@ -43,6 +43,8 @@ extern "C" {
/****************************************************************************/
typedef gint16 SAMPLE;
#define SAMPLE_MAX G_MAXINT16
#define SAMPLE_MIN G_MININT16
/* Defines an RTP packet */
typedef struct _rtp_packet {

View File

@ -56,6 +56,7 @@ static void rtpstream_draw(void *ti_ptr)
g_signal_emit_by_name(top_level, "signal_rtpstream_update");
*/
if (tapinfo && tapinfo->tap_draw) {
/* RTP_STREAM_DEBUG("streams: %d packets: %d", tapinfo->nstreams, tapinfo->npackets); */
tapinfo->tap_draw(tapinfo);
}
return;
@ -76,6 +77,7 @@ void rtpstream_scan(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, const
if (!tapinfo->is_registered)
register_tap_listener_rtp_stream(tapinfo, fstring);
/* RTP_STREAM_DEBUG("scanning %s, filter: %s", cap_file->filename, fstring); */
tapinfo->mode = TAP_ANALYSE;
cf_retap_packets(cap_file);

View File

@ -60,7 +60,7 @@ typedef struct _rtp_stream_info {
gboolean end_stream; /**< Used to track streams across payload types */
int rtp_event;
guint16 call_num; /**< Used to match call_num in voip_calls_info_t */
int call_num; /**< Used to match call_num in voip_calls_info_t */
guint32 setup_frame_number; /**< frame number of setup message */
/* Start and stop packets needed for .num and .abs_ts */
frame_data *start_fd;
@ -71,14 +71,13 @@ typedef struct _rtp_stream_info {
gboolean tag_vlan_error;
gboolean tag_diffserv_error;
gboolean decode; /**< Decode this stream */
GList *rtp_packet_list; /**< List of RTP rtp_packet_t */
gboolean decode; /**< Decode this stream. GTK+ only? */
GList *rtp_packet_list; /**< List of RTP rtp_packet_t. GTK+ only */
tap_rtp_stat_t rtp_stats; /**< here goes the RTP statistics info */
gboolean problem; /**< if the streams had wrong sequence numbers or wrong timerstamps */
gboolean problem; /**< if the streams had wrong sequence numbers or wrong timestamps */
} rtp_stream_info_t;
/** tapping modes */
typedef enum
{
@ -111,6 +110,16 @@ struct _rtpstream_tapinfo {
gboolean is_registered; /**< if the tap listener is currently registered or not */
};
#if 0
#define RTP_STREAM_DEBUG(...) { \
char *RTP_STREAM_DEBUG_MSG = g_strdup_printf(__VA_ARGS__); \
g_warning("rtp_stream: %s:%d %s", G_STRFUNC, __LINE__, RTP_STREAM_DEBUG_MSG); \
g_free(RTP_STREAM_DEBUG_MSG); \
}
#else
#define RTP_STREAM_DEBUG()
#endif
/****************************************************************************/
/* INTERFACE */

View File

@ -52,6 +52,8 @@ seq_analysis_info_t *
sequence_analysis_info_new(void)
{
seq_analysis_info_t *sainfo = g_new0(seq_analysis_info_t, 1);
/* SEQ_ANALYSIS_DEBUG("adding new item"); */
sainfo->items = g_queue_new();
sainfo->ht= g_hash_table_new(g_int_hash, g_int_equal);
return sainfo;
@ -61,6 +63,7 @@ void sequence_analysis_info_free(seq_analysis_info_t *sainfo)
{
if (!sainfo) return;
/* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
sequence_analysis_list_free(sainfo);
g_queue_free(sainfo->items);
@ -282,6 +285,7 @@ sequence_analysis_list_get(capture_file *cf, seq_analysis_info_t *sainfo)
cf_retap_packets(cf);
remove_tap_listener(sainfo);
/* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
/* Fill in the timestamps */
g_queue_foreach(sainfo->items, sequence_analysis_item_set_timestamp, cf->epan);
}
@ -329,6 +333,7 @@ sequence_analysis_list_free(seq_analysis_info_t *sainfo)
int i;
if (!sainfo) return;
/* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
/* free the graph data items */

View File

@ -72,12 +72,27 @@ typedef struct _seq_analysis_info {
gboolean all_packets; /**< all packets vs only displayed */
gboolean any_addr; /**< any addr (DL+net) vs net-only */
int nconv; /**< number of conversations in the list */
GQueue* items; /**< list with the graph analysis items */
GHashTable *ht; /**< hash table for retrieving graph analysis items */
GQueue* items; /**< list of seq_analysis_info_t */
GHashTable *ht; /**< hash table of seq_analysis_info_t */
address nodes[MAX_NUM_NODES]; /**< horizontal node list */
guint32 num_nodes; /**< actual number of nodes */
} seq_analysis_info_t;
#if 0
#ifdef _MSC_VER
#define _ws_func_ __FUNCTION__
#else
#define _ws_func_ __func__
#endif
#define SEQ_ANALYSIS_DEBUG(...) { \
char *SEQ_ANALYSIS_DEBUG_MSG = g_strdup_printf(__VA_ARGS__); \
g_warning("sequence analysis: %s:%d %s", _ws_func_, __LINE__, SEQ_ANALYSIS_DEBUG_MSG); \
g_free(SEQ_ANALYSIS_DEBUG_MSG); \
}
#else
#define SEQ_ANALYSIS_DEBUG()
#endif
/** Create and initialize a seq_analysis_info_t struct
* @return A pointer to a newly allocated seq_analysis_info_t struct.
*/

View File

@ -247,6 +247,8 @@ voip_calls_reset_all_taps(voip_calls_tapinfo_t *tapinfo)
rtp_stream_info_t *strinfo;
GList *list = NULL;
/* VOIP_CALLS_DEBUG("reset packets: %d streams: %d", tapinfo->npackets, tapinfo->nrtp_streams); */
/* free the data items first */
list = g_queue_peek_nth_link(tapinfo->callsinfos, 0);
while (list)
@ -610,15 +612,13 @@ rtp_packet(void *tap_offset_ptr, packet_info *pinfo, epan_dissect_t *edt, void c
/* not in the list? then create a new entry */
if (strinfo==NULL) {
strinfo = (rtp_stream_info_t *)g_malloc(sizeof(rtp_stream_info_t));
strinfo = (rtp_stream_info_t *)g_malloc0(sizeof(rtp_stream_info_t));
COPY_ADDRESS(&(strinfo->src_addr), &(pinfo->src));
strinfo->src_port = pinfo->srcport;
COPY_ADDRESS(&(strinfo->dest_addr), &(pinfo->dst));
strinfo->dest_port = pinfo->destport;
strinfo->ssrc = rtp_info->info_sync_src;
strinfo->end_stream = FALSE;
strinfo->payload_type = rtp_info->info_payload_type;
strinfo->payload_type_name = NULL;
strinfo->is_srtp = rtp_info->info_is_srtp;
/* if it is dynamic payload, let use the conv data to see if it is defined */
if ( (strinfo->payload_type >= PT_UNDF_96) && (strinfo->payload_type <= PT_UNDF_127) ) {
@ -632,10 +632,10 @@ rtp_packet(void *tap_offset_ptr, packet_info *pinfo, epan_dissect_t *edt, void c
}
}
if (!strinfo->payload_type_name) strinfo->payload_type_name = (gchar*)val_to_str_ext_wmem(NULL, strinfo->payload_type, &rtp_payload_type_short_vals_ext, "%u");
strinfo->packet_count = 0;
strinfo->start_fd = pinfo->fd;
strinfo->start_rel_time = pinfo->rel_ts;
strinfo->setup_frame_number = rtp_info->info_setup_frame_num;
strinfo->call_num = -1;
strinfo->rtp_event = -1;
tapinfo->rtp_stream_list = g_list_prepend(tapinfo->rtp_stream_list, strinfo);
}
@ -3899,6 +3899,7 @@ remove_tap_listener_iax2_calls(voip_calls_tapinfo_t *tap_id_base)
/* ***************************TAP for OTHER PROTOCOL **********************************/
/****************************************************************************/
/* voip_calls_packet and voip_calls_init_tap appear to be dead code. We don't have a "voip" tap. */
static gboolean
voip_calls_packet(void *tap_offset_ptr, packet_info *pinfo, epan_dissect_t *edt, const void *VoIPinfo)
{
@ -3908,6 +3909,7 @@ voip_calls_packet(void *tap_offset_ptr, packet_info *pinfo, epan_dissect_t *edt,
GList *list = NULL;
const voip_packet_info_t *pi = (const voip_packet_info_t *)VoIPinfo;
/* VOIP_CALLS_DEBUG("num %u", pinfo->fd->num); */
if (pi->call_id)
list = g_queue_peek_nth_link(tapinfo->callsinfos, 0);
while (list) {
@ -3965,6 +3967,7 @@ voip_calls_packet(void *tap_offset_ptr, packet_info *pinfo, epan_dissect_t *edt,
return TRUE;
}
/****************************************************************************/
void

View File

@ -189,8 +189,8 @@ typedef struct _voip_calls_tapinfo {
tap_draw_cb tap_draw; /**< tap draw callback */
void *tap_data; /**< data for tap callbacks */
int ncalls; /**< number of call */
GQueue* callsinfos; /**< queue with all calls */
GHashTable* callsinfo_hashtable[1]; /**< array of hashes per voip protocol; currently only the one for SIP is used */
GQueue* callsinfos; /**< queue with all calls (voip_calls_info_t) */
GHashTable* callsinfo_hashtable[1]; /**< array of hashes per voip protocol (voip_calls_info_t); currently only the one for SIP is used */
int npackets; /**< total number of packets of all calls */
voip_calls_info_t *filter_calls_fwd; /**< used as filter in some tap modes */
int start_packets;
@ -227,6 +227,21 @@ typedef struct _voip_calls_tapinfo {
gboolean redraw;
} voip_calls_tapinfo_t;
#if 0
#ifdef _MSC_VER
#define _ws_func_ __FUNCTION__
#else
#define _ws_func_ __func__
#endif
#define VOIP_CALLS_DEBUG(...) { \
char *VOIP_CALLS_DEBUG_MSG = g_strdup_printf(__VA_ARGS__); \
g_warning("voip_calls: %s:%d %s", _ws_func_, __LINE__, VOIP_CALLS_DEBUG_MSG); \
g_free(VOIP_CALLS_DEBUG_MSG); \
}
#else
#define VOIP_CALLS_DEBUG()
#endif
/****************************************************************************/
/* INTERFACE */
@ -268,4 +283,3 @@ void voip_calls_reset_all_taps(voip_calls_tapinfo_t *tapinfo);
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -25,5 +25,5 @@ set -e
apt-get update
apt-get build-dep -y wireshark
apt-get install -y cmake valgrind qt5-default \
libqt5multimedia5 libqt5multimediawidgets5 qtmultimedia5-dev \
libqt5multimedia5 qtmultimedia5-dev \
qttools5-dev qttools5-dev-tools