diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f5e591547..c704c7e015 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) diff --git a/Makefile.nmake b/Makefile.nmake index e2077a2c29..86fcf8f8e7 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -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 $@ diff --git a/acinclude.m4 b/acinclude.m4 index 704d92408e..0dceca3654 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -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 diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index e90f512449..ee8839692b 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -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 diff --git a/codecs/CMakeLists.txt b/codecs/CMakeLists.txt index 6397db18c0..63ac64d08d 100644 --- a/codecs/CMakeLists.txt +++ b/codecs/CMakeLists.txt @@ -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) diff --git a/codecs/Makefile.common b/codecs/Makefile.common index fc76046c34..d076d3b412 100644 --- a/codecs/Makefile.common +++ b/codecs/Makefile.common @@ -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 diff --git a/codecs/Makefile.nmake b/codecs/Makefile.nmake index 1a8dc6e5cb..53aa69bda3 100644 --- a/codecs/Makefile.nmake +++ b/codecs/Makefile.nmake @@ -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 diff --git a/codecs/speex/README.txt b/codecs/speex/README.txt new file mode 100644 index 0000000000..ee8cd66e13 --- /dev/null +++ b/codecs/speex/README.txt @@ -0,0 +1,2 @@ +Copied from http://git.xiph.org/speexdsp.git c470e2e89a6ca75b507437467692cd684b71a526 +and modified to compile in the Wireshark source tree. diff --git a/codecs/speex/arch.h b/codecs/speex/arch.h new file mode 100644 index 0000000000..c2de991dd9 --- /dev/null +++ b/codecs/speex/arch.h @@ -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 diff --git a/codecs/speex/resample.c b/codecs/speex/resample.c new file mode 100644 index 0000000000..c5f663432b --- /dev/null +++ b/codecs/speex/resample.c @@ -0,0 +1,1203 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + Copyright (C) 2008 Thorvald Natvig + + File: resample.c + Arbitrary resampling code + + 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. +*/ + +/* + The design goals of this code are: + - Very fast algorithm + - SIMD-friendly algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Warning: This resampler is relatively new. Although I think I got rid of + all the major bugs and I don't expect the API to change anymore, there + may be something I've missed. So use with caution. + + This algorithm is based on this original resampling algorithm: + Smith, Julius O. Digital Audio Resampling Home Page + Center for Computer Research in Music and Acoustics (CCRMA), + Stanford University, 2007. + Web published at http://www-ccrma.stanford.edu/~jos/resample/. + + There is one main difference, though. This resampler uses cubic + interpolation instead of linear interpolation in the above paper. This + makes the table much smaller and makes it possible to compute that table + on a per-stream basis. In turn, being able to tweak the table for each + stream makes it possible to both reduce complexity on simple ratios + (e.g. 2/3), and get rid of the rounding operations in the inner loop. + The latter both reduces CPU time and makes the algorithm more SIMD-friendly. +*/ + +#include "config.h" + +#define OUTSIDE_SPEEX 1 +#define FLOATING_POINT 1 + +#ifdef OUTSIDE_SPEEX +#include +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} +#include "speex_resampler.h" +#include "arch.h" +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_resampler.h" +#include "arch.h" +#include "os_support.h" +#endif /* OUTSIDE_SPEEX */ + +#include "stack_alloc.h" +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef _USE_SSE +#include "resample_sse.h" +#endif + +#ifdef _USE_NEON +#include "resample_neon.h" +#endif + +/* Numer of elements to allocate on the stack */ +#ifdef VAR_ARRAYS +#define FIXED_STACK_ALLOC 8192 +#else +#define FIXED_STACK_ALLOC 1024 +#endif + +typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); + +struct SpeexResamplerState_ { + spx_uint32_t in_rate; + spx_uint32_t out_rate; + spx_uint32_t num_rate; + spx_uint32_t den_rate; + + int quality; + spx_uint32_t nb_channels; + spx_uint32_t filt_len; + spx_uint32_t mem_alloc_size; + spx_uint32_t buffer_size; + int int_advance; + int frac_advance; + float cutoff; + spx_uint32_t oversample; + int initialised; + int started; + + /* These are per-channel */ + spx_int32_t *last_sample; + spx_uint32_t *samp_frac_num; + spx_uint32_t *magic_samples; + + spx_word16_t *mem; + spx_word16_t *sinc_table; + spx_uint32_t sinc_table_length; + resampler_basic_func resampler_ptr; + + int in_stride; + int out_stride; +} ; + +static const double kaiser12_table[68] = { + 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, + 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, + 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, + 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, + 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, + 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, + 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, + 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, + 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, + 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, + 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, + 0.00001000, 0.00000000}; +/* +static const double kaiser12_table[36] = { + 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, + 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, + 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, + 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, + 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, + 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +*/ +static const double kaiser10_table[36] = { + 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, + 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, + 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, + 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, + 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, + 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + +static const double kaiser8_table[36] = { + 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, + 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, + 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, + 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, + 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, + 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + +static const double kaiser6_table[36] = { + 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, + 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, + 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, + 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, + 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, + 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + +struct FuncDef { + const double *table; + int oversample; +}; + +static const struct FuncDef _KAISER12 = {kaiser12_table, 64}; +#define KAISER12 (&_KAISER12) +/*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; +#define KAISER12 (&_KAISER12)*/ +static const struct FuncDef _KAISER10 = {kaiser10_table, 32}; +#define KAISER10 (&_KAISER10) +static const struct FuncDef _KAISER8 = {kaiser8_table, 32}; +#define KAISER8 (&_KAISER8) +static const struct FuncDef _KAISER6 = {kaiser6_table, 32}; +#define KAISER6 (&_KAISER6) + +struct QualityMapping { + int base_length; + int oversample; + float downsample_bandwidth; + float upsample_bandwidth; + const struct FuncDef *window_func; +}; + + +/* This table maps conversion quality to internal parameters. There are two + reasons that explain why the up-sampling bandwidth is larger than the + down-sampling bandwidth: + 1) When up-sampling, we can assume that the spectrum is already attenuated + close to the Nyquist rate (from an A/D or a previous resampling filter) + 2) Any aliasing that occurs very close to the Nyquist rate will be masked + by the sinusoids/noise just below the Nyquist rate (guaranteed only for + up-sampling). +*/ +static const struct QualityMapping quality_map[11] = { + { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ + { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ + { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ + { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ + { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ + { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ + {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ + {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +}; +/*8,24,40,56,80,104,128,160,200,256,320*/ +static double compute_func(float x, const struct FuncDef *func) +{ + float y, frac; + double interp[4]; + int ind; + y = x*func->oversample; + ind = (int)floor(y); + frac = (y-ind); + /* CSE with handle the repeated powers */ + interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac); + interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); + /* Just to make sure we don't have rounding problems */ + interp[1] = 1.f-interp[3]-interp[2]-interp[0]; + + /*sum = frac*accum[1] + (1-frac)*accum[2];*/ + return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; +} + +#if 0 +#include +int main(int argc, char **argv) +{ + int i; + for (i=0;i<256;i++) + { + printf ("%f\n", compute_func(i/256., KAISER12)); + } + return 0; +} +#endif + +#ifdef FIXED_POINT +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6f) + return WORD2INT(32768.*cutoff); + else if (fabs(x) > .5f*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); +} +#else +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6) + return cutoff; + else if (fabs(x) > .5*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); +} +#endif + +#ifdef FIXED_POINT +static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + spx_word16_t x2, x3; + x2 = MULT16_16_P15(x, x); + x3 = MULT16_16_P15(x, x2); + interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); + interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); + interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); + /* Just to make sure we don't have rounding problems */ + interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; + if (interp[2]<32767) + interp[2]+=1; +} +#else +static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac; + interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; + /* Just to make sure we don't have rounding problems */ + interp[2] = 1.-interp[0]-interp[1]-interp[3]; +} +#endif + +static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinct = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_SINGLE + int j; + sum = 0; + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + double sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinct = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE + int j; + double accum[4] = {0,0,0,0}; + + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE + int j; + spx_word32_t accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1)); + sum = SATURATE32PSHR(sum, 15, 32767); +#else + cubic_coef(frac, interp); + sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = sum; + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE + int j; + double accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +/* This resampler is used to produce zero output in situations where memory + for the filter could not be allocated. The expected numbers of input and + output samples are still processed so that callers failing to check error + codes are not surprised, possibly getting into infinite loops. */ +static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in _U_, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + out[out_stride * out_sample++] = 0; + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +static int update_filter(SpeexResamplerState *st) +{ + spx_uint32_t old_length = st->filt_len; + spx_uint32_t old_alloc_size = st->mem_alloc_size; + int use_direct; + spx_uint32_t min_sinc_table_length; + spx_uint32_t min_alloc_size; + + st->int_advance = st->num_rate/st->den_rate; + st->frac_advance = st->num_rate%st->den_rate; + st->oversample = quality_map[st->quality].oversample; + st->filt_len = quality_map[st->quality].base_length; + + if (st->num_rate > st->den_rate) + { + /* down-sampling */ + st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; + /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ + st->filt_len = st->filt_len*st->num_rate / st->den_rate; + /* Round up to make sure we have a multiple of 8 for SSE */ + st->filt_len = ((st->filt_len-1)&(~0x7))+8; + if (2*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (4*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (8*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (16*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (st->oversample < 1) + st->oversample = 1; + } else { + /* up-sampling */ + st->cutoff = quality_map[st->quality].upsample_bandwidth; + } + + /* Choose the resampling type that requires the least amount of memory */ +#ifdef RESAMPLE_FULL_SINC_TABLE + use_direct = 1; + if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len) + goto fail; +#else + use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8 + && INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len; +#endif + if (use_direct) + { + min_sinc_table_length = st->filt_len*st->den_rate; + } else { + if ((INT_MAX/sizeof(spx_word16_t)-8)/st->oversample < st->filt_len) + goto fail; + + min_sinc_table_length = st->filt_len*st->oversample+8; + } + if (st->sinc_table_length < min_sinc_table_length) + { + spx_word16_t *sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,min_sinc_table_length*sizeof(spx_word16_t)); + if (!sinc_table) + goto fail; + + st->sinc_table = sinc_table; + st->sinc_table_length = min_sinc_table_length; + } + if (use_direct) + { + spx_uint32_t i; + for (i=0;iden_rate;i++) + { + spx_uint32_t j; + for (j=0;jfilt_len;j++) + { + st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); + } + } +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_direct_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_direct_double; + else + st->resampler_ptr = resampler_basic_direct_single; +#endif + /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ + } else { + spx_int32_t i; + for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) + st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_interpolate_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_interpolate_double; + else + st->resampler_ptr = resampler_basic_interpolate_single; +#endif + /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ + } + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + + /* Adding buffer_size to filt_len won't overflow here because filt_len + could be multiplied by sizeof(spx_word16_t) above. */ + min_alloc_size = st->filt_len-1 + st->buffer_size; + if (min_alloc_size > st->mem_alloc_size) + { + spx_word16_t *mem; + if (INT_MAX/sizeof(spx_word16_t)/st->nb_channels < min_alloc_size) + goto fail; + else if (!(mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*min_alloc_size * sizeof(*mem)))) + goto fail; + + st->mem = mem; + st->mem_alloc_size = min_alloc_size; + } + if (!st->started) + { + spx_uint32_t i; + for (i=0;inb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("reinit filter");*/ + } else if (st->filt_len > old_length) + { + spx_uint32_t i; + /* Increase the filter length */ + /*speex_warning("increase filter size");*/ + for (i=st->nb_channels;i--;) + { + spx_uint32_t j; + spx_uint32_t olen = old_length; + /*if (st->magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2*st->magic_samples[i]; + for (j=old_length-1+st->magic_samples[i];j--;) + st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; + for (j=0;jmagic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = 0; + st->magic_samples[i] = 0; + } + if (st->filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; + /* Then put zeros for lack of anything better */ + for (;jfilt_len-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; + /* Adjust last_sample */ + st->last_sample[i] += (st->filt_len - olen)/2; + } else { + /* Put back some of the magic! */ + st->magic_samples[i] = (olen - st->filt_len)/2; + for (j=0;jfilt_len-1+st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + } + } + } else if (st->filt_len < old_length) + { + spx_uint32_t i; + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ + for (i=0;inb_channels;i++) + { + spx_uint32_t j; + spx_uint32_t old_magic = st->magic_samples[i]; + st->magic_samples[i] = (old_length - st->filt_len)/2; + /* We must copy some of the memory that's no longer used */ + /* Copy data going backward */ + for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + st->magic_samples[i] += old_magic; + } + } + return RESAMPLER_ERR_SUCCESS; + +fail: + st->resampler_ptr = resampler_basic_zero; + /* st->mem may still contain consumed input samples for the filter. + Restore filt_len so that filt_len - 1 still points to the position after + the last of these samples. */ + st->filt_len = old_length; + return RESAMPLER_ERR_ALLOC_FAILED; +} + +EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); +} + +EXPORT 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) +{ + spx_uint32_t i; + SpeexResamplerState *st; + int filter_err; + + if (quality > 10 || quality < 0) + { + if (err) + *err = RESAMPLER_ERR_INVALID_ARG; + return NULL; + } + st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); + st->initialised = 0; + st->started = 0; + st->in_rate = 0; + st->out_rate = 0; + st->num_rate = 0; + st->den_rate = 0; + st->quality = -1; + st->sinc_table_length = 0; + st->mem_alloc_size = 0; + st->filt_len = 0; + st->mem = 0; + st->resampler_ptr = 0; + + st->cutoff = 1.f; + st->nb_channels = nb_channels; + st->in_stride = 1; + st->out_stride = 1; + + st->buffer_size = 160; + + /* Per channel data */ + st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)); + st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); + st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); + for (i=0;ilast_sample[i] = 0; + st->magic_samples[i] = 0; + st->samp_frac_num[i] = 0; + } + + speex_resampler_set_quality(st, quality); + speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); + + filter_err = update_filter(st); + if (filter_err == RESAMPLER_ERR_SUCCESS) + { + st->initialised = 1; + } else { + speex_resampler_destroy(st); + st = NULL; + } + if (err) + *err = filter_err; + + return st; +} + +EXPORT void speex_resampler_destroy(SpeexResamplerState *st) +{ + speex_free(st->mem); + speex_free(st->sinc_table); + speex_free(st->last_sample); + speex_free(st->magic_samples); + speex_free(st->samp_frac_num); + speex_free(st); +} + +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int j=0; + const int N = st->filt_len; + int out_sample = 0; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + spx_uint32_t ilen; + + st->started = 1; + + /* Call the right resampler through the function ptr */ + out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); + + if (st->last_sample[channel_index] < (spx_int32_t)*in_len) + *in_len = st->last_sample[channel_index]; + *out_len = out_sample; + st->last_sample[channel_index] -= *in_len; + + ilen = *in_len; + + for(j=0;jmagic_samples[channel_index]; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + const int N = st->filt_len; + + speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); + + st->magic_samples[channel_index] -= tmp_in_len; + + /* If we couldn't process all "magic" input samples, save the rest for next time */ + if (st->magic_samples[channel_index]) + { + spx_uint32_t i; + for (i=0;imagic_samples[channel_index];i++) + mem[N-1+i]=mem[N-1+i+tmp_in_len]; + } + *out += out_len*st->out_stride; + return out_len; +} + +#ifdef FIXED_POINT +EXPORT 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) +#else +EXPORT 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) +#endif +{ + spx_uint32_t j; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const int filt_offs = st->filt_len - 1; + const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; + const int istride = st->in_stride; + + if (st->magic_samples[channel_index]) + olen -= speex_resampler_magic(st, channel_index, &out, olen); + if (! st->magic_samples[channel_index]) { + while (ilen && olen) { + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = olen; + + if (in) { + for(j=0;jout_stride; + if (in) + in += ichunk * istride; + } + } + *in_len -= ilen; + *out_len -= olen; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +#ifdef FIXED_POINT +EXPORT 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) +#else +EXPORT 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) +#endif +{ + spx_uint32_t j; + const int istride_save = st->in_stride; + const int ostride_save = st->out_stride; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1); +#ifdef VAR_ARRAYS + const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; + VARDECL(spx_word16_t *ystack); + ALLOC(ystack, ylen, spx_word16_t); +#else + const unsigned int ylen = FIXED_STACK_ALLOC; + spx_word16_t ystack[FIXED_STACK_ALLOC]; +#endif + + st->out_stride = 1; + + while (ilen && olen) { + spx_word16_t *y = ystack; + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = (olen > ylen) ? ylen : olen; + spx_uint32_t omagic = 0; + + if (st->magic_samples[channel_index]) { + omagic = speex_resampler_magic(st, channel_index, &y, ochunk); + ochunk -= omagic; + olen -= omagic; + } + if (! st->magic_samples[channel_index]) { + if (in) { + for(j=0;jfilt_len-1]=WORD2INT(in[j*istride_save]); +#else + x[j+st->filt_len-1]=in[j*istride_save]; +#endif + } else { + for(j=0;jfilt_len-1]=0; + } + + speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk); + } else { + ichunk = 0; + ochunk = 0; + } + + for (j=0;jout_stride = ostride_save; + *in_len -= ilen; + *out_len -= olen; + + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_out_len = *out_len; + spx_uint32_t bak_in_len = *in_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_out_len; + *in_len = bak_in_len; + if (in != NULL) + speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT 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) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_out_len = *out_len; + spx_uint32_t bak_in_len = *in_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_out_len; + *in_len = bak_in_len; + if (in != NULL) + speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); +} + +EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) +{ + *in_rate = st->in_rate; + *out_rate = st->out_rate; +} + +EXPORT 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) +{ + spx_uint32_t fact; + spx_uint32_t old_den; + spx_uint32_t i; + if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) + return RESAMPLER_ERR_SUCCESS; + + old_den = st->den_rate; + st->in_rate = in_rate; + st->out_rate = out_rate; + st->num_rate = ratio_num; + st->den_rate = ratio_den; + /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ + for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) + { + while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) + { + st->num_rate /= fact; + st->den_rate /= fact; + } + } + + if (old_den > 0) + { + for (i=0;inb_channels;i++) + { + st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; + /* Safety net */ + if (st->samp_frac_num[i] >= st->den_rate) + st->samp_frac_num[i] = st->den_rate-1; + } + } + + if (st->initialised) + return update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) +{ + *ratio_num = st->num_rate; + *ratio_den = st->den_rate; +} + +EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality) +{ + if (quality > 10 || quality < 0) + return RESAMPLER_ERR_INVALID_ARG; + if (st->quality == quality) + return RESAMPLER_ERR_SUCCESS; + st->quality = quality; + if (st->initialised) + return update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) +{ + *quality = st->quality; +} + +EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->in_stride = stride; +} + +EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->in_stride; +} + +EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->out_stride = stride; +} + +EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->out_stride; +} + +EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st) +{ + return st->filt_len / 2; +} + +EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st) +{ + return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; +} + +EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels;i++) + st->last_sample[i] = st->filt_len/2; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels;i++) + { + st->last_sample[i] = 0; + st->magic_samples[i] = 0; + st->samp_frac_num[i] = 0; + } + for (i=0;inb_channels*(st->filt_len-1);i++) + st->mem[i] = 0; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT const char *speex_resampler_strerror(int err) +{ + switch (err) + { + case RESAMPLER_ERR_SUCCESS: + return "Success."; + case RESAMPLER_ERR_ALLOC_FAILED: + return "Memory allocation failed."; + case RESAMPLER_ERR_BAD_STATE: + return "Bad resampler state."; + case RESAMPLER_ERR_INVALID_ARG: + return "Invalid argument."; + case RESAMPLER_ERR_PTR_OVERLAP: + return "Input and output buffers overlap."; + default: + return "Unknown error. Bad error code or strange version mismatch."; + } +} diff --git a/codecs/speex/speex_resampler.h b/codecs/speex/speex_resampler.h new file mode 100644 index 0000000000..a71cd4fb68 --- /dev/null +++ b/codecs/speex/speex_resampler.h @@ -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 diff --git a/codecs/speex/stack_alloc.h b/codecs/speex/stack_alloc.h new file mode 100644 index 0000000000..6c56334f86 --- /dev/null +++ b/codecs/speex/stack_alloc.h @@ -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 +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# 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 + +#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 diff --git a/configure.ac b/configure.ac index d1de15d3ce..81394ee505 100644 --- a/configure.ac +++ b/configure.ac @@ -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" diff --git a/docbook/wsug_src/WSUG_chapter_telephony.asciidoc b/docbook/wsug_src/WSUG_chapter_telephony.asciidoc index 12e6c9c91c..f84d022f32 100644 --- a/docbook/wsug_src/WSUG_chapter_telephony.asciidoc +++ b/docbook/wsug_src/WSUG_chapter_telephony.asciidoc @@ -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]] diff --git a/epan/dissectors/packet-rtp.h b/epan/dissectors/packet-rtp.h index b1a421fdab..d4f381b15b 100644 --- a/epan/dissectors/packet-rtp.h +++ b/epan/dissectors/packet-rtp.h @@ -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 */ diff --git a/ui/gtk/rtp_analysis.c b/ui/gtk/rtp_analysis.c index beb62afe6f..4cef14e97e 100644 --- a/ui/gtk/rtp_analysis.c +++ b/ui/gtk/rtp_analysis.c @@ -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; diff --git a/ui/gtk/rtp_player.c b/ui/gtk/rtp_player.c index 2774544926..6880d479d7 100644 --- a/ui/gtk/rtp_player.c +++ b/ui/gtk/rtp_player.c @@ -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 */ diff --git a/ui/gtk/voip_calls_dlg.c b/ui/gtk/voip_calls_dlg.c index 0fdcea5af5..082cd0c6dd 100644 --- a/ui/gtk/voip_calls_dlg.c +++ b/ui/gtk/voip_calls_dlg.c @@ -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; } diff --git a/ui/help_url.c b/ui/help_url.c index 42ae987204..7a2b9a026e 100644 --- a/ui/help_url.c +++ b/ui/help_url.c @@ -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(); diff --git a/ui/help_url.h b/ui/help_url.h index 120b762137..41a23e9f75 100644 --- a/ui/help_url.h +++ b/ui/help_url.h @@ -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 diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index efb028110b..d214b4826f 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -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 diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index e4198c630a..13bd0d2db7 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -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 diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index ab8c3c9086..1af30ecbfb 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -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 \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 43f470c013..a056d5b6b9 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -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 \ diff --git a/ui/qt/packet_dialog.cpp b/ui/qt/packet_dialog.cpp index 05f8878903..1ecff0370c 100644 --- a/ui/qt/packet_dialog.cpp +++ b/ui/qt/packet_dialog.cpp @@ -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) { diff --git a/ui/qt/rtp_audio_stream.cpp b/ui/qt/rtp_audio_stream.cpp new file mode 100644 index 0000000000..1b6bb44b80 --- /dev/null +++ b/ui/qt/rtp_audio_stream.cpp @@ -0,0 +1,396 @@ +/* rtp_audio_frame.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +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 RtpAudioStream::visualTimestamps(bool relative) +{ + QVector ts_keys = packet_timestamps_.keys().toVector(); + if (relative) return ts_keys; + + QVector 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 RtpAudioStream::visualSamples(int y_offset) +{ + QVector 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 RtpAudioStream::outOfSequenceTimestamps(bool relative) +{ + if (relative) return out_of_seq_timestamps_; + + QVector 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 RtpAudioStream::outOfSequenceSamples(int y_offset) +{ + QVector 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::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: + */ diff --git a/ui/qt/rtp_audio_stream.h b/ui/qt/rtp_audio_stream.h new file mode 100644 index 0000000000..b2842aad37 --- /dev/null +++ b/ui/qt/rtp_audio_stream.h @@ -0,0 +1,151 @@ +/* rtp_audio_stream.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include + +#include +#include +#include +#include +#include +#include + +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 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 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 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 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_; + QListrtp_streams_; + double global_start_rel_time_; + double start_abs_offset_; + double start_rel_time_; + double stop_rel_time_; + quint32 audio_out_rate_; + QSet payload_names_; + struct SpeexResamplerState_ *audio_resampler_; + struct SpeexResamplerState_ *visual_resampler_; + QAudioOutput *audio_output_; + QMap packet_timestamps_; + QVector visual_samples_; + QVector 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: + */ diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp new file mode 100644 index 0000000000..b930a08d0b --- /dev/null +++ b/ui/qt/rtp_player_dialog.cpp @@ -0,0 +1,656 @@ +/* rtp_player_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include + +#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 +#include +#include +#include + +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(); + 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(); + 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(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(); + 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 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(); + 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 = ""; + + if (packet_num > 0) { + hint += tr("%1. Press \"G\" to to to packet %2") + .arg(getHoveredTime()) + .arg(packet_num); + } + + hint += ""; + 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*)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(); + + 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(); + 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(); + + 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: + */ diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h new file mode 100644 index 0000000000..a52544ab1c --- /dev/null +++ b/ui/qt/rtp_player_dialog.h @@ -0,0 +1,136 @@ +/* rtp_player_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include "ui/rtp_stream.h" + +#include "wireshark_dialog.h" + +#include + +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: + */ diff --git a/ui/qt/rtp_player_dialog.ui b/ui/qt/rtp_player_dialog.ui new file mode 100644 index 0000000000..03d405cce0 --- /dev/null +++ b/ui/qt/rtp_player_dialog.ui @@ -0,0 +1,429 @@ + + + RtpPlayerDialog + + + + 0 + 0 + 708 + 400 + + + + RTP Player + + + + + + Qt::Vertical + + + + + false + + + false + + + + Source Address + + + + + Source Port + + + + + Destination Address + + + + + Destination Port + + + + + SSRC + + + + + First Packet + + + + + Packets + + + + + Time Span (s) + + + + + Sample Rate (Hz) + + + + + Payloads + + + + + + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + <small><i>No audio</i></small> + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 10 + + + + + + + + <html><head/><body><p><br/></p></body></html> + + + Jitter Buffer: + + + + + + + The simulated jitter buffer in milliseconds. + + + 500.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + Playback Timing: + + + + + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: 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. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + + + + Jitter Buffer + + + + + RTP Timestamp + + + + + Uninterrupted Mode + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + + + Time of Day + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Reset Graph + + + Reset the graph to its initial state. + + + 0 + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Left 1 Pixels + + + Move Left 1 Pixels + + + Shift+Left + + + + + Move Right 1 Pixels + + + Move Right 1 Pixels + + + Shift+Right + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + Drag / Zoom + + + Toggle mouse drag / zoom behavior + + + Z + + + + + Capture / Session Time Origin + + + Toggle capture / session time origin + + + T + + + + + Crosshairs + + + Toggle crosshairs + + + Space + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
+ + + + buttonBox + accepted() + RtpPlayerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RtpPlayerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/voip_calls_dialog.cpp b/ui/qt/voip_calls_dialog.cpp index e530993c56..b18fd91537 100644 --- a/ui/qt/voip_calls_dialog.cpp +++ b/ui/qt/voip_calls_dialog.cpp @@ -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(); - 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 *other_call_info = other.data(0, Qt::UserRole).value(); - 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(&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(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(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(); @@ -481,6 +537,8 @@ void VoipCallsDialog::on_buttonBox_clicked(QAbstractButton *button) prepareFilter(); } else if (button == sequence_button_) { showSequence(); + } else if (button == player_button_) { + showPlayer(); } } diff --git a/ui/qt/voip_calls_dialog.h b/ui/qt/voip_calls_dialog.h index 55245f4d74..0b4d567878 100644 --- a/ui/qt/voip_calls_dialog.h +++ b/ui/qt/voip_calls_dialog.h @@ -82,6 +82,7 @@ private: void updateWidgets(); void prepareFilter(); void showSequence(); + void showPlayer(); private slots: void captureFileClosing(); diff --git a/ui/qt/voip_calls_dialog.ui b/ui/qt/voip_calls_dialog.ui index 27ec107427..d4d94ea541 100644 --- a/ui/qt/voip_calls_dialog.ui +++ b/ui/qt/voip_calls_dialog.ui @@ -11,7 +11,7 @@ - Dialog + VoIP Calls diff --git a/ui/rtp_media.h b/ui/rtp_media.h index 444c1db18c..a5d293cebe 100644 --- a/ui/rtp_media.h +++ b/ui/rtp_media.h @@ -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 { diff --git a/ui/rtp_stream.c b/ui/rtp_stream.c index f54235519d..da916bb0a9 100644 --- a/ui/rtp_stream.c +++ b/ui/rtp_stream.c @@ -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); diff --git a/ui/rtp_stream.h b/ui/rtp_stream.h index d5c85b9f96..2e747ed481 100644 --- a/ui/rtp_stream.h +++ b/ui/rtp_stream.h @@ -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 */ diff --git a/ui/tap-sequence-analysis.c b/ui/tap-sequence-analysis.c index c8aee359c2..cdd7e61a28 100644 --- a/ui/tap-sequence-analysis.c +++ b/ui/tap-sequence-analysis.c @@ -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 */ diff --git a/ui/tap-sequence-analysis.h b/ui/tap-sequence-analysis.h index fa0ba95df8..f3b900464e 100644 --- a/ui/tap-sequence-analysis.h +++ b/ui/tap-sequence-analysis.h @@ -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. */ diff --git a/ui/voip_calls.c b/ui/voip_calls.c index 7200f901dc..a673fde33d 100644 --- a/ui/voip_calls.c +++ b/ui/voip_calls.c @@ -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 diff --git a/ui/voip_calls.h b/ui/voip_calls.h index 6378f300cc..170042a454 100644 --- a/ui/voip_calls.h +++ b/ui/voip_calls.h @@ -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: */ - diff --git a/vagrant_provision.sh b/vagrant_provision.sh index 3504b7becd..b993d0c2c6 100644 --- a/vagrant_provision.sh +++ b/vagrant_provision.sh @@ -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