diff options
author | Jason Parker <jparker@digium.com> | 2013-03-11 15:09:56 -0500 |
---|---|---|
committer | Jason Parker <jparker@digium.com> | 2013-03-11 15:09:56 -0500 |
commit | 483805f79570115ab95c69698792d238c1719b1b (patch) | |
tree | 6b53ab2fd2b2478f864ccc8bd1b0bfaedc4d2050 /pjmedia | |
parent | f3ab456a17af1c89a6e3be4d20c5944853df1cb0 (diff) |
Import pjproject-2.1
Diffstat (limited to 'pjmedia')
48 files changed, 2620 insertions, 582 deletions
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 2f97296..026d89c 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -113,7 +113,7 @@ export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o \ h263_packetizer.o h264_packetizer.o \ $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - ipp_codecs.o $(CODEC_OBJS) \ + ipp_codecs.o opencore_amr.o silk.o $(CODEC_OBJS) \ g7221_sdp_match.o amr_sdp_match.o export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \ $(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS) @@ -173,7 +173,9 @@ pjmedia-audiodev: pjsdp: $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(PJSDP_LIB) -pjmedia-test: $(PJMEDIA_LIB) +$(PJMEDIA_LIB): pjmedia + +pjmedia-test: $(PJMEDIA_LIB) pjmedia $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(PJMEDIA_TEST_EXE) .PHONY: ../lib/pjmedia.ko diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in index 98ecf28..04224e8 100644 --- a/pjmedia/build/os-auto.mak.in +++ b/pjmedia/build/os-auto.mak.in @@ -58,6 +58,7 @@ AC_NO_ILBC_CODEC=@ac_no_ilbc_codec@ AC_NO_G722_CODEC=@ac_no_g722_codec@ AC_NO_G7221_CODEC=@ac_no_g7221_codec@ AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@ +AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@ export CODEC_OBJS= @@ -113,7 +114,15 @@ endif ifeq ($(AC_NO_OPENCORE_AMRNB),1) export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRNB_CODEC=0 else -export CODEC_OBJS += opencore_amrnb.o +export CODEC_OBJS += opencore_amr.o +endif + +ifeq ($(AC_NO_OPENCORE_AMRWB),1) +export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRWB_CODEC=0 +else +ifeq ($(AC_NO_OPENCORE_AMRNB),1) +export CODEC_OBJS += opencore_amr.o +endif endif diff --git a/pjmedia/build/pjmedia_codec.vcproj b/pjmedia/build/pjmedia_codec.vcproj index 158d43d..eddbc8c 100644 --- a/pjmedia/build/pjmedia_codec.vcproj +++ b/pjmedia/build/pjmedia_codec.vcproj @@ -2975,7 +2975,7 @@ </FileConfiguration>
</File>
<File
- RelativePath="..\src\pjmedia-codec\opencore_amrnb.c"
+ RelativePath="..\src\pjmedia-codec\opencore_amr.c"
>
</File>
<File
@@ -2983,6 +2983,10 @@ >
</File>
<File
+ RelativePath="..\src\pjmedia-codec\silk.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia-codec\speex_codec.c"
>
<FileConfiguration
@@ -3122,7 +3126,7 @@ >
</File>
<File
- RelativePath="..\include\pjmedia-codec\opencore_amrnb.h"
+ RelativePath="..\include\pjmedia-codec\opencore_amr.h"
>
</File>
<File
@@ -3134,6 +3138,10 @@ >
</File>
<File
+ RelativePath="..\include\pjmedia-codec\silk.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia-codec\speex.h"
>
</File>
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h index f8edb0d..7e13d14 100644 --- a/pjmedia/include/pjmedia-audiodev/audiodev.h +++ b/pjmedia/include/pjmedia-audiodev/audiodev.h @@ -1,4 +1,4 @@ -/* $Id: audiodev.h 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: audiodev.h 4243 2012-08-31 11:42:17Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -424,6 +424,12 @@ typedef struct pjmedia_aud_param */ pj_bool_t cng_enabled; + /** + * Enable/disable VAD. This setting is optional, and will only be used + * if PJMEDIA_AUD_DEV_CAP_VAD is set in the flags. + */ + pj_bool_t vad_enabled; + } pjmedia_aud_param; diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h index c666996..c63e46e 100644 --- a/pjmedia/include/pjmedia-codec.h +++ b/pjmedia/include/pjmedia-codec.h @@ -1,4 +1,4 @@ -/* $Id: pjmedia-codec.h 4049 2012-04-13 06:24:23Z ming $ */ +/* $Id: pjmedia-codec.h 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -34,8 +34,9 @@ #include <pjmedia-codec/g722.h> #include <pjmedia-codec/g7221.h> #include <pjmedia-codec/ipp_codecs.h> +#include <pjmedia-codec/opencore_amr.h> #include <pjmedia-codec/passthrough.h> -#include <pjmedia-codec/opencore_amrnb.h> +#include <pjmedia-codec/silk.h> #endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */ diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h index 2c71cc9..29350db 100644 --- a/pjmedia/include/pjmedia-codec/config.h +++ b/pjmedia/include/pjmedia-codec/config.h @@ -1,4 +1,4 @@ -/* $Id: config.h 4070 2012-04-23 13:48:10Z bennylp $ */ +/* $Id: config.h 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -323,8 +323,18 @@ #endif /** + * Enable OpenCORE AMR-WB codec. + * See https://trac.pjsip.org/repos/ticket/1608 for some info. + * + * Default: 0 + */ +#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +# define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0 +#endif + +/** * Link with libopencore-amrXX via pragma comment on Visual Studio. - * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC + * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC * is enabled. * * Default: 1 @@ -335,7 +345,7 @@ /** * Link with libopencore-amrXX.a that has been produced with gcc. - * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC + * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC * and PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS are enabled. * * Default: 1 @@ -378,6 +388,38 @@ /** + * Enable SILK codec. + * + * Default: 0 + */ +#ifndef PJMEDIA_HAS_SILK_CODEC +# define PJMEDIA_HAS_SILK_CODEC 0 +#endif + + +/** + * SILK codec default complexity setting, valid values are 0 (lowest), 1, + * and 2. + * + * Default: 2 + */ +#ifndef PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY +# define PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY 2 +#endif + +/** + * SILK codec default quality setting, valid values are ranging from + * 0 (lowest) to 10. Please note that pjsua-lib may override this setting + * via its codec quality setting (i.e PJSUA_DEFAULT_CODEC_QUALITY). + * + * Default: 10 + */ +#ifndef PJMEDIA_CODEC_SILK_DEFAULT_QUALITY +# define PJMEDIA_CODEC_SILK_DEFAULT_QUALITY 10 +#endif + + +/** * Specify if FFMPEG codecs are available. * * Default: PJMEDIA_HAS_LIBAVCODEC diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in index 887d083..11da110 100644 --- a/pjmedia/include/pjmedia-codec/config_auto.h.in +++ b/pjmedia/include/pjmedia-codec/config_auto.h.in @@ -1,4 +1,4 @@ -/* $Id: config_auto.h.in 3841 2011-10-24 09:28:13Z ming $ */ +/* $Id: config_auto.h.in 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -74,6 +74,15 @@ #undef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC #endif +/* OpenCORE AMR-WB codec */ +#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +#undef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +#endif + +/* SILK codec */ +#ifndef PJMEDIA_HAS_SILK_CODEC +#undef PJMEDIA_HAS_SILK_CODEC +#endif #endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */ diff --git a/pjmedia/include/pjmedia-codec/opencore_amr.h b/pjmedia/include/pjmedia-codec/opencore_amr.h new file mode 100644 index 0000000..3d5049b --- /dev/null +++ b/pjmedia/include/pjmedia-codec/opencore_amr.h @@ -0,0 +1,146 @@ +/* $Id: opencore_amr.h 4335 2013-01-29 08:09:15Z ming $ */ +/* + * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_CODEC_OPENCORE_AMR_H__ +#define __PJMEDIA_CODEC_OPENCORE_AMR_H__ + +#include <pjmedia-codec/types.h> + +/** + * @defgroup PJMED_OC_AMR OpenCORE AMR Codec + * @ingroup PJMEDIA_CODEC_CODECS + * @brief AMRCodec wrapper for OpenCORE AMR codec + * @{ + */ + +PJ_BEGIN_DECL + +/** + * Bitmask options to be passed during AMR codec factory initialization. + */ +enum pjmedia_amr_options +{ + PJMEDIA_AMR_NO_NB = 1, /**< Disable narrowband mode. */ + PJMEDIA_AMR_NO_WB = 2, /**< Disable wideband mode. */ +}; + +/** + * Settings. Use #pjmedia_codec_opencore_amrnb/wb_set_config() to + * activate. + */ +typedef struct pjmedia_codec_amr_config +{ + /** + * Control whether to use octent align. + */ + pj_bool_t octet_align; + + /** + * Set the bitrate. + */ + unsigned bitrate; + +} pjmedia_codec_amr_config; + +typedef pjmedia_codec_amr_config pjmedia_codec_amrnb_config; +typedef pjmedia_codec_amr_config pjmedia_codec_amrwb_config; + +/** + * Initialize and register AMR codec factory to pjmedia endpoint. + * + * @param endpt The pjmedia endpoint. + * @param options Bitmask of pjmedia_amr_options (default=0). + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amr_init(pjmedia_endpt* endpt, + unsigned options); + +/** + * Initialize and register AMR codec factory using default settings to + * pjmedia endpoint. + * + * @param endpt The pjmedia endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_codec_opencore_amr_init_default(pjmedia_endpt* endpt); + +/** + * Unregister AMR codec factory from pjmedia endpoint and deinitialize + * the OpenCORE codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amr_deinit(void); + +/** + * Initialize and register AMR-NB codec factory to pjmedia endpoint. Calling + * this function will automatically initialize AMR codec factory without + * the wideband mode (i.e. it is equivalent to calling + * #pjmedia_codec_opencore_amr_init() with PJMEDIA_AMR_NO_WB). Application + * should call #pjmedia_codec_opencore_amr_init() instead if wishing to use + * both modes. + * + * @param endpt The pjmedia endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_init(pjmedia_endpt* endpt); + +/** + * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize + * the OpenCORE codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void); + + +/** + * Set AMR-NB parameters. + * + * @param cfg The settings; + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_set_config( + const pjmedia_codec_amrnb_config* cfg); + + +/** + * Set AMR-WB parameters. + * + * @param cfg The settings; + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrwb_set_config( + const pjmedia_codec_amrwb_config* cfg); + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ */ + diff --git a/pjmedia/include/pjmedia-codec/opencore_amrnb.h b/pjmedia/include/pjmedia-codec/opencore_amrnb.h deleted file mode 100644 index aedb460..0000000 --- a/pjmedia/include/pjmedia-codec/opencore_amrnb.h +++ /dev/null @@ -1,89 +0,0 @@ -/* $Id: opencore_amrnb.h 3841 2011-10-24 09:28:13Z ming $ */ -/* - * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ -#define __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ - -#include <pjmedia-codec/types.h> - -/** - * @defgroup PJMED_OC_AMRNB OpenCORE AMR-NB Codec - * @ingroup PJMEDIA_CODEC_CODECS - * @brief AMRCodec wrapper for OpenCORE AMR-NB codec - * @{ - */ - -PJ_BEGIN_DECL - -/** - * Settings. Use #pjmedia_codec_opencore_amrnb_set_config() to - * activate. - */ -typedef struct pjmedia_codec_amrnb_config -{ - /** - * Control whether to use octent align. - */ - pj_bool_t octet_align; - - /** - * Set the bitrate. - */ - unsigned bitrate; - -} pjmedia_codec_amrnb_config; - - -/** - * Initialize and register AMR-NB codec factory to pjmedia endpoint. - * - * @param endpt The pjmedia endpoint. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_init(pjmedia_endpt* endpt); - -/** - * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize - * the OpenCORE codec library. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void); - - -/** - * Set AMR-NB parameters. - * - * @param cfg The settings; - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_set_config( - const pjmedia_codec_amrnb_config* cfg); - -PJ_END_DECL - - -/** - * @} - */ - -#endif /* __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ */ - diff --git a/pjmedia/include/pjmedia-codec/silk.h b/pjmedia/include/pjmedia-codec/silk.h new file mode 100644 index 0000000..810faca --- /dev/null +++ b/pjmedia/include/pjmedia-codec/silk.h @@ -0,0 +1,133 @@ +/* $Id: silk.h 4264 2012-09-24 06:58:16Z nanang $ */ +/* + * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com) + * Contributed by Regis Montoya (aka r3gis - www.r3gis.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_CODEC_SILK_H__ +#define __PJMEDIA_CODEC_SILK_H__ + +/** + * @file silk.h + * @brief SILK codec. + */ + +#include <pjmedia-codec/types.h> + +/** + * @defgroup PJMED_SILK SILK Codec Family + * @ingroup PJMEDIA_CODEC_CODECS + * @brief Implementation of SILK codecs (narrow/medium/wide/superwide-band). + * @{ + * + * This section describes functions to initialize and register SILK codec + * factory to the codec manager. After the codec factory has been registered, + * application can use @ref PJMEDIA_CODEC API to manipulate the codec. + * + * The SILK codec uses multiple bit rates, and supports super wideband + * (24 kHz sampling rate), wideband (16 kHz sampling rate), medium (12kHz + * sampling rate), and narrowband (telephone quality, 8 kHz sampling rate). + * + * By default, the SILK codec factory registers two SILK codecs: + * "SILK/8000" narrowband codec and "SILK/16000" wideband codec. This behavior + * can be changed by specifying #pjmedia_codec_silk_options flags during + * initialization. + * + * + * \section codec_setting Codec Settings + * + * \subsection general_setting General Settings + * + * General codec settings for this codec such as VAD and PLC can be + * manipulated through the <tt>setting</tt> field in #pjmedia_codec_param. + * Please see the documentation of #pjmedia_codec_param for more info. + * + * \subsection specific_setting Codec Specific Settings + * + * The following settings are applicable for this codec. + * + * \subsubsection quality_vs_complexity Quality vs Complexity + * + * The SILK codec quality versus computational complexity and bandwidth + * requirement can be adjusted by modifying the quality and complexity + * setting, by calling #pjmedia_codec_silk_set_config(). + * + * The default setting of quality is specified in + * #PJMEDIA_CODEC_SILK_DEFAULT_QUALITY. And the default setting of + * complexity is specified in #PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY. + */ + +PJ_BEGIN_DECL + +typedef struct pjmedia_codec_silk_setting +{ + pj_bool_t enabled; /**< Enable/disable. */ + int quality; /**< Encoding quality, or use -1 for default + (@see PJMEDIA_CODEC_SILK_DEFAULT_QUALITY). */ + int complexity; /**< Encoding complexity, or use -1 for default + (@see PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY)*/ +} pjmedia_codec_silk_setting; + + +/** + * Initialize and register SILK codec factory to pjmedia endpoint. By default, + * only narrowband (8kHz sampling rate) and wideband (16kHz sampling rate) + * will be enabled. Quality and complexity for those sampling rate modes + * will be set to the default values (see #PJMEDIA_CODEC_SILK_DEFAULT_QUALITY + * and #PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY), application may modify these + * settings via #pjmedia_codec_silk_set_config(). + * + * @param endpt The pjmedia endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_silk_init(pjmedia_endpt *endpt); + + +/** + * Change the configuration setting of the SILK codec for the specified + * clock rate. + * + * @param clock_rate PCM sampling rate, in Hz, valid values are 8000, + * 12000, 16000 and 24000. + * @param opt The setting to be applied for the specified + * clock rate. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_silk_set_config( + unsigned clock_rate, + const pjmedia_codec_silk_setting *opt); + + +/** + * Unregister SILK codec factory from pjmedia endpoint and deinitialize + * the SILK codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_silk_deinit(void); + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJMEDIA_CODEC_SILK_H__ */ + diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h index a18fa30..53e71e1 100644 --- a/pjmedia/include/pjmedia-codec/types.h +++ b/pjmedia/include/pjmedia-codec/types.h @@ -1,4 +1,4 @@ -/* $Id: types.h 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: types.h 4264 2012-09-24 06:58:16Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -63,18 +63,10 @@ enum pjmedia_audio_pt PJMEDIA_RTP_PT_SPEEX_NB, /**< Speex narrowband/8KHz */ PJMEDIA_RTP_PT_SPEEX_WB, /**< Speex wideband/16KHz */ PJMEDIA_RTP_PT_SPEEX_UWB, /**< Speex 32KHz */ - PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ - PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */ - //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */ - //PJMEDIA_RTP_PT_L16_11KHZ_STEREO, /**< L16 @ 11KHz, stereo */ - PJMEDIA_RTP_PT_L16_16KHZ_MONO, /**< L16 @ 16KHz, mono */ - PJMEDIA_RTP_PT_L16_16KHZ_STEREO, /**< L16 @ 16KHz, stereo */ - //PJMEDIA_RTP_PT_L16_22KHZ_MONO, /**< L16 @ 22KHz, mono */ - //PJMEDIA_RTP_PT_L16_22KHZ_STEREO, /**< L16 @ 22KHz, stereo */ - //PJMEDIA_RTP_PT_L16_32KHZ_MONO, /**< L16 @ 32KHz, mono */ - //PJMEDIA_RTP_PT_L16_32KHZ_STEREO, /**< L16 @ 32KHz, stereo */ - //PJMEDIA_RTP_PT_L16_48KHZ_MONO, /**< L16 @ 48KHz, mono */ - //PJMEDIA_RTP_PT_L16_48KHZ_STEREO, /**< L16 @ 48KHz, stereo */ + PJMEDIA_RTP_PT_SILK_NB, /**< SILK narrowband/8KHz */ + PJMEDIA_RTP_PT_SILK_MB, /**< SILK mediumband/12KHz */ + PJMEDIA_RTP_PT_SILK_WB, /**< SILK wideband/16KHz */ + PJMEDIA_RTP_PT_SILK_SWB, /**< SILK 24KHz */ PJMEDIA_RTP_PT_ILBC, /**< iLBC (13.3/15.2Kbps) */ PJMEDIA_RTP_PT_AMR, /**< AMR (4.75 - 12.2Kbps) */ PJMEDIA_RTP_PT_AMRWB, /**< AMRWB (6.6 - 23.85Kbps)*/ @@ -91,6 +83,18 @@ enum pjmedia_audio_pt PJMEDIA_RTP_PT_G7221C_48, /**< G722.1 Annex C (48Kbps)*/ PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */ PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */ + PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ + PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */ + //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */ + //PJMEDIA_RTP_PT_L16_11KHZ_STEREO, /**< L16 @ 11KHz, stereo */ + PJMEDIA_RTP_PT_L16_16KHZ_MONO, /**< L16 @ 16KHz, mono */ + PJMEDIA_RTP_PT_L16_16KHZ_STEREO, /**< L16 @ 16KHz, stereo */ + //PJMEDIA_RTP_PT_L16_22KHZ_MONO, /**< L16 @ 22KHz, mono */ + //PJMEDIA_RTP_PT_L16_22KHZ_STEREO, /**< L16 @ 22KHz, stereo */ + //PJMEDIA_RTP_PT_L16_32KHZ_MONO, /**< L16 @ 32KHz, mono */ + //PJMEDIA_RTP_PT_L16_32KHZ_STEREO, /**< L16 @ 32KHz, stereo */ + //PJMEDIA_RTP_PT_L16_48KHZ_MONO, /**< L16 @ 48KHz, mono */ + //PJMEDIA_RTP_PT_L16_48KHZ_STEREO, /**< L16 @ 48KHz, stereo */ /* Caution! * Ensure the value of the last pt above is <= 127. diff --git a/pjmedia/include/pjmedia-videodev/config.h b/pjmedia/include/pjmedia-videodev/config.h index 12a251a..ef161eb 100644 --- a/pjmedia/include/pjmedia-videodev/config.h +++ b/pjmedia/include/pjmedia-videodev/config.h @@ -1,4 +1,4 @@ -/* $Id: config.h 4016 2012-04-04 05:05:50Z bennylp $ */ +/* $Id: config.h 4414 2013-03-05 08:21:02Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * @@ -132,6 +132,17 @@ PJ_BEGIN_DECL # define PJMEDIA_VIDEO_DEV_HAS_AVI 1 #endif +/** + * Specify the SDL library name to be linked with Visual Studio project. + * By default, the name is autodetected based on SDL version ("sdl.lib" or + * "sdl2.lib"), but application may explicitly specify the library name if this + * autodetection fails. Common names are: "sdl2.lib" or "sdl.lib". + * + * Default: undeclared. + */ +#ifndef PJMEDIA_SDL_LIB +# undef PJMEDIA_SDL_LIB +#endif /** * @} diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h index 62c79e0..58fa7a1 100644 --- a/pjmedia/include/pjmedia/codec.h +++ b/pjmedia/include/pjmedia/codec.h @@ -1,4 +1,4 @@ -/* $Id: codec.h 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: codec.h 4278 2012-10-05 10:04:54Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -276,6 +276,7 @@ typedef struct pjmedia_codec_param unsigned channel_cnt; /**< Channel count. */ pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */ pj_uint32_t max_bps; /**< Maximum bandwidth in bits/sec */ + unsigned max_rx_frame_size; /**< Maximum frame size */ pj_uint16_t frm_ptime; /**< Decoder frame ptime in msec. */ pj_uint16_t enc_ptime; /**< Encoder ptime, or zero if it's equal to decoder ptime. */ @@ -308,6 +309,18 @@ typedef struct pjmedia_codec_param } pjmedia_codec_param; +/** + * Duplicate codec parameter. + * + * @param pool The pool. + * @param src The codec parameter to be duplicated. + * + * @return Duplicated codec parameter. + */ +PJ_DECL(pjmedia_codec_param*) pjmedia_codec_param_clone( + pj_pool_t *pool, + const pjmedia_codec_param *src); + /* * Forward declaration for pjmedia_codec. diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index d5a598f..b229eca 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1,4 +1,4 @@ -/* $Id: config.h 4130 2012-05-17 08:35:51Z nanang $ */ +/* $Id: config.h 4240 2012-08-31 09:03:36Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -371,7 +371,7 @@ /** - * Max packet size to support. + * Max packet size for transmitting direction. */ #ifndef PJMEDIA_MAX_MTU # define PJMEDIA_MAX_MTU 1500 @@ -379,6 +379,14 @@ /** + * Max packet size for receiving direction. + */ +#ifndef PJMEDIA_MAX_MRU +# define PJMEDIA_MAX_MRU 2000 +#endif + + +/** * DTMF/telephone-event duration, in timestamp. */ #ifndef PJMEDIA_DTMF_DURATION @@ -677,6 +685,28 @@ /** + * This macro controls whether pjmedia should include SDP + * bandwidth modifier "TIAS" (RFC3890). + * + * Note that there is also a run-time variable to turn this setting + * on or off, defined in endpoint.c. To access this variable, use + * the following construct + * + \verbatim + extern pj_bool_t pjmedia_add_bandwidth_tias_in_sdp; + + // Do not enable bandwidth information inclusion in sdp + pjmedia_add_bandwidth_tias_in_sdp = PJ_FALSE; + \endverbatim + * + * Default: 1 (yes) + */ +#ifndef PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP +# define PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP 1 +#endif + + +/** * This macro controls whether pjmedia should include SDP rtpmap * attribute for static payload types. SDP rtpmap for static * payload types are optional, although they are normally included diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h index 2dc58d6..bababef 100644 --- a/pjmedia/include/pjmedia/sdp.h +++ b/pjmedia/include/pjmedia/sdp.h @@ -1,4 +1,4 @@ -/* $Id: sdp.h 3945 2012-01-27 09:12:59Z nanang $ */ +/* $Id: sdp.h 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -686,6 +686,23 @@ PJ_DECL(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp); /** + * Perform semantic validation for the specified SDP session descriptor. + * This function perform validation beyond just syntactic verification, + * such as to verify the value of network type and address type, check + * the connection line, and verify that \a rtpmap attribute is present + * when dynamic payload type is used. + * + * @param sdp The SDP session descriptor to validate. + * @param strict Flag whether the check should be strict, i.e: allow + * media without connection line when port is zero. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp, + pj_bool_t strict); + + +/** * Clone SDP session descriptor. * * @param pool The pool used to clone the session. diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h index dca9f29..e86c974 100644 --- a/pjmedia/include/pjmedia/transport.h +++ b/pjmedia/include/pjmedia/transport.h @@ -1,4 +1,4 @@ -/* $Id: transport.h 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: transport.h 4345 2013-02-13 07:43:32Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -566,7 +566,7 @@ PJ_INLINE(void) pjmedia_transport_info_init(pjmedia_transport_info *info) * for example to fill in the "c=" and "m=" line of local SDP. * * @param tp The transport. - * @param info Media socket info to be initialized. + * @param info Media transport info to be initialized. * * @return PJ_SUCCESS on success. */ @@ -581,6 +581,29 @@ PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp, /** + * Utility API to get transport type specific info from the specified media + * transport info. + * + * @param info Media transport info. + * @param type Media transport type. + * + * @return Pointer to media transport specific info, or NULL if + * specific info for the transport type is not found. + */ +PJ_INLINE(void*) pjmedia_transport_info_get_spc_info( + pjmedia_transport_info *info, + pjmedia_transport_type type) +{ + unsigned i; + for (i = 0; i < info->specific_info_cnt; ++i) { + if (info->spc_info[i].type == type) + return (void*)info->spc_info[i].buffer; + } + return NULL; +} + + +/** * Attach callbacks to be called on receipt of incoming RTP/RTCP packets. * This is just a simple wrapper which calls <tt>attach()</tt> member of * the transport. diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h index 4500fae..417ee6d 100644 --- a/pjmedia/include/pjmedia/transport_ice.h +++ b/pjmedia/include/pjmedia/transport_ice.h @@ -1,4 +1,4 @@ -/* $Id: transport_ice.h 3872 2011-10-28 04:27:41Z bennylp $ */ +/* $Id: transport_ice.h 4350 2013-02-15 03:57:31Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -69,6 +69,12 @@ typedef struct pjmedia_ice_cb typedef struct pjmedia_ice_transport_info { /** + * Specifies whether ICE is used, i.e. SDP offer and answer indicates + * that both parties support ICE and ICE should be used for the session. + */ + pj_bool_t active; + + /** * ICE sesion state. */ pj_ice_strans_state sess_state; diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c index 72b995e..31b3b06 100644 --- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c @@ -1,4 +1,4 @@ -/* $Id: alsa_dev.c 4130 2012-05-17 08:35:51Z nanang $ */ +/* $Id: alsa_dev.c 4283 2012-10-12 06:19:32Z ming $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved. @@ -232,7 +232,7 @@ static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name) pj_bzero(adi, sizeof(*adi)); /* Set device name */ - strcpy(adi->name, dev_name); + strncpy(adi->name, dev_name, sizeof(adi->name)); /* Check the number of playback channels */ adi->output_count = (pb_result>=0) ? 1 : 0; diff --git a/pjmedia/src/pjmedia-audiodev/bb10_dev.c b/pjmedia/src/pjmedia-audiodev/bb10_dev.c index 418256d..f920080 100644 --- a/pjmedia/src/pjmedia-audiodev/bb10_dev.c +++ b/pjmedia/src/pjmedia-audiodev/bb10_dev.c @@ -1,4 +1,4 @@ -/* $Id: bb10_dev.c 4151 2012-06-01 04:49:57Z ming $ */ +/* $Id: bb10_dev.c 4340 2013-02-05 05:15:01Z bennylp $ */ /* * Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com) * @@ -33,6 +33,11 @@ #if defined(PJMEDIA_AUDIO_DEV_HAS_BB10) && PJMEDIA_AUDIO_DEV_HAS_BB10 != 0 +#ifndef PJ_BBSDK_VER + /* Format: 0xMMNNRR: MM: major, NN: minor, RR: revision */ +# define PJ_BBSDK_VER 0x100006 +#endif + #include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -40,6 +45,9 @@ #include <pthread.h> #include <errno.h> #include <sys/asoundlib.h> +#if PJ_BBSDK_VER >= 0x100006 +#include <audio/audio_manager_routing.h> +#endif #define THIS_FILE "bb10_dev.c" @@ -114,8 +122,9 @@ struct bb10_stream int quit; /* Playback */ + unsigned int pb_ctrl_audio_manager_handle; snd_pcm_t *pb_pcm; - snd_mixer_t *pb_mixer; + unsigned int pb_audio_manager_handle; unsigned long pb_frames; /* samples_per_frame */ pjmedia_aud_play_cb pb_cb; unsigned pb_buf_size; @@ -124,7 +133,7 @@ struct bb10_stream /* Capture */ snd_pcm_t *ca_pcm; - snd_mixer_t *ca_mixer; + unsigned int ca_audio_manager_handle; unsigned long ca_frames; /* samples_per_frame */ pjmedia_aud_rec_cb ca_cb; unsigned ca_buf_size; @@ -160,8 +169,7 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af) { pjmedia_aud_dev_info *adi; int pb_result, ca_result; - int card = -1; - int dev = 0; + unsigned int handle; snd_pcm_t *pcm_handle; if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs)) @@ -171,20 +179,29 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af) TRACE_((THIS_FILE, "bb10_add_dev Enter")); - if ((pb_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev, - SND_PCM_OPEN_PLAYBACK)) >= 0) + if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &pcm_handle, + &handle, + (char*)"voice", + SND_PCM_OPEN_PLAYBACK)) + >= 0) { - TRACE_((THIS_FILE, "Try to open the device for playback - success")); snd_pcm_close (pcm_handle); + audio_manager_free_handle(handle); } else { TRACE_((THIS_FILE, "Try to open the device for playback - failure")); } - if ((ca_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev, - SND_PCM_OPEN_CAPTURE)) >=0) + if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &pcm_handle, + &handle, + (char*)"voice", + SND_PCM_OPEN_CAPTURE)) + >= 0) { - TRACE_((THIS_FILE, "Try to open the device for capture - success")); - snd_pcm_close (pcm_handle); + snd_pcm_close (pcm_handle); + audio_manager_free_handle(handle); + } else { TRACE_((THIS_FILE, "Try to open the device for capture - failure")); } @@ -239,7 +256,7 @@ pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf) static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f) { pj_status_t status; - + status = bb10_factory_refresh(f); if (status != PJ_SUCCESS) return status; @@ -314,7 +331,7 @@ static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f, pj_memcpy(info, &af->devs[index], sizeof(*info)); info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; - + return PJ_SUCCESS; } @@ -358,7 +375,7 @@ static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f, TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d" " spf = %d", param->clock_rate, param->flags, param->samples_per_frame)); - + return PJ_SUCCESS; } @@ -368,14 +385,16 @@ static void close_play_pcm(struct bb10_stream *stream) if (stream != NULL && stream->pb_pcm != NULL) { snd_pcm_close(stream->pb_pcm); stream->pb_pcm = NULL; - } -} -static void close_play_mixer(struct bb10_stream *stream) -{ - if (stream != NULL && stream->pb_mixer != NULL) { - snd_mixer_close(stream->pb_mixer); - stream->pb_mixer = NULL; + if (stream->pb_audio_manager_handle != 0) { + audio_manager_free_handle(stream->pb_audio_manager_handle); + stream->pb_audio_manager_handle = 0; + } + + if (stream->pb_ctrl_audio_manager_handle != 0) { + audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle); + stream->pb_ctrl_audio_manager_handle = 0; + } } } @@ -391,14 +410,11 @@ static void close_capture_pcm(struct bb10_stream *stream) if (stream != NULL && stream->ca_pcm != NULL) { snd_pcm_close(stream->ca_pcm); stream->ca_pcm = NULL; - } -} -static void close_capture_mixer(struct bb10_stream *stream) -{ - if (stream != NULL && stream->ca_mixer != NULL) { - snd_mixer_close(stream->ca_mixer); - stream->ca_mixer = NULL; + if (stream->ca_audio_manager_handle != 0) { + audio_manager_free_handle(stream->ca_audio_manager_handle); + stream->ca_audio_manager_handle = 0; + } } } @@ -435,10 +451,9 @@ static int pb_thread_func (void *arg) if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { - close_play_mixer(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); - return PJ_SUCCESS; + return PJ_SUCCESS; } while (!stream->quit) { @@ -451,6 +466,7 @@ static int pb_thread_func (void *arg) frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; + /* Read the audio from pjmedia */ result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; @@ -460,18 +476,45 @@ static int pb_thread_func (void *arg) /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); - if (result != size) { - TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result)); + if (result != size || result < 0) { + /* either the write to output device has failed or not the + * full amount of bytes have been written. This usually happens + * when audio routing is being changed by another thread + * Use a status variable for reading the error + */ + snd_pcm_channel_status_t status; + status.channel = SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { + /* Call has failed nothing we can do except log and + * continue */ + PJ_LOG(4,(THIS_FILE, + "underrun: playback channel status error")); + } else { + /* The status of the error has been read + * RIM say these are expected so we can "re-prepare" the stream + */ + PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", + status.status)); + if (status.status == SND_PCM_STATUS_READY || + status.status == SND_PCM_STATUS_UNDERRUN || + status.status == SND_PCM_STATUS_ERROR ) + { + if (snd_pcm_plugin_prepare (stream->pb_pcm, + SND_PCM_CHANNEL_PLAYBACK) < 0) + { + PJ_LOG(4,(THIS_FILE, + "underrun: playback channel prepare error")); + } + } + } } - tstamp.u64 += nframes; } flush_play(stream); - close_play_mixer(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); - + return PJ_SUCCESS; } @@ -513,29 +556,58 @@ static int ca_thread_func (void *arg) if ((result = snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE)) < 0) { - close_capture_mixer(stream); close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); - return PJ_SUCCESS; + return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; pj_bzero (buf, size); - + + /* read the input device */ result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); - if (result == -EPIPE) { - PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!")); - snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE); - continue; - } else if (result < 0) { - PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!")); + if(result <0 || result != size) { + /* We expect result to be size (640) + * It's not so we have to read the status error and "prepare" + * the channel. This usually happens when output audio routing + * has been changed by another thread. + * We won't "continue", instead just do what we can and leave + * the end of the loop to write what's in the buffer. Not entirely + * correct but saves a potential underrun in PJMEDIA + */ + PJ_LOG (4,(THIS_FILE, + "snd_pcm_plugin_read ERROR read = %d required = %d", + result,size)); + snd_pcm_channel_status_t status; + status.channel = SND_PCM_CHANNEL_CAPTURE; + if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0) + { + /* Should not fail but all we can do is continue */ + PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d", + result)); + } else { + /* RIM say these are the errors that we should "prepare" + * after */ + if (status.status == SND_PCM_STATUS_READY || + status.status == SND_PCM_STATUS_OVERRUN || + status.status == SND_PCM_STATUS_ERROR) + { + if (snd_pcm_plugin_prepare (stream->ca_pcm, + SND_PCM_CHANNEL_CAPTURE) < 0) + { + PJ_LOG (4,(THIS_FILE, + "overrun: capture channel prepare error")); + } + } + } } if (stream->quit) break; + /* Write the capture audio data to PJMEDIA */ frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void *) buf; frame.size = size; @@ -550,19 +622,68 @@ static int ca_thread_func (void *arg) } flush_capture(stream); - close_capture_mixer(stream); close_capture_pcm(stream); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; } +/* Audio routing, speaker/headset */ +static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream, + bool speaker) +{ + /* Although the play and capture have audio manager handles, audio routing + * requires a separate handle + */ + int ret = PJ_SUCCESS; + + if (stream->pb_ctrl_audio_manager_handle == 0) { + /* lazy init an audio manager handle */ + ret = audio_manager_get_handle(AUDIO_TYPE_VIDEO_CHAT, 0, false, + &stream->pb_ctrl_audio_manager_handle); + if (ret != 0) { + TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret)); + return PJMEDIA_EAUD_SYSERR; + } + } + + /* Set for either speaker or earpiece */ + if (speaker) { + ret = audio_manager_set_handle_type( + stream->pb_ctrl_audio_manager_handle, + AUDIO_TYPE_VIDEO_CHAT, + AUDIO_DEVICE_SPEAKER, + AUDIO_DEVICE_DEFAULT); + } else { + ret = audio_manager_set_handle_type( + stream->pb_ctrl_audio_manager_handle, + AUDIO_TYPE_VIDEO_CHAT, + AUDIO_DEVICE_HANDSET, + AUDIO_DEVICE_DEFAULT); + } + + if (ret == 0) { + /* RIM recommend this call */ + ret = audio_manager_set_handle_routing_conditions( + stream->pb_ctrl_audio_manager_handle, + SETTINGS_RESET_ON_DEVICE_CONNECTION); + if (ret != 0) { + TRACE_((THIS_FILE, + "audio_manager_set_handle_routing_conditions ret = %d", + ret)); + return PJMEDIA_EAUD_SYSERR; + } + } else { + TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + + return PJ_SUCCESS; +} static pj_status_t bb10_open_playback (struct bb10_stream *stream, const pjmedia_aud_param *param) { - int card = -1; - int dev = 0; int ret = 0; snd_pcm_channel_info_t pi; snd_pcm_channel_setup_t setup; @@ -575,34 +696,54 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, return PJMEDIA_EAUD_INVDEV; } - if ((ret = snd_pcm_open_preferred (&stream->pb_pcm, &card, &dev, - SND_PCM_OPEN_PLAYBACK)) < 0) + /* Use the bb10 audio manager API to open as opposed to QNX core audio + * Echo cancellation built in + */ + if ((ret = audio_manager_snd_pcm_open_name( + AUDIO_TYPE_VIDEO_CHAT, + &stream->pb_pcm, &stream->pb_audio_manager_handle, + (char*)"voice", + SND_PCM_OPEN_PLAYBACK)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret)); + TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); return PJMEDIA_EAUD_SYSERR; } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_disable(stream->pb_pcm, + PLUGIN_DISABLE_MMAP)) < 0) + { + TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_enable(stream->pb_pcm, + PLUGIN_ROUTING)) < 0) + { + TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* TODO PJ_ZERO */ memset (&pi, 0, sizeof (pi)); pi.channel = SND_PCM_CHANNEL_PLAYBACK; if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); - return PJMEDIA_EAUD_SYSERR; + TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; } memset (&pp, 0, sizeof (pp)); - /* Request VoIP compatible capabilities - * On simulator frag_size is always negotiated to 170 - */ + /* Request VoIP compatible capabilities */ pp.mode = SND_PCM_MODE_BLOCK; pp.channel = SND_PCM_CHANNEL_PLAYBACK; pp.start_mode = SND_PCM_START_DATA; pp.stop_mode = SND_PCM_STOP_ROLLOVER; /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; - /* Increasing this internal buffer count delays write failure in the loop */ - pp.buf.block.frags_max = 4; + /* RIM recommends maximum of 3 */ + pp.buf.block.frags_max = 3; pp.buf.block.frags_min = 1; pp.format.interleave = 1; /* HARD CODE for the time being PJMEDIA expects 16khz */ @@ -622,7 +763,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, memset (&group, 0, sizeof (group)); setup.channel = SND_PCM_CHANNEL_PLAYBACK; setup.mixer_gid = &group.gid; - + if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) { TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret)); return PJMEDIA_EAUD_SYSERR; @@ -631,14 +772,6 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, if (group.gid.name[0] == 0) { return PJMEDIA_EAUD_SYSERR; } - - if ((ret = snd_mixer_open (&stream->pb_mixer, card, - setup.mixer_device)) < 0) - { - TRACE_((THIS_FILE, "snd_mixer_open ret = %d", ret)); - return PJMEDIA_EAUD_SYSERR; - } - rate = param->clock_rate; /* Set the sound device buffer size and latency */ @@ -658,7 +791,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream, TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d", stream->pb_frames, param->clock_rate)); - + return PJ_SUCCESS; } @@ -668,8 +801,6 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, int ret = 0; unsigned int rate; unsigned long tmp_buf_size; - int card = -1; - int dev = 0; int frame_size; snd_pcm_channel_info_t pi; snd_mixer_group_t group; @@ -679,11 +810,27 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt) return PJMEDIA_EAUD_INVDEV; - /* BB10 Audio init here (not prepare) */ - if ((ret = snd_pcm_open_preferred (&stream->ca_pcm, &card, &dev, - SND_PCM_OPEN_CAPTURE)) < 0) + if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, + &stream->ca_pcm, + &stream->ca_audio_manager_handle, + (char*)"voice", + SND_PCM_OPEN_CAPTURE)) < 0) + { + TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm, + PLUGIN_DISABLE_MMAP)) < 0) { - TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret)); + TRACE_(("snd_pcm_plugin_set_disable failed: %d",ret)); + return PJMEDIA_EAUD_SYSERR; + } + /* Required call from January 2013 gold OS release */ + if ((ret = snd_pcm_plugin_set_enable(stream->ca_pcm, + PLUGIN_ROUTING)) < 0) + { + TRACE_(("snd_pcm_plugin_set_enable failed: %d",ret)); return PJMEDIA_EAUD_SYSERR; } @@ -707,8 +854,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, pp.stop_mode = SND_PCM_STOP_ROLLOVER; /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; - /* Not applicable for capture hence -1 */ - pp.buf.block.frags_max = -1; + /* From January 2013 gold OS release. RIM recommend these for capture */ + pp.buf.block.frags_max = 1; pp.buf.block.frags_min = 1; pp.format.interleave = 1; /* HARD CODE for the time being PJMEDIA expects 16khz */ @@ -740,18 +887,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream, } else { } - if ((ret = snd_mixer_open (&stream->ca_mixer, card, - setup.mixer_device)) < 0) - { - TRACE_((THIS_FILE,"snd_mixer_open ret = %d",ret)); - return PJMEDIA_EAUD_SYSERR; - } - - /* frag_size should be 160 */ frame_size = setup.buf.block.frag_size; - /* END BB10 init */ - /* Set clock rate */ rate = param->clock_rate; stream->ca_frames = (unsigned long) param->samples_per_frame / @@ -820,7 +957,6 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, status = bb10_open_capture (stream, param); if (status != PJ_SUCCESS) { if (param->dir & PJMEDIA_DIR_PLAYBACK) { - close_play_mixer(stream); close_play_pcm(stream); } pj_pool_release (pool); @@ -828,12 +964,24 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, } } + /* Part of the play functionality but the RIM/Truphone loopback sample + * initialializes after the play and capture + * "false" is default/earpiece for output + */ + status = bb10_initialize_playback_ctrl(stream,false); + if (status != PJ_SUCCESS) { + return PJMEDIA_EAUD_SYSERR; + } + *p_strm = &stream->base; return PJ_SUCCESS; } -/* API: get running parameter */ +/* + * API: get running parameter + * based on ALSA template + */ static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { @@ -847,7 +995,10 @@ static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s, } -/* API: get capability */ +/* + * API: get capability + * based on ALSA template +*/ static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) @@ -862,28 +1013,51 @@ static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s, /* Recording latency */ *(unsigned*)pval = stream->param.input_latency_ms; return PJ_SUCCESS; + } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (stream->param.dir & PJMEDIA_DIR_PLAYBACK)) { /* Playback latency */ *(unsigned*)pval = stream->param.output_latency_ms; return PJ_SUCCESS; + } else { return PJMEDIA_EAUD_INVCAP; } } -/* API: set capability */ +/* + * API: set capability + * Currently just supporting toggle between speaker and earpiece + */ static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value) { - PJ_UNUSED_ARG(strm); - PJ_UNUSED_ARG(cap); - PJ_UNUSED_ARG(value); + pj_status_t ret = PJ_SUCCESS; + struct bb10_stream *stream = (struct bb10_stream*)strm; + + if (cap != PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE || value == NULL) { + TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP")); + return PJMEDIA_EAUD_INVCAP; + + } else { + pjmedia_aud_dev_route route = *((pjmedia_aud_dev_route*)value); + /* Use the initialization function which lazy-inits the + * handle for routing + */ + if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) { + ret = bb10_initialize_playback_ctrl(stream,true); + } else { + ret = bb10_initialize_playback_ctrl(stream,false); + } + } - return PJMEDIA_EAUD_INVCAP; + if (ret != PJ_SUCCESS) { + TRACE_((THIS_FILE,"bb10_stream_set_cap() = %d",ret)); + } + return ret; } @@ -952,7 +1126,7 @@ static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s) static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s) { struct bb10_stream *stream = (struct bb10_stream*)s; - + TRACE_((THIS_FILE,"bb10_stream_destroy()")); bb10_stream_stop (s); diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp index d2c6564..9b60ae1 100644 --- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp @@ -1,4 +1,4 @@ -/* $Id: symb_aps_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */ +/* $Id: symb_aps_dev.cpp 4243 2012-08-31 11:42:17Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -973,7 +973,8 @@ static void RecCb(TAPSCommBuffer &buf, void *user_data) { unsigned samples_got; - samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240; + samples_got = + strm->param.ext_fmt.det.aud.avg_bps == 15200? 160 : 240; /* Check if we got a normal frame. */ if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) { @@ -1171,9 +1172,9 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data) sf = pjmedia_frame_ext_get_subframe(frame, 0); samples_cnt = frame->samples_cnt / frame->subframe_cnt; - pj_assert((strm->param.ext_fmt.bitrate == 15200 && + pj_assert((strm->param.ext_fmt.det.aud.avg_bps == 15200 && samples_cnt == 160) || - (strm->param.ext_fmt.bitrate != 15200 && + (strm->param.ext_fmt.det.aud.avg_bps != 15200 && samples_cnt == 240)); if (sf->data && sf->bitlen) { @@ -1391,34 +1392,41 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) } if (supported) { + pjmedia_format ext_fmt; + switch(i) { case 0: /* AMRNB */ - af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_AMR; - af->dev_info.ext_fmt[fmt_cnt].bitrate = 7400; - af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_AMR, + 8000, 1, 16, 20, 7400, 12200); + af->dev_info.ext_fmt[fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE; ++fmt_cnt; break; case 1: /* G.711 */ - af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMU; - af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000; - af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMU, + 8000, 1, 16, 20, 64000, 64000); + af->dev_info.ext_fmt[fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; ++fmt_cnt; - af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMA; - af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000; - af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMA, + 8000, 1, 16, 20, 64000, 64000); + af->dev_info.ext_fmt[fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; ++fmt_cnt; g711_supported = PJ_TRUE; break; case 2: /* G.729 */ - af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_G729; - af->dev_info.ext_fmt[fmt_cnt].bitrate = 8000; - af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_G729, + 8000, 1, 16, 20, 8000, 8000); + af->dev_info.ext_fmt[fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE; ++fmt_cnt; break; case 3: /* iLBC */ - af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_ILBC; - af->dev_info.ext_fmt[fmt_cnt].bitrate = 13333; - af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_ILBC, + 8000, 1, 16, 30, 13333, 15200); + af->dev_info.ext_fmt[fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE; ++fmt_cnt; break; } @@ -1570,18 +1578,18 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, /* Set audio engine mode. */ if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR) { - aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.bitrate; + aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.det.aud.avg_bps; } else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU || strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 || (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC && - strm->param.ext_fmt.bitrate != 15200)) + strm->param.ext_fmt.det.aud.avg_bps != 15200)) { aps_setting.mode = EULawOr30ms; } else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA || (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC && - strm->param.ext_fmt.bitrate == 15200)) + strm->param.ext_fmt.det.aud.avg_bps == 15200)) { aps_setting.mode = EALawOr20ms; } @@ -1596,11 +1604,13 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, { aps_setting.vad = EFalse; } else { - aps_setting.vad = strm->param.ext_fmt.vad; + aps_setting.vad = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_VAD) && + strm->param.vad_enabled; } /* Set other audio engine attributes. */ - aps_setting.plc = strm->param.plc_enabled; + aps_setting.plc = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_PLC) && + strm->param.plc_enabled; aps_setting.cng = aps_setting.vad; aps_setting.loudspk = strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER; diff --git a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp index 5b94903..70fe3b3 100644 --- a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp @@ -1,4 +1,4 @@ -/* $Id: symb_vas_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */ +/* $Id: symb_vas_dev.cpp 4243 2012-08-31 11:42:17Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * @@ -1015,7 +1015,8 @@ static void RecCb(CVoIPDataBuffer *buf, void *user_data) { unsigned samples_got; - samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240; + samples_got = + strm->param.ext_fmt.det.aud.avg_bps == 15200? 160 : 240; /* Check if we got a normal or SID frame. */ if (buffer[0] != 0) { @@ -1214,9 +1215,9 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data) sf = pjmedia_frame_ext_get_subframe(frame, 0); samples_cnt = frame->samples_cnt / frame->subframe_cnt; - pj_assert((strm->param.ext_fmt.bitrate == 15200 && + pj_assert((strm->param.ext_fmt.det.aud.avg_bps == 15200 && samples_cnt == 160) || - (strm->param.ext_fmt.bitrate != 15200 && + (strm->param.ext_fmt.det.aud.avg_bps != 15200 && samples_cnt == 240)); if (sf->data && sf->bitlen) { @@ -1230,7 +1231,8 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data) buffer.Append(0); /* VAS iLBC frame is 20ms or 30ms */ - frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50; + frame_len = + strm->param.ext_fmt.det.aud.avg_bps == 15200? 38 : 50; buffer.AppendFill(0, frame_len); } @@ -1244,7 +1246,8 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data) buffer.Append(0); /* VAS iLBC frame is 20ms or 30ms */ - frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50; + frame_len = + strm->param.ext_fmt.det.aud.avg_bps == 15200? 38 : 50; buffer.AppendFill(0, frame_len); } @@ -1408,40 +1411,47 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) vas_factory_ = NULL; for (TInt i = 0; i < dnlink_formats.Count(); i++) { + pjmedia_format ext_fmt; + /* Format must be supported by both downlink & uplink. */ if (uplink_formats.Find(dnlink_formats[i]) == KErrNotFound) continue; switch (dnlink_formats[i]) { case EAMR_NB: - af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_AMR; - af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 7400; - af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_AMR, + 8000, 1, 16, 20, 7400, 12200); + af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE; break; case EG729: - af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_G729; - af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 8000; - af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_G729, + 8000, 1, 16, 20, 8000, 8000); + af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; break; case EILBC: - af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_ILBC; - af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 13333; - af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_ILBC, + 8000, 1, 16, 30, 13333, 15200); + af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE; break; case EG711: #if PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION==2 case EG711_10MS: #endif - af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMU; - af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000; - af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMU, + 8000, 1, 16, 20, 64000, 64000); + af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; ++ext_fmt_cnt; - af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMA; - af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000; - af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; + pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMA, + 8000, 1, 16, 20, 64000, 64000); + af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt; + //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE; break; default: @@ -1616,7 +1626,7 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, } else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR) { - vas_setting.mode = strm->param.ext_fmt.bitrate; + vas_setting.mode = strm->param.ext_fmt.det.aud.avg_bps; } else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU) { @@ -1628,7 +1638,7 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, } else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC) { - if (strm->param.ext_fmt.bitrate == 15200) + if (strm->param.ext_fmt.det.aud.avg_bps == 15200) vas_setting.mode = CVoIPFormatIntfc::EiLBC20mSecFrame; else vas_setting.mode = CVoIPFormatIntfc::EiLBC30mSecFrame; @@ -1647,11 +1657,13 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, { vas_setting.vad = EFalse; } else { - vas_setting.vad = strm->param.ext_fmt.vad; + vas_setting.vad = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_VAD) && + strm->param.vad_enabled; } /* Set other audio engine attributes. */ - vas_setting.plc = strm->param.plc_enabled; + vas_setting.plc = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_PLC) && + strm->param.plc_enabled; vas_setting.cng = vas_setting.vad; vas_setting.loudspk = strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER; diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c index caf63e5..0c21821 100644 --- a/pjmedia/src/pjmedia-codec/audio_codecs.c +++ b/pjmedia/src/pjmedia-codec/audio_codecs.c @@ -1,4 +1,4 @@ -/* $Id: audio_codecs.c 3910 2011-12-15 06:34:25Z nanang $ */ +/* $Id: audio_codecs.c 4335 2013-01-29 08:09:15Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * @@ -107,9 +107,16 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, return status; #endif /* PJMEDIA_HAS_L16_CODEC */ -#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC - /* Register OpenCORE AMR-NB */ - status = pjmedia_codec_opencore_amrnb_init(endpt); +#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC || PJMEDIA_HAS_OPENCORE_AMRWB_CODEC + /* Register OpenCORE AMR */ + status = pjmedia_codec_opencore_amr_init(endpt, 0); + if (status != PJ_SUCCESS) + return status; +#endif + +#if PJMEDIA_HAS_SILK_CODEC + /* Register SILK */ + status = pjmedia_codec_silk_init(endpt); if (status != PJ_SUCCESS) return status; #endif diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c index d0e87ef..a71a2cc 100644 --- a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c +++ b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c @@ -1,4 +1,4 @@ -/* $Id: ffmpeg_vid_codecs.c 4089 2012-04-26 07:27:06Z nanang $ */ +/* $Id: ffmpeg_vid_codecs.c 4311 2012-12-20 06:45:09Z nanang $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * @@ -62,11 +62,16 @@ #endif #if LIBAVCODEC_VER_AT_LEAST(53,61) -/* Not sure when AVCodec::encode2 is introduced. It appears in - * libavcodec 53.61 where some codecs actually still use AVCodec::encode - * (e.g: H263, H264). - */ -# define AVCODEC_HAS_ENCODE(c) (c->encode || c->encode2) +# if LIBAVCODEC_VER_AT_LEAST(54,59) + /* Not sure when AVCodec::encode is obsoleted/removed. */ +# define AVCODEC_HAS_ENCODE(c) (c->encode2) +# else + /* Not sure when AVCodec::encode2 is introduced. It appears in + * libavcodec 53.61 where some codecs actually still use AVCodec::encode + * (e.g: H263, H264). + */ +# define AVCODEC_HAS_ENCODE(c) (c->encode || c->encode2) +# endif # define AV_OPT_SET(obj,name,val,opt) (av_opt_set(obj,name,val,opt)==0) # define AV_OPT_SET_INT(obj,name,val) (av_opt_set_int(obj,name,val,0)==0) #else diff --git a/pjmedia/src/pjmedia-codec/opencore_amrnb.c b/pjmedia/src/pjmedia-codec/opencore_amr.c index 9b6daa9..ccfcc2e 100644 --- a/pjmedia/src/pjmedia-codec/opencore_amrnb.c +++ b/pjmedia/src/pjmedia-codec/opencore_amr.c @@ -1,6 +1,6 @@ -/* $Id: opencore_amrnb.c 3939 2012-01-10 05:38:40Z nanang $ */ +/* $Id: opencore_amr.c 4348 2013-02-14 02:00:13Z ming $ */ /* - * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se> * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ */ /* - * AMR-NB codec implementation with OpenCORE AMRNB library + * AMR codec implementation with OpenCORE AMR library */ #include <pjmedia-codec/g722.h> #include <pjmedia-codec/amr_sdp_match.h> @@ -38,13 +38,30 @@ #if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \ (PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0) +#define USE_AMRNB +#endif + +#if defined(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC) && \ + (PJMEDIA_HAS_OPENCORE_AMRWB_CODEC != 0) +#define USE_AMRWB +#endif +#if defined(USE_AMRNB) || defined(USE_AMRWB) + +#ifdef USE_AMRNB #include <opencore-amrnb/interf_enc.h> #include <opencore-amrnb/interf_dec.h> +#endif + +#ifdef USE_AMRWB +#include <vo-amrwbenc/enc_if.h> +#include <opencore-amrwb/dec_if.h> +#endif + #include <pjmedia-codec/amr_helper.h> -#include <pjmedia-codec/opencore_amrnb.h> +#include <pjmedia-codec/opencore_amr.h> -#define THIS_FILE "opencore_amrnb.c" +#define THIS_FILE "opencore_amr.c" /* Tracing */ #define PJ_TRACE 0 @@ -58,9 +75,10 @@ /* Use PJMEDIA PLC */ #define USE_PJMEDIA_PLC 1 +#define FRAME_LENGTH_MS 20 -/* Prototypes for AMR-NB factory */ +/* Prototypes for AMR factory */ static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t amr_default_attr(pjmedia_codec_factory *factory, @@ -75,7 +93,7 @@ static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory, static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ); -/* Prototypes for AMR-NB implementation. */ +/* Prototypes for AMR implementation. */ static pj_status_t amr_codec_init(pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t amr_codec_open(pjmedia_codec *codec, @@ -103,7 +121,7 @@ static pj_status_t amr_codec_recover(pjmedia_codec *codec, -/* Definition for AMR-NB codec operations. */ +/* Definition for AMR codec operations. */ static pjmedia_codec_op amr_op = { &amr_codec_init, @@ -116,7 +134,7 @@ static pjmedia_codec_op amr_op = &amr_codec_recover }; -/* Definition for AMR-NB codec factory operations. */ +/* Definition for AMR codec factory operations. */ static pjmedia_codec_factory_op amr_factory_op = { &amr_test_alloc, @@ -124,23 +142,25 @@ static pjmedia_codec_factory_op amr_factory_op = &amr_enum_codecs, &amr_alloc_codec, &amr_dealloc_codec, - &pjmedia_codec_opencore_amrnb_deinit + &pjmedia_codec_opencore_amr_deinit }; -/* AMR-NB factory */ +/* AMR factory */ static struct amr_codec_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; + pj_bool_t init[2]; } amr_codec_factory; -/* AMR-NB codec private data. */ +/* AMR codec private data. */ struct amr_data { pj_pool_t *pool; + unsigned clock_rate; void *encoder; void *decoder; pj_bool_t plc_enabled; @@ -154,18 +174,38 @@ struct amr_data pj_timestamp last_tx; }; -static pjmedia_codec_amrnb_config def_config = +/* Index for AMR tables. */ +enum { + IDX_AMR_NB, /* Index for narrowband. */ + IDX_AMR_WB /* Index for wideband. */ +}; + +static pjmedia_codec_amr_config def_config[2] = +{{ /* AMR-NB */ PJ_FALSE, /* octet align */ 5900 /* bitrate */ -}; + }, + { /* AMR-WB */ + PJ_FALSE, /* octet align */ + 12650 /* bitrate */ + }}; +static const pj_uint16_t* amr_bitrates[2] = + {pjmedia_codec_amrnb_bitrates, pjmedia_codec_amrwb_bitrates}; + +static const unsigned amr_bitrates_size[2] = +{ + PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates), + PJ_ARRAY_SIZE(pjmedia_codec_amrwb_bitrates) +}; /* - * Initialize and register AMR-NB codec factory to pjmedia endpoint. + * Initialize and register AMR codec factory to pjmedia endpoint. */ -PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt ) +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_init( pjmedia_endpt *endpt, + unsigned options) { pjmedia_codec_mgr *codec_mgr; pj_str_t codec_name; @@ -174,12 +214,22 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt ) if (amr_codec_factory.pool != NULL) return PJ_SUCCESS; - /* Create AMR-NB codec factory. */ + /* Create AMR codec factory. */ amr_codec_factory.base.op = &amr_factory_op; amr_codec_factory.base.factory_data = NULL; amr_codec_factory.endpt = endpt; +#ifdef USE_AMRNB + amr_codec_factory.init[IDX_AMR_NB] = ((options & PJMEDIA_AMR_NO_NB) == 0); +#else + amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE; +#endif +#ifdef USE_AMRWB + amr_codec_factory.init[IDX_AMR_WB] = ((options & PJMEDIA_AMR_NO_WB) == 0); +#else + amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE; +#endif - amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amrnb", 1000, + amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amr", 1000, 1000); if (!amr_codec_factory.pool) return PJ_ENOMEM; @@ -214,16 +264,30 @@ on_error: return status; } +PJ_DEF(pj_status_t) +pjmedia_codec_opencore_amr_init_default( pjmedia_endpt *endpt ) +{ + return pjmedia_codec_opencore_amr_init(endpt, 0); +} + +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt ) +{ + return pjmedia_codec_opencore_amr_init(endpt, PJMEDIA_AMR_NO_WB); +} + /* - * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize - * the AMR-NB codec library. + * Unregister AMR codec factory from pjmedia endpoint and deinitialize + * the AMR codec library. */ -PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void) +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; + amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE; + amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE; + if (amr_codec_factory.pool == NULL) return PJ_SUCCESS; @@ -235,7 +299,7 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void) return PJ_EINVALIDOP; } - /* Unregister AMR-NB codec factory. */ + /* Unregister AMR codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &amr_codec_factory.base); @@ -246,50 +310,93 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void) return status; } +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void) +{ + if (amr_codec_factory.init[IDX_AMR_NB] && + amr_codec_factory.init[IDX_AMR_WB]) + { + PJ_LOG(4, (THIS_FILE, "Should call " + "pjmedia_codec_opencore_amr_deinit() instead")); + + return PJ_EINVALIDOP; + } + + return pjmedia_codec_opencore_amr_deinit(); +} -PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config( - const pjmedia_codec_amrnb_config *config) +static pj_status_t +amr_set_config(unsigned idx, const pjmedia_codec_amr_config *config) { unsigned nbitrates; - - def_config = *config; + def_config[idx] = *config; /* Normalize bitrate. */ - nbitrates = PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates); - if (def_config.bitrate < pjmedia_codec_amrnb_bitrates[0]) - def_config.bitrate = pjmedia_codec_amrnb_bitrates[0]; - else if (def_config.bitrate > pjmedia_codec_amrnb_bitrates[nbitrates-1]) - def_config.bitrate = pjmedia_codec_amrnb_bitrates[nbitrates-1]; - else + nbitrates = amr_bitrates_size[idx]; + if (def_config[idx].bitrate < amr_bitrates[idx][0]) { + def_config[idx].bitrate = amr_bitrates[idx][0]; + } else if (def_config[idx].bitrate > amr_bitrates[idx][nbitrates-1]) { + def_config[idx].bitrate = amr_bitrates[idx][nbitrates-1]; + } else { unsigned i; for (i = 0; i < nbitrates; ++i) { - if (def_config.bitrate <= pjmedia_codec_amrnb_bitrates[i]) + if (def_config[idx].bitrate <= amr_bitrates[idx][i]) break; } - def_config.bitrate = pjmedia_codec_amrnb_bitrates[i]; + def_config[idx].bitrate = amr_bitrates[idx][i]; } return PJ_SUCCESS; } -/* - * Check if factory can allocate the specified codec. +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config( + const pjmedia_codec_amrnb_config *config) +{ + return amr_set_config(IDX_AMR_NB, (const pjmedia_codec_amr_config *)config); +} + +PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrwb_set_config( + const pjmedia_codec_amrwb_config *config) +{ + return amr_set_config(IDX_AMR_WB, (const pjmedia_codec_amr_config *)config); +} + +/* + * Check if factory can allocate the specified codec. */ static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { + const pj_str_t amr_tag = { "AMR", 3}; + const pj_str_t amrwb_tag = { "AMR-WB", 6}; PJ_UNUSED_ARG(factory); + /* Type MUST be audio. */ + if (info->type != PJMEDIA_TYPE_AUDIO) + return PJMEDIA_CODEC_EUNSUP; + /* Check payload type. */ - if (info->pt != PJMEDIA_RTP_PT_AMR) + if (info->pt != PJMEDIA_RTP_PT_AMR && info->pt != PJMEDIA_RTP_PT_AMRWB) return PJMEDIA_CODEC_EUNSUP; + + /* Check encoding name. */ + if (pj_stricmp(&info->encoding_name, &amr_tag) != 0 && + pj_stricmp(&info->encoding_name, &amrwb_tag) != 0) + { + return PJMEDIA_CODEC_EUNSUP; + } + + /* Check clock-rate */ + if ((info->clock_rate == 8000 && amr_codec_factory.init[IDX_AMR_NB]) || + (info->clock_rate == 16000 && amr_codec_factory.init[IDX_AMR_WB])) + { + return PJ_SUCCESS; + } - /* Ignore the rest, since it's static payload type. */ - - return PJ_SUCCESS; + /* Unsupported or disabled. */ + return PJMEDIA_CODEC_EUNSUP; } /* @@ -299,23 +406,25 @@ static pj_status_t amr_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { + unsigned idx; + PJ_UNUSED_ARG(factory); - PJ_UNUSED_ARG(id); + idx = (id->clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB); pj_bzero(attr, sizeof(pjmedia_codec_param)); - attr->info.clock_rate = 8000; + attr->info.clock_rate = (id->clock_rate <= 8000? 8000: 16000); attr->info.channel_cnt = 1; - attr->info.avg_bps = def_config.bitrate; - attr->info.max_bps = pjmedia_codec_amrnb_bitrates[7]; + attr->info.avg_bps = def_config[idx].bitrate; + attr->info.max_bps = amr_bitrates[idx][amr_bitrates_size[idx]-1]; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = 20; - attr->info.pt = PJMEDIA_RTP_PT_AMR; + attr->info.pt = (pj_uint8_t)id->pt; attr->setting.frm_per_pkt = 2; attr->setting.vad = 1; attr->setting.plc = 1; - if (def_config.octet_align) { + if (def_config[idx].octet_align) { attr->setting.dec_fmtp.cnt = 1; attr->setting.dec_fmtp.param[0].name = pj_str("octet-align"); attr->setting.dec_fmtp.param[0].val = pj_str("1"); @@ -328,7 +437,7 @@ static pj_status_t amr_default_attr( pjmedia_codec_factory *factory, /* - * Enum codecs supported by this factory (i.e. only AMR-NB!). + * Enum codecs supported by this factory (i.e. AMR-NB and AMR-WB). */ static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory, unsigned *count, @@ -337,21 +446,34 @@ static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory, PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); - pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); - codecs[0].encoding_name = pj_str("AMR"); - codecs[0].pt = PJMEDIA_RTP_PT_AMR; - codecs[0].type = PJMEDIA_TYPE_AUDIO; - codecs[0].clock_rate = 8000; - codecs[0].channel_cnt = 1; + *count = 0; - *count = 1; + if (amr_codec_factory.init[IDX_AMR_NB]) { + pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); + codecs[*count].encoding_name = pj_str("AMR"); + codecs[*count].pt = PJMEDIA_RTP_PT_AMR; + codecs[*count].type = PJMEDIA_TYPE_AUDIO; + codecs[*count].clock_rate = 8000; + codecs[*count].channel_cnt = 1; + (*count)++; + } + + if (amr_codec_factory.init[IDX_AMR_WB]) { + pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); + codecs[*count].encoding_name = pj_str("AMR-WB"); + codecs[*count].pt = PJMEDIA_RTP_PT_AMRWB; + codecs[*count].type = PJMEDIA_TYPE_AUDIO; + codecs[*count].clock_rate = 16000; + codecs[*count].channel_cnt = 1; + (*count)++; + } return PJ_SUCCESS; } /* - * Allocate a new AMR-NB codec instance. + * Allocate a new AMR codec instance. */ static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, @@ -365,7 +487,7 @@ static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory, PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL); - pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amrnb-inst", + pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amr-inst", 512, 512); codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); @@ -379,7 +501,9 @@ static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory, #if USE_PJMEDIA_PLC /* Create PLC */ - status = pjmedia_plc_create(pool, 8000, 160, 0, &amr_data->plc); + status = pjmedia_plc_create(pool, id->clock_rate, + id->clock_rate * FRAME_LENGTH_MS / 1000, 0, + &amr_data->plc); if (status != PJ_SUCCESS) { return status; } @@ -437,12 +561,14 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec, pj_uint8_t octet_align = 0; pj_int8_t enc_mode; const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11}; + unsigned idx; PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP); + idx = (attr->info.clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB); enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps); - pj_assert(enc_mode >= 0 && enc_mode <= 7); + pj_assert(enc_mode >= 0 && enc_mode < amr_bitrates_size[idx]); /* Check octet-align */ for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) { @@ -476,7 +602,7 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec, p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val); l = pj_strlen(&attr->setting.enc_fmtp.param[i].val); while (l--) { - if (*p>='0' && *p<='7') { + if (*p>='0' && *p<=('0'+amr_bitrates_size[idx]-1)) { pj_int8_t tmp = *p - '0' - enc_mode; if (PJ_ABS(diff) > PJ_ABS(tmp) || @@ -496,38 +622,56 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec, } } + amr_data->clock_rate = attr->info.clock_rate; amr_data->vad_enabled = (attr->setting.vad != 0); amr_data->plc_enabled = (attr->setting.plc != 0); amr_data->enc_mode = enc_mode; - amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled); + if (idx == IDX_AMR_NB) { +#ifdef USE_AMRNB + amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled); +#endif + } else { +#ifdef USE_AMRWB + amr_data->encoder = E_IF_init(); +#endif + } if (amr_data->encoder == NULL) { - TRACE_((THIS_FILE, "Encoder_Interface_init() failed")); + TRACE_((THIS_FILE, "Encoder initialization failed")); amr_codec_close(codec); return PJMEDIA_CODEC_EFAILED; } setting = &amr_data->enc_setting; pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting)); - setting->amr_nb = 1; + setting->amr_nb = (idx == IDX_AMR_NB? 1: 0); setting->reorder = 0; setting->octet_aligned = octet_align; setting->cmr = 15; - amr_data->decoder = Decoder_Interface_init(); + if (idx == IDX_AMR_NB) { +#ifdef USE_AMRNB + amr_data->decoder = Decoder_Interface_init(); +#endif + } else { +#ifdef USE_AMRWB + amr_data->decoder = D_IF_init(); +#endif + } if (amr_data->decoder == NULL) { - TRACE_((THIS_FILE, "Decoder_Interface_init() failed")); + TRACE_((THIS_FILE, "Decoder initialization failed")); amr_codec_close(codec); return PJMEDIA_CODEC_EFAILED; } setting = &amr_data->dec_setting; pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting)); - setting->amr_nb = 1; + setting->amr_nb = (idx == IDX_AMR_NB? 1: 0); setting->reorder = 0; setting->octet_aligned = octet_align; - TRACE_((THIS_FILE, "AMR-NB codec allocated: vad=%d, plc=%d, bitrate=%d", + TRACE_((THIS_FILE, "AMR codec allocated: clockrate=%d vad=%d, plc=%d," + " bitrate=%d", amr_data->clock_rate, amr_data->vad_enabled, amr_data->plc_enabled, - pjmedia_codec_amrnb_bitrates[amr_data->enc_mode])); + amr_bitrates[idx][amr_data->enc_mode])); return PJ_SUCCESS; } @@ -545,16 +689,32 @@ static pj_status_t amr_codec_close( pjmedia_codec *codec ) PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP); if (amr_data->encoder) { - Encoder_Interface_exit(amr_data->encoder); + if (amr_data->enc_setting.amr_nb) { +#ifdef USE_AMRNB + Encoder_Interface_exit(amr_data->encoder); +#endif + } else { +#ifdef USE_AMRWB + E_IF_exit(amr_data->encoder); +#endif + } amr_data->encoder = NULL; } if (amr_data->decoder) { - Decoder_Interface_exit(amr_data->decoder); + if (amr_data->dec_setting.amr_nb) { +#ifdef USE_AMRNB + Decoder_Interface_exit(amr_data->decoder); +#endif + } else { +#ifdef USE_AMRWB + D_IF_exit(amr_data->decoder); +#endif + } amr_data->decoder = NULL; } - TRACE_((THIS_FILE, "AMR-NB codec closed")); + TRACE_((THIS_FILE, "AMR codec closed")); return PJ_SUCCESS; } @@ -575,11 +735,15 @@ static pj_status_t amr_codec_modify( pjmedia_codec *codec, amr_data->vad_enabled = (attr->setting.vad != 0); amr_data->plc_enabled = (attr->setting.plc != 0); - if (prev_vad_state != amr_data->vad_enabled) { + if (amr_data->enc_setting.amr_nb && + prev_vad_state != amr_data->vad_enabled) + { /* Reinit AMR encoder to update VAD setting */ TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting.")); +#ifdef USE_AMRNB Encoder_Interface_exit(amr_data->encoder); amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled); +#endif if (amr_data->encoder == NULL) { TRACE_((THIS_FILE, "Encoder_Interface_init() failed")); amr_codec_close(codec); @@ -587,7 +751,7 @@ static pj_status_t amr_codec_modify( pjmedia_codec *codec, } } - TRACE_((THIS_FILE, "AMR-NB codec modified: vad=%d, plc=%d", + TRACE_((THIS_FILE, "AMR codec modified: vad=%d, plc=%d", amr_data->vad_enabled, amr_data->plc_enabled)); return PJ_SUCCESS; } @@ -606,6 +770,7 @@ static pj_status_t amr_codec_parse( pjmedia_codec *codec, struct amr_data *amr_data = (struct amr_data*) codec->codec_data; pj_uint8_t cmr; pj_status_t status; + unsigned idx = (amr_data->enc_setting.amr_nb? 0: 1); status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting, frames, frame_cnt, &cmr); @@ -613,11 +778,11 @@ static pj_status_t amr_codec_parse( pjmedia_codec *codec, return status; /* Check for Change Mode Request. */ - if (cmr <= 7 && amr_data->enc_mode != cmr) { + if (cmr < amr_bitrates_size[idx] && amr_data->enc_mode != cmr) { amr_data->enc_mode = cmr; - TRACE_((THIS_FILE, "AMR-NB encoder switched mode to %d (%dbps)", - amr_data->enc_mode, - pjmedia_codec_amrnb_bitrates[amr_data->enc_mode])); + TRACE_((THIS_FILE, "AMR encoder switched mode to %d (%dbps)", + amr_data->enc_mode, + amr_bitrates[idx][amr_data->enc_mode])); } return PJ_SUCCESS; @@ -649,7 +814,7 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec, PJ_ASSERT_RETURN(input && output, PJ_EINVAL); nsamples = input->size >> 1; - samples_per_frame = 160; + samples_per_frame = amr_data->clock_rate * FRAME_LENGTH_MS / 1000; PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); @@ -661,20 +826,30 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec, speech = (pj_int16_t*)input->buf; bitstream = (unsigned char*)output->buf; while (nsamples >= samples_per_frame) { - size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode, - speech, bitstream, 0); + if (amr_data->enc_setting.amr_nb) { +#ifdef USE_AMRNB + size = Encoder_Interface_Encode (amr_data->encoder, + amr_data->enc_mode, + speech, bitstream, 0); +#endif + } else { +#ifdef USE_AMRWB + size = E_IF_encode (amr_data->encoder, amr_data->enc_mode, + speech, bitstream, 0); +#endif + } if (size == 0) { output->size = 0; output->buf = NULL; output->type = PJMEDIA_FRAME_TYPE_NONE; - TRACE_((THIS_FILE, "AMR-NB encode() failed")); + TRACE_((THIS_FILE, "AMR encode() failed")); return PJMEDIA_CODEC_EFAILED; } - nsamples -= 160; + nsamples -= samples_per_frame; speech += samples_per_frame; bitstream += size; out_size += size; - TRACE_((THIS_FILE, "AMR-NB encode(): mode=%d, size=%d", + TRACE_((THIS_FILE, "AMR encode(): mode=%d, size=%d", amr_data->enc_mode, out_size)); } @@ -690,8 +865,13 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec, info->mode = (pj_int8_t)amr_data->enc_mode; info->start_bit = 0; frames[i].buf = p + 1; - frames[i].size = (info->frame_type <= 8)? - pjmedia_codec_amrnb_framelen[info->frame_type] : 0; + if (amr_data->enc_setting.amr_nb) { + frames[i].size = (info->frame_type <= 8)? + pjmedia_codec_amrnb_framelen[info->frame_type] : 0; + } else { + frames[i].size = (info->frame_type <= 9)? + pjmedia_codec_amrwb_framelen[info->frame_type] : 0; + } p += frames[i].size + 1; /* Count the number of SID and DTX frames */ @@ -713,7 +893,8 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec, dtx_duration = pj_timestamp_diff32(&amr_data->last_tx, &input->timestamp); if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || - dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000) + dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD* + amr_data->clock_rate/1000) { output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; @@ -755,31 +936,43 @@ static pj_status_t amr_codec_decode( pjmedia_codec *codec, struct amr_data *amr_data = (struct amr_data*) codec->codec_data; pjmedia_frame input_; pjmedia_codec_amr_bit_info *info; - /* VA AMR-NB decoding buffer: AMR-NB max frame size + 1 byte header. */ - unsigned char bitstream[32]; + unsigned out_size; + /* AMR decoding buffer: AMR max frame size + 1 byte header. */ + unsigned char bitstream[61]; pj_assert(amr_data != NULL); PJ_ASSERT_RETURN(input && output, PJ_EINVAL); - if (output_buf_len < 320) + out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2; + if (output_buf_len < out_size) return PJMEDIA_CODEC_EPCMTOOSHORT; input_.buf = &bitstream[1]; - input_.size = 31; /* AMR-NB max frame size */ + /* AMR max frame size */ + input_.size = (amr_data->dec_setting.amr_nb? 31: 60); pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_); info = (pjmedia_codec_amr_bit_info*)&input_.bit_info; - /* VA AMRNB decoder requires frame info in the first byte. */ + /* VA AMR decoder requires frame info in the first byte. */ bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2); - TRACE_((THIS_FILE, "AMR-NB decode(): mode=%d, ft=%d, size=%d", + TRACE_((THIS_FILE, "AMR decode(): mode=%d, ft=%d, size=%d", info->mode, info->frame_type, input_.size)); /* Decode */ - Decoder_Interface_Decode(amr_data->decoder, bitstream, - (pj_int16_t*)output->buf, 0); + if (amr_data->dec_setting.amr_nb) { +#ifdef USE_AMRNB + Decoder_Interface_Decode(amr_data->decoder, bitstream, + (pj_int16_t*)output->buf, 0); +#endif + } else { +#ifdef USE_AMRWB + D_IF_decode(amr_data->decoder, bitstream, + (pj_int16_t*)output->buf, 0); +#endif + } - output->size = 320; + output->size = out_size; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; @@ -804,16 +997,17 @@ static pj_status_t amr_codec_recover( pjmedia_codec *codec, struct pjmedia_frame *output) { struct amr_data *amr_data = codec->codec_data; + unsigned out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2; TRACE_((THIS_FILE, "amr_codec_recover")); PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP); - PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT); + PJ_ASSERT_RETURN(output_buf_len >= out_size, PJMEDIA_CODEC_EPCMTOOSHORT); pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf); - output->size = 320; + output->size = out_size; output->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; @@ -821,11 +1015,17 @@ static pj_status_t amr_codec_recover( pjmedia_codec *codec, #endif #if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS -# if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC -# pragma comment( lib, "libopencore-amrnb.a") -# else -# error Unsupported OpenCORE AMR library, fix here -# endif +# if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC +# ifdef USE_AMRNB +# pragma comment( lib, "libopencore-amrnb.a") +# endif +# ifdef USE_AMRWB +# pragma comment( lib, "libopencore-amrwb.a") +# pragma comment( lib, "libvo-amrwbenc.a") +# endif +# else +# error Unsupported OpenCORE AMR library, fix here +# endif #endif #endif diff --git a/pjmedia/src/pjmedia-codec/silk.c b/pjmedia/src/pjmedia-codec/silk.c new file mode 100644 index 0000000..75ee7ca --- /dev/null +++ b/pjmedia/src/pjmedia-codec/silk.c @@ -0,0 +1,953 @@ +/* $Id: silk.c 4339 2013-01-31 05:23:46Z ming $ */ +/* + * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com) + * Contributed by Regis Montoya (aka r3gis - www.r3gis.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <pjmedia-codec/silk.h> +#include <pjmedia/codec.h> +#include <pjmedia/delaybuf.h> +#include <pjmedia/endpoint.h> +#include <pjmedia/errno.h> +#include <pjmedia/port.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/log.h> + +#if defined(PJMEDIA_HAS_SILK_CODEC) && (PJMEDIA_HAS_SILK_CODEC!=0) + +#include "SKP_Silk_SDK_API.h" + +#define THIS_FILE "silk.c" + +#ifndef PJMEDIA_SILK_DELAY_BUF_OPTIONS + #define PJMEDIA_SILK_DELAY_BUF_OPTIONS PJMEDIA_DELAY_BUF_SIMPLE_FIFO +#endif + +#define FRAME_LENGTH_MS 20 +#define SILK_ENC_CTL_PACKET_LOSS_PCT 10 +#define SILK_MIN_BITRATE 5000 +#define CALC_BITRATE_QUALITY(quality, max_br) \ + (quality * max_br / 10) +#define CALC_BITRATE(max_br) \ + CALC_BITRATE_QUALITY(PJMEDIA_CODEC_SILK_DEFAULT_QUALITY, \ + max_br); + + +/* Prototypes for SILK factory */ +static pj_status_t silk_test_alloc( pjmedia_codec_factory *factory, + const pjmedia_codec_info *id ); +static pj_status_t silk_default_attr( pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec_param *attr ); +static pj_status_t silk_enum_codecs ( pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]); +static pj_status_t silk_alloc_codec( pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec); +static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory, + pjmedia_codec *codec ); + +/* Prototypes for SILK implementation. */ +static pj_status_t silk_codec_init( pjmedia_codec *codec, + pj_pool_t *pool ); +static pj_status_t silk_codec_open( pjmedia_codec *codec, + pjmedia_codec_param *attr ); +static pj_status_t silk_codec_close( pjmedia_codec *codec ); +static pj_status_t silk_codec_modify( pjmedia_codec *codec, + const pjmedia_codec_param *attr ); +static pj_status_t silk_codec_parse( pjmedia_codec *codec, + void *pkt, + pj_size_t pkt_size, + const pj_timestamp *timestamp, + unsigned *frame_cnt, + pjmedia_frame frames[]); +static pj_status_t silk_codec_encode( pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t silk_codec_decode( pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t silk_codec_recover( pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output); + + +typedef enum +{ + PARAM_NB, /* Index for narrowband parameter. */ + PARAM_MB, /* Index for medium parameter. */ + PARAM_WB, /* Index for wideband parameter. */ + PARAM_SWB, /* Index for super-wideband parameter */ +} silk_mode; + + +/* Silk default parameter */ +typedef struct silk_param +{ + int enabled; /* Is this mode enabled? */ + int pt; /* Payload type. */ + unsigned clock_rate; /* Default sampling rate to be used. */ + pj_uint16_t ptime; /* packet length (in ms). */ + pj_uint32_t bitrate; /* Bit rate for current mode. */ + pj_uint32_t max_bitrate; /* Max bit rate for current mode. */ + int complexity; /* Complexity mode: 0/lowest to 2. */ +} silk_param; + + +/* Definition for SILK codec operations. */ +static pjmedia_codec_op silk_op = +{ + &silk_codec_init, + &silk_codec_open, + &silk_codec_close, + &silk_codec_modify, + &silk_codec_parse, + &silk_codec_encode, + &silk_codec_decode, + &silk_codec_recover +}; + +/* Definition for SILK codec factory operations. */ +static pjmedia_codec_factory_op silk_factory_op = +{ + &silk_test_alloc, + &silk_default_attr, + &silk_enum_codecs, + &silk_alloc_codec, + &silk_dealloc_codec, + &pjmedia_codec_silk_deinit +}; + + +/* SILK factory private data */ +static struct silk_factory +{ + pjmedia_codec_factory base; + pjmedia_endpt *endpt; + pj_pool_t *pool; + pj_mutex_t *mutex; + struct silk_param silk_param[4]; +} silk_factory; + + +/* SILK codec private data. */ +typedef struct silk_private +{ + silk_mode mode; /**< Silk mode. */ + pj_pool_t *pool; /**< Pool for each instance. */ + unsigned samples_per_frame; + pj_uint8_t pcm_bytes_per_sample; + + pj_bool_t enc_ready; + SKP_SILK_SDK_EncControlStruct enc_ctl; + void *enc_st; + + pj_bool_t dec_ready; + SKP_SILK_SDK_DecControlStruct dec_ctl; + void *dec_st; + + /* Buffer to hold decoded frames. */ + void *dec_buf[SILK_MAX_FRAMES_PER_PACKET-1]; + SKP_int16 dec_buf_size[SILK_MAX_FRAMES_PER_PACKET-1]; + pj_size_t dec_buf_sz; + unsigned dec_buf_cnt; + pj_uint32_t pkt_info; /**< Packet info for buffered frames. */ +} silk_private; + + +silk_mode silk_get_mode_from_clock_rate(unsigned clock_rate) { + if (clock_rate <= silk_factory.silk_param[PARAM_NB].clock_rate) { + return PARAM_NB; + } else if (clock_rate <= silk_factory.silk_param[PARAM_MB].clock_rate) { + return PARAM_MB; + } else if (clock_rate <= silk_factory.silk_param[PARAM_WB].clock_rate) { + return PARAM_WB; + } + return PARAM_SWB; +} + + +PJ_DEF(pj_status_t) pjmedia_codec_silk_init(pjmedia_endpt *endpt) +{ + pjmedia_codec_mgr *codec_mgr; + silk_param *sp; + pj_status_t status; + + if (silk_factory.endpt != NULL) { + /* Already initialized. */ + return PJ_SUCCESS; + } + + /* Init factory */ + pj_bzero(&silk_factory, sizeof(silk_factory)); + silk_factory.base.op = &silk_factory_op; + silk_factory.base.factory_data = NULL; + silk_factory.endpt = endpt; + + /* Create pool */ + silk_factory.pool = pjmedia_endpt_create_pool(endpt, "silk", 4000, 4000); + if (!silk_factory.pool) + return PJ_ENOMEM; + + /* Create mutex. */ + status = pj_mutex_create_simple(silk_factory.pool, "silk", + &silk_factory.mutex); + if (status != PJ_SUCCESS) + goto on_error; + + /* Initialize default codec params */ + + /* From SILK docs: + - SILK bitrate tables: + +----------------+---------+-----------+ + | | fs (Hz) | BR (kbps) | + +----------------+---------+-----------+ + | Narrowband | 8000 | 6 - 20 | + | Mediumband | 12000 | 7 - 25 | + | Wideband | 16000 | 8 - 30 | + | Super Wideband | 24000 | 12 - 40 | + +----------------+---------+-----------+ + - The upper limits of the bit rate ranges in this table are + recommended values. + */ + + sp = &silk_factory.silk_param[PARAM_NB]; + sp->pt = PJMEDIA_RTP_PT_SILK_NB; + sp->clock_rate = 8000; + sp->max_bitrate = 22000; + sp->bitrate = CALC_BITRATE(sp->max_bitrate); + sp->ptime = FRAME_LENGTH_MS; + sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY; + sp->enabled = 1; + + sp = &silk_factory.silk_param[PARAM_MB]; + sp->pt = PJMEDIA_RTP_PT_SILK_MB; + sp->clock_rate = 12000; + sp->max_bitrate = 28000; + sp->bitrate = CALC_BITRATE(sp->max_bitrate); + sp->ptime = FRAME_LENGTH_MS; + sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY; + sp->enabled = 0; + + sp = &silk_factory.silk_param[PARAM_WB]; + sp->pt = PJMEDIA_RTP_PT_SILK_WB; + sp->clock_rate = 16000; + sp->max_bitrate = 36000; + sp->bitrate = CALC_BITRATE(sp->max_bitrate); + sp->ptime = FRAME_LENGTH_MS; + sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY; + sp->enabled = 1; + + sp = &silk_factory.silk_param[PARAM_SWB]; + sp->pt = PJMEDIA_RTP_PT_SILK_SWB; + sp->clock_rate = 24000; + sp->max_bitrate = 46000; + sp->bitrate = CALC_BITRATE(sp->max_bitrate); + sp->ptime = FRAME_LENGTH_MS; + sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY; + sp->enabled = 0; + + + /* Get the codec manager. */ + codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); + if (!codec_mgr) { + return PJ_EINVALIDOP; + } + + /* Register codec factory to endpoint. */ + status = pjmedia_codec_mgr_register_factory(codec_mgr, + &silk_factory.base); + if (status != PJ_SUCCESS) + return status; + + PJ_LOG(4,(THIS_FILE, "SILK codec version %s initialized", + SKP_Silk_SDK_get_version())); + return PJ_SUCCESS; + +on_error: + if (silk_factory.mutex) { + pj_mutex_destroy(silk_factory.mutex); + silk_factory.mutex = NULL; + } + if (silk_factory.pool) { + pj_pool_release(silk_factory.pool); + silk_factory.pool = NULL; + } + + return status; +} + + +/* + * Change the configuration setting of the SILK codec for the specified + * clock rate. + */ +PJ_DEF(pj_status_t) pjmedia_codec_silk_set_config( + unsigned clock_rate, + const pjmedia_codec_silk_setting *opt) +{ + unsigned i; + + /* Look up in factory modes table */ + for (i = 0; i < sizeof(silk_factory.silk_param)/ + sizeof(silk_factory.silk_param[0]); ++i) + { + if (silk_factory.silk_param[i].clock_rate == clock_rate) { + int quality = PJMEDIA_CODEC_SILK_DEFAULT_QUALITY; + int complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY; + + silk_factory.silk_param[i].enabled = opt->enabled; + if (opt->complexity >= 0) + complexity = opt->complexity; + silk_factory.silk_param[i].complexity = complexity; + if (opt->quality >= 0) + quality = opt->quality; + silk_factory.silk_param[i].bitrate = + CALC_BITRATE_QUALITY(quality, + silk_factory.silk_param[i].max_bitrate); + if (silk_factory.silk_param[i].bitrate < SILK_MIN_BITRATE) + silk_factory.silk_param[i].bitrate = SILK_MIN_BITRATE; + + return PJ_SUCCESS; + } + } + + return PJ_ENOTFOUND; +} + + +/* + * Unregister SILK codec factory from pjmedia endpoint and deinitialize + * the SILK codec library. + */ +PJ_DEF(pj_status_t) pjmedia_codec_silk_deinit(void) +{ + pjmedia_codec_mgr *codec_mgr; + pj_status_t status; + + if (silk_factory.endpt == NULL) { + /* Not registered. */ + return PJ_SUCCESS; + } + + /* Lock mutex. */ + pj_mutex_lock(silk_factory.mutex); + + /* Get the codec manager. */ + codec_mgr = pjmedia_endpt_get_codec_mgr(silk_factory.endpt); + if (!codec_mgr) { + silk_factory.endpt = NULL; + pj_mutex_unlock(silk_factory.mutex); + return PJ_EINVALIDOP; + } + + /* Unregister silk codec factory. */ + status = pjmedia_codec_mgr_unregister_factory(codec_mgr, + &silk_factory.base); + silk_factory.endpt = NULL; + + /* Destroy mutex. */ + pj_mutex_destroy(silk_factory.mutex); + silk_factory.mutex = NULL; + + + /* Release pool. */ + pj_pool_release(silk_factory.pool); + silk_factory.pool = NULL; + + return status; +} + + +/* + * Check if factory can allocate the specified codec. + */ +static pj_status_t silk_test_alloc(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info ) +{ + const pj_str_t silk_tag = {"SILK", 4}; + unsigned i; + + PJ_UNUSED_ARG(factory); + PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL); + + /* Type MUST be audio. */ + if (info->type != PJMEDIA_TYPE_AUDIO) + return PJMEDIA_CODEC_EUNSUP; + + /* Check encoding name. */ + if (pj_stricmp(&info->encoding_name, &silk_tag) != 0) + return PJMEDIA_CODEC_EUNSUP; + + /* Channel count must be one */ + if (info->channel_cnt != 1) + return PJMEDIA_CODEC_EUNSUP; + + /* Check clock-rate */ + for (i=0; i<PJ_ARRAY_SIZE(silk_factory.silk_param); ++i) { + silk_param *sp = &silk_factory.silk_param[i]; + if (sp->enabled && info->clock_rate == sp->clock_rate) + { + return PJ_SUCCESS; + } + } + /* Clock rate not supported */ + return PJMEDIA_CODEC_EUNSUP; +} + + +/* + * Generate default attribute. + */ +static pj_status_t silk_default_attr( pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec_param *attr ) +{ + silk_param *sp; + int i; + + PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL); + + i = silk_get_mode_from_clock_rate(id->clock_rate); + pj_assert(i >= PARAM_NB && i <= PARAM_SWB); + + sp = &silk_factory.silk_param[i]; + + pj_bzero(attr, sizeof(pjmedia_codec_param)); + attr->info.channel_cnt = 1; + attr->info.clock_rate = sp->clock_rate; + attr->info.avg_bps = sp->bitrate; + attr->info.max_bps = sp->max_bitrate; + attr->info.frm_ptime = sp->ptime; + attr->info.pcm_bits_per_sample = 16; + attr->info.pt = (pj_uint8_t) sp->pt; + attr->setting.frm_per_pkt = 1; + attr->setting.vad = 0; /* DTX is not recommended for quality reason */ + attr->setting.plc = 1; + + i = 0; + attr->setting.dec_fmtp.param[i].name = pj_str("useinbandfec"); + attr->setting.dec_fmtp.param[i++].val = pj_str("0"); + /* + attr->setting.dec_fmtp.param[i].name = pj_str("maxaveragebitrate"); + attr->setting.dec_fmtp.param[i++].val = pj_str(mode->bitrate_str); + */ + attr->setting.dec_fmtp.cnt = (pj_uint8_t)i; + + return PJ_SUCCESS; +} + + +/* + * Enum codecs supported by this factory. + */ +static pj_status_t silk_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]) +{ + unsigned max; + int i; + + PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL); + PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); + + max = *count; + *count = 0; + + for (i = 0; i<PJ_ARRAY_SIZE(silk_factory.silk_param) && *count<max; ++i) + { + silk_param *sp = &silk_factory.silk_param[i]; + + if (!sp->enabled) + continue; + + pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); + codecs[*count].encoding_name = pj_str("SILK"); + codecs[*count].pt = sp->pt; + codecs[*count].type = PJMEDIA_TYPE_AUDIO; + codecs[*count].clock_rate = sp->clock_rate; + codecs[*count].channel_cnt = 1; + + ++*count; + } + + return PJ_SUCCESS; +} + + +/* + * Allocate a new SILK codec instance. + */ +static pj_status_t silk_alloc_codec(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec) +{ + pj_pool_t *pool; + pjmedia_codec *codec; + silk_private *silk; + + PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); + PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL); + + /* Create pool for codec instance */ + pool = pjmedia_endpt_create_pool(silk_factory.endpt, "silk", 512, 512); + + /* Allocate codec */ + codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); + codec->op = &silk_op; + codec->factory = factory; + codec->codec_data = PJ_POOL_ZALLOC_T(pool, silk_private); + silk = (silk_private*) codec->codec_data; + silk->pool = pool; + silk->enc_ready = PJ_FALSE; + silk->dec_ready = PJ_FALSE; + silk->mode = silk_get_mode_from_clock_rate(id->clock_rate); + + *p_codec = codec; + return PJ_SUCCESS; +} + + +/* + * Free codec. + */ +static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory, + pjmedia_codec *codec ) +{ + silk_private *silk; + + PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); + PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL); + + silk = (silk_private*)codec->codec_data; + + /* Close codec, if it's not closed. */ + if (silk->enc_ready == PJ_TRUE || silk->dec_ready == PJ_TRUE) { + silk_codec_close(codec); + } + + pj_pool_release(silk->pool); + + return PJ_SUCCESS; +} + + +/* + * Init codec. + */ +static pj_status_t silk_codec_init(pjmedia_codec *codec, + pj_pool_t *pool ) +{ + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(pool); + return PJ_SUCCESS; +} + + +/* + * Open codec. + */ +static pj_status_t silk_codec_open(pjmedia_codec *codec, + pjmedia_codec_param *attr ) +{ + + silk_private *silk; + silk_param *sp; + SKP_int st_size, err; + pj_bool_t enc_use_fec; + unsigned enc_bitrate, i; + + PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); + + silk = (silk_private*)codec->codec_data; + sp = &silk_factory.silk_param[silk->mode]; + + /* Already opened? */ + if (silk->enc_ready || silk->dec_ready) + return PJ_SUCCESS; + + /* Allocate and initialize encoder */ + err = SKP_Silk_SDK_Get_Encoder_Size(&st_size); + if (err) { + PJ_LOG(3,(THIS_FILE, "Failed to get encoder state size (err=%d)", + err)); + return PJMEDIA_CODEC_EFAILED; + } + silk->enc_st = pj_pool_zalloc(silk->pool, st_size); + err = SKP_Silk_SDK_InitEncoder(silk->enc_st, &silk->enc_ctl); + if (err) { + PJ_LOG(3,(THIS_FILE, "Failed to init encoder (err=%d)", err)); + return PJMEDIA_CODEC_EFAILED; + } + + /* Check fmtp params */ + enc_use_fec = PJ_TRUE; + enc_bitrate = sp->bitrate; + for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { + pjmedia_codec_fmtp *fmtp = &attr->setting.enc_fmtp; + const pj_str_t STR_USEINBANDFEC = {"useinbandfec", 12}; + const pj_str_t STR_MAXAVERAGEBITRATE = {"maxaveragebitrate", 17}; + + if (!pj_stricmp(&fmtp->param[i].name, &STR_USEINBANDFEC)) { + enc_use_fec = pj_strtoul(&fmtp->param[i].val) != 0; + } else if (!pj_stricmp(&fmtp->param[i].name, &STR_MAXAVERAGEBITRATE)) { + enc_bitrate = pj_strtoul(&fmtp->param[i].val); + if (enc_bitrate > sp->max_bitrate) { + enc_bitrate = sp->max_bitrate; + } + } + } + + /* Setup encoder control for encoding process */ + silk->enc_ready = PJ_TRUE; + silk->samples_per_frame = FRAME_LENGTH_MS * + attr->info.clock_rate / 1000; + silk->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8; + + silk->enc_ctl.API_sampleRate = attr->info.clock_rate; + silk->enc_ctl.maxInternalSampleRate = attr->info.clock_rate; + silk->enc_ctl.packetSize = attr->setting.frm_per_pkt * + silk->samples_per_frame; + /* For useInBandFEC setting to be useful, we need to set + * packetLossPercentage greater than LBRR_LOSS_THRES (1) + */ + silk->enc_ctl.packetLossPercentage = SILK_ENC_CTL_PACKET_LOSS_PCT; + silk->enc_ctl.useInBandFEC = enc_use_fec; + silk->enc_ctl.useDTX = attr->setting.vad; + silk->enc_ctl.complexity = sp->complexity; + silk->enc_ctl.bitRate = enc_bitrate; + + + /* Allocate and initialize decoder */ + err = SKP_Silk_SDK_Get_Decoder_Size(&st_size); + if (err) { + PJ_LOG(3,(THIS_FILE, "Failed to get decoder state size (err=%d)", + err)); + return PJMEDIA_CODEC_EFAILED; + } + silk->dec_st = pj_pool_zalloc(silk->pool, st_size); + err = SKP_Silk_SDK_InitDecoder(silk->dec_st); + if (err) { + PJ_LOG(3,(THIS_FILE, "Failed to init decoder (err=%d)", err)); + return PJMEDIA_CODEC_EFAILED; + } + + /* Setup decoder control for decoding process */ + silk->dec_ctl.API_sampleRate = attr->info.clock_rate; + silk->dec_ctl.framesPerPacket = 1; /* for proper PLC at start */ + silk->dec_ready = PJ_TRUE; + silk->dec_buf_sz = attr->info.clock_rate * attr->info.channel_cnt * + attr->info.frm_ptime / 1000 * + silk->pcm_bytes_per_sample; + + /* Inform the stream to prepare a larger buffer since we cannot parse + * SILK packets and split it into individual frames. + */ + attr->info.max_rx_frame_size = attr->info.max_bps * + attr->info.frm_ptime / 8 / 1000; + if ((attr->info.max_bps * attr->info.frm_ptime) % 8000 != 0) + { + ++attr->info.max_rx_frame_size; + } + attr->info.max_rx_frame_size *= SILK_MAX_FRAMES_PER_PACKET; + + return PJ_SUCCESS; +} + + +/* + * Close codec. + */ +static pj_status_t silk_codec_close( pjmedia_codec *codec ) +{ + silk_private *silk; + silk = (silk_private*)codec->codec_data; + + silk->enc_ready = PJ_FALSE; + silk->dec_ready = PJ_FALSE; + + return PJ_SUCCESS; +} + + +/* + * Modify codec settings. + */ +static pj_status_t silk_codec_modify(pjmedia_codec *codec, + const pjmedia_codec_param *attr ) +{ + PJ_TODO(implement_silk_codec_modify); + + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(attr); + + return PJ_SUCCESS; +} + + +/* + * Encode frame. + */ +static pj_status_t silk_codec_encode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + silk_private *silk; + SKP_int err; + unsigned nsamples; + SKP_int16 out_size; + + PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL); + silk = (silk_private*)codec->codec_data; + + /* Check frame in size */ + nsamples = input->size >> 1; + PJ_ASSERT_RETURN(nsamples % silk->samples_per_frame == 0, + PJMEDIA_CODEC_EPCMFRMINLEN); + + /* Encode */ + output->size = 0; + out_size = (SKP_int16)output_buf_len; + err = SKP_Silk_SDK_Encode(silk->enc_st, &silk->enc_ctl, + (SKP_int16*)input->buf, nsamples, + (SKP_uint8*)output->buf, &out_size); + if (err) { + PJ_LOG(3, (THIS_FILE, "Failed to encode frame (err=%d)", err)); + output->type = PJMEDIA_FRAME_TYPE_NONE; + if (err == SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT) + return PJMEDIA_CODEC_EFRMTOOSHORT; + return PJMEDIA_CODEC_EFAILED; + } + + output->size = out_size; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + output->timestamp = input->timestamp; + + return PJ_SUCCESS; +} + + +/* + * Get frames in the packet. + */ +static pj_status_t silk_codec_parse( pjmedia_codec *codec, + void *pkt, + pj_size_t pkt_size, + const pj_timestamp *ts, + unsigned *frame_cnt, + pjmedia_frame frames[]) +{ + silk_private *silk; + SKP_Silk_TOC_struct toc; + unsigned i, count; + + PJ_ASSERT_RETURN(codec && ts && frames && frame_cnt, PJ_EINVAL); + silk = (silk_private*)codec->codec_data; + + SKP_Silk_SDK_get_TOC(pkt, pkt_size, &toc); + count = toc.framesInPacket; + pj_assert(count <= SILK_MAX_FRAMES_PER_PACKET); + + for (i = 0; i < count; i++) { + frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO; + frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) | + (((unsigned)pkt & 0xFF) << 8) | + (toc.framesInPacket << 4) | i; + frames[i].buf = pkt; + frames[i].size = pkt_size; + frames[i].timestamp.u64 = ts->u64 + i * silk->samples_per_frame; + } + + *frame_cnt = count; + return PJ_SUCCESS; +} + +static pj_status_t silk_codec_decode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + silk_private *silk; + SKP_int16 out_size; + SKP_Silk_TOC_struct toc; + SKP_int err = 0; + unsigned pkt_info, frm_info; + + PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL); + silk = (silk_private*)codec->codec_data; + PJ_ASSERT_RETURN(output_buf_len >= silk->dec_buf_sz, PJ_ETOOSMALL); + + SKP_Silk_SDK_get_TOC(input->buf, input->size, &toc); + pkt_info = input->bit_info & 0xFFFFFF00; + frm_info = input->bit_info & 0xF; + + if (toc.framesInPacket == 0) { + /* In SILK ARM version, the table of content can indicate + * that the number of frames in the packet is 0. + * Try to get the number of frames in packet that we save + * in the frame instead. + */ + toc.framesInPacket = (input->bit_info & 0xF0) >> 4; + } + + if (toc.framesInPacket == 0) { + output->size = 0; + } else if (silk->pkt_info != pkt_info || input->bit_info == 0) { + unsigned i; + SKP_int16 nsamples; + + silk->pkt_info = pkt_info; + nsamples = (SKP_int16)silk->dec_buf_sz / silk->pcm_bytes_per_sample; + + if (toc.framesInPacket-1 > (SKP_int)silk->dec_buf_cnt) { + /* Grow the buffer */ + for (i = silk->dec_buf_cnt+1; i < (unsigned)toc.framesInPacket; + i++) + { + silk->dec_buf[i-1] = pj_pool_alloc(silk->pool, + silk->dec_buf_sz); + } + silk->dec_buf_cnt = toc.framesInPacket-1; + } + + /* We need to decode all the frames in the packet. */ + for (i = 0; i < (unsigned)toc.framesInPacket;) { + void *buf; + SKP_int16 *size; + + if (i == 0 || i == frm_info) { + buf = output->buf; + size = &out_size; + } else { + buf = silk->dec_buf[i-1]; + size = &silk->dec_buf_size[i-1]; + } + + *size = nsamples; + err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl, + 0, /* Normal frame flag */ + input->buf, input->size, + buf, size); + if (err) { + PJ_LOG(3, (THIS_FILE, "Failed to decode frame (err=%d)", + err)); + *size = 0; + } else { + *size = *size * silk->pcm_bytes_per_sample; + } + + if (i == frm_info) { + output->size = *size; + } + + i++; + if (!silk->dec_ctl.moreInternalDecoderFrames && + i < (unsigned)toc.framesInPacket) + { + /* It turns out that the packet does not have + * the number of frames as mentioned in the TOC. + */ + for (; i < (unsigned)toc.framesInPacket; i++) { + silk->dec_buf_size[i-1] = 0; + if (i == frm_info) { + output->size = 0; + } + } + } + } + } else { + /* We have already decoded this packet. */ + if (frm_info == 0 || silk->dec_buf_size[frm_info-1] == 0) { + /* The decoding was a failure. */ + output->size = 0; + } else { + /* Copy the decoded frame from the buffer. */ + pj_assert(frm_info-1 < silk->dec_buf_cnt); + if (frm_info-1 >= silk->dec_buf_cnt) { + output->size = 0; + } else { + pj_memcpy(output->buf, silk->dec_buf[frm_info-1], + silk->dec_buf_size[frm_info-1]); + output->size = silk->dec_buf_size[frm_info-1]; + } + } + } + + if (output->size == 0) { + output->type = PJMEDIA_TYPE_NONE; + output->buf = NULL; + return PJMEDIA_CODEC_EFAILED; + } + + output->type = PJMEDIA_TYPE_AUDIO; + output->timestamp = input->timestamp; + + return PJ_SUCCESS; +} + + +/* + * Recover lost frame. + */ +static pj_status_t silk_codec_recover(pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + silk_private *silk; + SKP_int16 out_size; + SKP_int err; + + PJ_ASSERT_RETURN(codec && output_buf_len && output, PJ_EINVAL); + silk = (silk_private*)codec->codec_data; + + out_size = (SKP_int16)output_buf_len / silk->pcm_bytes_per_sample; + err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl, + 1, /* Lost frame flag */ + NULL, + 0, + output->buf, + &out_size); + if (err) { + PJ_LOG(3, (THIS_FILE, "Failed to conceal lost frame (err=%d)", err)); + output->type = PJMEDIA_FRAME_TYPE_NONE; + output->buf = NULL; + output->size = 0; + return PJMEDIA_CODEC_EFAILED; + } + + output->size = out_size * silk->pcm_bytes_per_sample; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + + return PJ_SUCCESS; +} + +#if defined(_MSC_VER) +# if PJ_DEBUG +# pragma comment(lib, "SKP_Silk_FLP_Win32_debug.lib") +# else +# pragma comment(lib, "SKP_Silk_FLP_Win32_mt.lib") +# endif +#endif + + +#endif /* PJMEDIA_HAS_SILK_CODEC */ diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c index 6fb7c0b..f05d94a 100644 --- a/pjmedia/src/pjmedia-videodev/dshow_dev.c +++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c @@ -1,4 +1,4 @@ -/* $Id: dshow_dev.c 3953 2012-02-16 08:49:33Z ming $ */ +/* $Id: dshow_dev.c 4253 2012-09-13 08:35:24Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * @@ -33,6 +33,7 @@ #include <windows.h> #define COBJMACROS #include <DShow.h> +#include <wmsdkidl.h> #ifdef _MSC_VER # pragma warning(pop) @@ -64,14 +65,16 @@ typedef struct dshow_fmt_info { pjmedia_format_id pjmedia_format; const GUID *dshow_format; + pj_bool_t enabled; } dshow_fmt_info; static dshow_fmt_info dshow_fmts[] = { - {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} , - {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} , - {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} , - {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} , + {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2, PJ_FALSE} , + {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24, PJ_FALSE} , + {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32, PJ_FALSE} , + {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV, PJ_FALSE} , + {PJMEDIA_FORMAT_I420, &WMMEDIASUBTYPE_I420, PJ_FALSE} }; /* dshow_ device info */ @@ -329,6 +332,8 @@ static void enum_dev_cap(IBaseFilter *filter, &rpcstatus2) == 0 && rpcstatus2 == RPC_S_OK) { + if (!dshow_fmt) + dshow_fmts[j].enabled = PJ_TRUE; if (sup_fmt) sup_fmt[j] = PJ_TRUE; if (pSrcpin) { @@ -640,7 +645,7 @@ static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id) unsigned i; for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) { - if (dshow_fmts[i].pjmedia_format == id) + if (dshow_fmts[i].pjmedia_format == id && dshow_fmts[i].enabled) return &dshow_fmts[i]; } diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c index 7e40da1..9c0944b 100644 --- a/pjmedia/src/pjmedia-videodev/sdl_dev.c +++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c @@ -1,4 +1,4 @@ -/* $Id: sdl_dev.c 4157 2012-06-06 09:37:25Z nanang $ */ +/* $Id: sdl_dev.c 4414 2013-03-05 08:21:02Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * @@ -1418,7 +1418,9 @@ static pj_status_t job_queue_destroy(job_queue *jq) } #ifdef _MSC_VER -# if SDL_VERSION_ATLEAST(2,0,0) +# if defined(PJMEDIA_SDL_LIB) +# pragma comment( lib, PJMEDIA_SDL_LIB) +# elif SDL_VERSION_ATLEAST(2,0,0) # pragma comment( lib, "sdl2.lib") # elif SDL_VERSION_ATLEAST(1,3,0) # pragma comment( lib, "sdl.lib") diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c index 880d47b..5b9c5e0 100644 --- a/pjmedia/src/pjmedia-videodev/v4l2_dev.c +++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c @@ -1,4 +1,4 @@ -/* $Id: v4l2_dev.c 3901 2011-12-07 10:43:28Z nanang $ */ +/* $Id: v4l2_dev.c 4310 2012-12-19 05:38:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * @@ -670,6 +670,7 @@ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream, struct v4l2_buffer buf; pj_time_val time; pj_status_t status = PJ_SUCCESS; + unsigned tmp_idx; pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -697,9 +698,11 @@ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream, pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused); on_return: + tmp_idx = buf.index; pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; + buf.index = tmp_idx; xioctl(stream->fd, VIDIOC_QBUF, &buf); return status; diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c index db8cad2..5107c8c 100644 --- a/pjmedia/src/pjmedia/codec.c +++ b/pjmedia/src/pjmedia/codec.c @@ -1,4 +1,4 @@ -/* $Id: codec.c 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: codec.c 4254 2012-09-14 04:06:29Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -41,6 +41,39 @@ static void sort_codecs(pjmedia_codec_mgr *mgr); /* + * Duplicate codec parameter. + */ +PJ_DEF(pjmedia_codec_param*) pjmedia_codec_param_clone( + pj_pool_t *pool, + const pjmedia_codec_param *src) +{ + pjmedia_codec_param *p; + unsigned i; + + PJ_ASSERT_RETURN(pool && src, NULL); + + p = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param); + + /* Update codec param */ + pj_memcpy(p, src, sizeof(pjmedia_codec_param)); + for (i = 0; i < src->setting.dec_fmtp.cnt; ++i) { + pj_strdup(pool, &p->setting.dec_fmtp.param[i].name, + &src->setting.dec_fmtp.param[i].name); + pj_strdup(pool, &p->setting.dec_fmtp.param[i].val, + &src->setting.dec_fmtp.param[i].val); + } + for (i = 0; i < src->setting.enc_fmtp.cnt; ++i) { + pj_strdup(pool, &p->setting.enc_fmtp.param[i].name, + &src->setting.enc_fmtp.param[i].name); + pj_strdup(pool, &p->setting.enc_fmtp.param[i].val, + &src->setting.enc_fmtp.param[i].val); + } + + return p; +} + + +/* * Initialize codec manager. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr, @@ -600,22 +633,11 @@ PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_default_param( codec_desc->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_default_param); p = codec_desc->param; p->pool = pool; - p->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param); /* Update codec param */ - pj_memcpy(p->param, param, sizeof(pjmedia_codec_param)); - for (i = 0; i < param->setting.dec_fmtp.cnt; ++i) { - pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].name, - ¶m->setting.dec_fmtp.param[i].name); - pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].val, - ¶m->setting.dec_fmtp.param[i].val); - } - for (i = 0; i < param->setting.enc_fmtp.cnt; ++i) { - pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].name, - ¶m->setting.enc_fmtp.param[i].name); - pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].val, - ¶m->setting.enc_fmtp.param[i].val); - } + p->param = pjmedia_codec_param_clone(pool, param); + if (!p->param) + return PJ_EINVAL; pj_mutex_unlock(mgr->mutex); diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index a08e7b0..391e4e3 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -1,4 +1,4 @@ -/* $Id: conference.c 4162 2012-06-11 04:17:54Z nanang $ */ +/* $Id: conference.c 4198 2012-07-05 10:25:46Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -1810,11 +1810,11 @@ static pj_status_t get_frame(pjmedia_port *this_port, /* Var "ci" is to count how many ports have been visited so far. */ ++ci; - /* Reset buffer (only necessary if more than one transmitter) and + /* Reset buffer (only necessary if the port has transmitter) and * reset auto adjustment level for mixed signal. */ conf_port->mix_adj = NORMAL_LEVEL; - if (conf_port->transmitter_cnt > 1) { + if (conf_port->transmitter_cnt) { pj_bzero(conf_port->mix_buf, conf->samples_per_frame*sizeof(conf_port->mix_buf[0])); } diff --git a/pjmedia/src/pjmedia/converter.c b/pjmedia/src/pjmedia/converter.c index 50fbcc8..19a8a8c 100644 --- a/pjmedia/src/pjmedia/converter.c +++ b/pjmedia/src/pjmedia/converter.c @@ -1,4 +1,4 @@ -/* $Id: converter.c 4087 2012-04-26 03:39:24Z ming $ */ +/* $Id: converter.c 4412 2013-03-05 03:12:32Z riza $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * @@ -39,7 +39,9 @@ PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **p_mgr) { pjmedia_converter_mgr *mgr; +#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL pj_status_t status = PJ_SUCCESS; +#endif mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr); pj_list_init(&mgr->factory_list); diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index e20a5a4..b61152e 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -1,4 +1,4 @@ -/* $Id: endpoint.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: endpoint.c 4240 2012-08-31 09:03:36Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -49,6 +49,10 @@ static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; pj_bool_t pjmedia_add_rtpmap_for_static_pt = PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT; +/* Config to control use of RFC3890 TIAS */ +pj_bool_t pjmedia_add_bandwidth_tias_in_sdp = + PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP; + /* Worker thread proc. */ @@ -551,7 +555,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ - if (max_bitrate) { + if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; @@ -715,7 +719,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ - if (max_bitrate) { + if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c index 0a61a47..2e3ec7b 100644 --- a/pjmedia/src/pjmedia/g711.c +++ b/pjmedia/src/pjmedia/g711.c @@ -1,4 +1,4 @@ -/* $Id: g711.c 3664 2011-07-19 03:42:28Z nanang $ */ +/* $Id: g711.c 4266 2012-09-26 05:55:18Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -331,7 +331,8 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, #if !PLC_DISABLED /* Create PLC, always with 10ms ptime */ - status = pjmedia_plc_create(g711_factory.pool, 8000, 80, + status = pjmedia_plc_create(g711_factory.pool, 8000, + SAMPLES_PER_FRAME, 0, &codec_priv->plc); if (status != PJ_SUCCESS) { pj_mutex_unlock(g711_factory.mutex); @@ -341,7 +342,7 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, /* Create VAD */ status = pjmedia_silence_det_create(g711_factory.pool, - 8000, 80, + 8000, SAMPLES_PER_FRAME, &codec_priv->vad); if (status != PJ_SUCCESS) { pj_mutex_unlock(g711_factory.mutex); diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 74f6469..8c54074 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -1,4 +1,4 @@ -/* $Id: jbuf.c 4100 2012-04-26 16:57:47Z nanang $ */ +/* $Id: jbuf.c 4369 2013-02-21 21:55:54Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -167,7 +167,7 @@ struct pjmedia_jbuf /* Enabling this would log the jitter buffer state about once per * second. */ -#if 1 +#if 0 # define TRACE__(args) PJ_LOG(5,args) #else # define TRACE__(args) @@ -599,6 +599,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb, jb->jb_min_prefetch = jb->jb_max_prefetch = jb->jb_prefetch = jb->jb_init_prefetch = prefetch; + pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_NONE); return PJ_SUCCESS; } @@ -724,6 +725,8 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; + if (jb->jb_prefetch > jb->jb_max_prefetch) + jb->jb_prefetch = jb->jb_max_prefetch; } /* Reset history */ @@ -747,6 +750,8 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch > jb->jb_max_prefetch) jb->jb_prefetch = jb->jb_max_prefetch; + if (jb->jb_prefetch < jb->jb_min_prefetch) + jb->jb_prefetch = jb->jb_min_prefetch; } jb->jb_stable_hist = 0; diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c index f57797c..fbfae13 100644 --- a/pjmedia/src/pjmedia/rtcp.c +++ b/pjmedia/src/pjmedia/rtcp.c @@ -1,4 +1,4 @@ -/* $Id: rtcp.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: rtcp.c 4283 2012-10-12 06:19:32Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -752,7 +752,8 @@ static void parse_rtcp_bye(pjmedia_rtcp_session *sess, /* Check and get BYE reason */ if (size > 8) { - reason.slen = *((pj_uint8_t*)pkt+8); + reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_), + *((pj_uint8_t*)pkt+8)); pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9), reason.slen); reason.ptr = sess->stat.peer_sdes_buf_; diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c index 7fe6f89..c9ea53c 100644 --- a/pjmedia/src/pjmedia/rtp.c +++ b/pjmedia/src/pjmedia/rtp.c @@ -1,4 +1,4 @@ -/* $Id: rtp.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* $Id: rtp.c 4235 2012-08-24 03:15:42Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -118,8 +118,6 @@ PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, int payload_len, int ts_len, const void **rtphdr, int *hdrlen ) { - PJ_UNUSED_ARG(payload_len); - /* Update timestamp */ ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len); diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index e54bfcf..c0d43d8 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -1,4 +1,4 @@ -/* $Id: sdp.c 3945 2012-01-27 09:12:59Z nanang $ */ +/* $Id: sdp.c 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -1423,6 +1423,14 @@ static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c) /* Validate SDP session descriptor. */ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp) { + return pjmedia_sdp_validate2(sdp, PJ_TRUE); +} + + +/* Validate SDP session descriptor. */ +PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp, + pj_bool_t strict) +{ unsigned i; const pj_str_t STR_RTPMAP = { "rtpmap", 6 }; @@ -1471,7 +1479,8 @@ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp) */ if (m->conn == NULL) { if (sdp->conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; + if (strict || m->desc.port != 0) + return PJMEDIA_SDP_EMISSINGCONN; } /* Verify payload type. */ @@ -1505,6 +1514,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp) return PJ_SUCCESS; } + PJ_DEF(pj_status_t) pjmedia_sdp_transport_cmp( const pj_str_t *t1, const pj_str_t *t2) { diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c index bdce18d..61e6d93 100644 --- a/pjmedia/src/pjmedia/sdp_neg.c +++ b/pjmedia/src/pjmedia/sdp_neg.c @@ -1,4 +1,4 @@ -/* $Id: sdp_neg.c 3980 2012-03-20 09:23:20Z ming $ */ +/* $Id: sdp_neg.c 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -138,7 +138,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, *p_neg = NULL; /* Validate remote offer and initial answer */ - status = pjmedia_sdp_validate(remote); + status = pjmedia_sdp_validate2(remote, PJ_FALSE); if (status != PJ_SUCCESS) return status; diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 5c7c873..840b06c 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1,4 +1,4 @@ -/* $Id: stream.c 4120 2012-05-12 07:18:09Z ming $ */ +/* $Id: stream.c 4336 2013-01-29 08:15:02Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -73,6 +73,8 @@ # define PJMEDIA_STREAM_INC 1000 #endif +/* Number of DTMF E bit transmissions */ +#define DTMF_EBIT_RETRANSMIT_CNT 3 /** * Media channel. @@ -94,6 +96,7 @@ struct dtmf { int event; pj_uint32_t duration; + int ebit_cnt; /**< # of E bit transmissions */ }; /** @@ -876,7 +879,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) */ static void create_dtmf_payload(pjmedia_stream *stream, struct pjmedia_frame *frame_out, - int *first, int *last) + int forced_last, int *first, int *last) { pjmedia_rtp_dtmf_event *event; struct dtmf *digit = &stream->tx_dtmf_buf[0]; @@ -896,25 +899,33 @@ static void create_dtmf_payload(pjmedia_stream *stream, } digit->duration += PJMEDIA_PIA_SPF(&stream->port.info); + if (digit->duration >= PJMEDIA_DTMF_DURATION) + digit->duration = PJMEDIA_DTMF_DURATION; event->event = (pj_uint8_t)digit->event; event->e_vol = 10; event->duration = pj_htons((pj_uint16_t)digit->duration); + if (forced_last) { + digit->duration = PJMEDIA_DTMF_DURATION; + } if (digit->duration >= PJMEDIA_DTMF_DURATION) { event->e_vol |= 0x80; - *last = 1; - /* Prepare next digit. */ - pj_mutex_lock(stream->jb_mutex); + if (++digit->ebit_cnt >= DTMF_EBIT_RETRANSMIT_CNT) { + *last = 1; - pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), - stream->tx_dtmf_count, 0); - --stream->tx_dtmf_count; + /* Prepare next digit. */ + pj_mutex_lock(stream->jb_mutex); - pj_mutex_unlock(stream->jb_mutex); + pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), + stream->tx_dtmf_count, 0); + --stream->tx_dtmf_count; + + pj_mutex_unlock(stream->jb_mutex); + } } frame_out->size = 4; @@ -1217,7 +1228,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, if (stream->tx_dtmf_count) { int first=0, last=0; - create_dtmf_payload(stream, &frame_out, &first, &last); + create_dtmf_payload(stream, &frame_out, 0, &first, &last); /* Encapsulate into RTP packet. Note that: * - RTP marker should be set on the beginning of a new event @@ -1235,7 +1246,9 @@ static pj_status_t put_frame_imp( pjmedia_port *port, * Increment the RTP timestamp of the RTP session, for next * RTP packets. */ - inc_timestamp = PJMEDIA_DTMF_DURATION - rtp_ts_len; + inc_timestamp = PJMEDIA_DTMF_DURATION + + ((DTMF_EBIT_RETRANSMIT_CNT-1) * samples_per_frame) + - rtp_ts_len; } @@ -1765,7 +1778,11 @@ static void on_rx_rtp( void *data, peer_frm_ts_diff == (frm_ts_span>>1))) { if (peer_frm_ts_diff < stream->rtp_rx_ts_len_per_frame) + { stream->rtp_rx_ts_len_per_frame = peer_frm_ts_diff; + /* Done, stop the check immediately */ + stream->rtp_rx_check_cnt = 1; + } if (--stream->rtp_rx_check_cnt == 0) { PJ_LOG(4, (THIS_FILE, "G722 codec used, remote" @@ -1985,6 +2002,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); stream->own_pool = own_pool; pj_memcpy(&stream->si, info, sizeof(*info)); + stream->si.param = pjmedia_codec_param_clone(pool, info->param); + pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name); /* Init stream/port name */ name.ptr = (char*) pj_pool_alloc(pool, M); @@ -2159,12 +2178,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Get the frame size */ - stream->frame_size = stream->codec_param.info.max_bps * - stream->codec_param.info.frm_ptime / 8 / 1000; - if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime) - % 8000 != 0) - { - ++stream->frame_size; + if (stream->codec_param.info.max_rx_frame_size > 0) { + stream->frame_size = stream->codec_param.info.max_rx_frame_size; + } else { + stream->frame_size = stream->codec_param.info.max_bps * + stream->codec_param.info.frm_ptime / 8 / 1000; + if ((stream->codec_param.info.max_bps * + stream->codec_param.info.frm_ptime) % 8000 != 0) + { + ++stream->frame_size; + } } /* How many consecutive PLC frames can be generated */ @@ -2172,7 +2195,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.info.frm_ptime; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) - stream->rtp_rx_check_cnt = 5; + stream->rtp_rx_check_cnt = 50; stream->has_g722_mpeg_bug = PJ_FALSE; stream->rtp_rx_last_ts = 0; stream->rtp_rx_last_cnt = 0; @@ -2410,6 +2433,8 @@ err_cleanup: */ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { + pj_status_t status; + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); /* Send RTCP BYE (also SDES & XR) */ @@ -2417,6 +2442,48 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_TRUE); } + /* If we're in the middle of transmitting DTMF digit, send one last + * RFC 2833 RTP packet with 'End' flag set. + */ + if (stream->tx_dtmf_count && stream->tx_dtmf_buf[0].duration != 0) { + pjmedia_frame frame_out; + pjmedia_channel *channel = stream->enc; + int first=0, last=0; + void *rtphdr; + int rtphdrlen; + + pj_bzero(&frame_out, sizeof(frame_out)); + frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); + frame_out.size = 0; + + create_dtmf_payload(stream, &frame_out, 1, &first, &last); + + /* Encapsulate into RTP packet. Note that: + * - RTP marker should be set on the beginning of a new event + * - RTP timestamp is constant for the same packet. + */ + status = pjmedia_rtp_encode_rtp( &channel->rtp, + stream->tx_event_pt, first, + frame_out.size, 0, + (const void**)&rtphdr, + &rtphdrlen); + if (status == PJ_SUCCESS) { + /* Copy RTP header to the beginning of packet */ + pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + + /* Send the RTP packet to the transport. */ + status = pjmedia_transport_send_rtp(stream->transport, + channel->out_pkt, + frame_out.size + + sizeof(pjmedia_rtp_hdr)); + } + + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(stream->port.info.name.ptr, status, + "Error sending RTP/DTMF end packet")); + } + } + /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. @@ -2692,6 +2759,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt; stream->tx_dtmf_buf[stream->tx_dtmf_count+i].duration = 0; + stream->tx_dtmf_buf[stream->tx_dtmf_count+i].ebit_cnt = 0; } if (status != PJ_SUCCESS) diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index 40bb71b..7c3deb2 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -1,4 +1,4 @@ -/* $Id: transport_ice.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: transport_ice.c 4350 2013-02-15 03:57:31Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -285,7 +285,9 @@ static void set_no_ice(struct transport_ice *tp_ice, const char *reason, "Stopping ICE, reason=%s", reason)); } - pj_ice_strans_stop_ice(tp_ice->ice_st); + if (tp_ice->ice_st) { + pj_ice_strans_stop_ice(tp_ice->ice_st); + } tp_ice->use_ice = PJ_FALSE; } @@ -1541,6 +1543,8 @@ static pj_status_t transport_get_info(pjmedia_transport *tp, ii = (pjmedia_ice_transport_info*) tsi->buffer; pj_bzero(ii, sizeof(*ii)); + ii->active = tp_ice->use_ice; + if (pj_ice_strans_has_sess(tp_ice->ice_st)) ii->role = pj_ice_strans_get_role(tp_ice->ice_st); else diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c index 1321e75..a661c37 100644 --- a/pjmedia/src/pjmedia/transport_srtp.c +++ b/pjmedia/src/pjmedia/transport_srtp.c @@ -1,4 +1,4 @@ -/* $Id: transport_srtp.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* $Id: transport_srtp.c 4366 2013-02-21 20:41:31Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -34,10 +34,12 @@ #define THIS_FILE "transport_srtp.c" -/* Maximum size of packet */ -#define MAX_RTP_BUFFER_LEN 1500 -#define MAX_RTCP_BUFFER_LEN 1500 -#define MAX_KEY_LEN 32 +/* Maximum size of outgoing packet */ +#define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU +#define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU + +/* Maximum SRTP crypto key length */ +#define MAX_KEY_LEN 128 /* Initial value of probation counter. When probation counter > 0, * it means SRTP is in probation state, and it may restart when @@ -611,19 +613,47 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( /* Declare SRTP session initialized */ srtp->session_inited = PJ_TRUE; - PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, - octet_string_hex_string(tx->key.ptr, tx->key.slen))); - if (srtp->tx_policy.flags) { - PJ_LOG(5,(srtp->pool->obj_name,"TX: disable%s%s", (cr_tx_idx?"":" enc"), - (au_tx_idx?"":" auth"))); - } + /* Logging stuffs */ +#if PJ_LOG_MAX_LEVEL >= 5 + { + char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)]; + int b64_len; + + /* TX crypto and key */ + b64_len = sizeof(b64); + status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen, + b64, &b64_len); + if (status != PJ_SUCCESS) + b64_len = pj_ansi_sprintf(b64, "--key too long--"); + else + b64[b64_len] = '\0'; + + PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", + srtp->tx_policy.name.ptr, b64)); + if (srtp->tx_policy.flags) { + PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s", + (cr_tx_idx?"":" enc"), + (au_tx_idx?"":" auth"))); + } - PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, - octet_string_hex_string(rx->key.ptr, rx->key.slen))); - if (srtp->rx_policy.flags) { - PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), - (au_rx_idx?"":" auth"))); + /* RX crypto and key */ + b64_len = sizeof(b64); + status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen, + b64, &b64_len); + if (status != PJ_SUCCESS) + b64_len = pj_ansi_sprintf(b64, "--key too long--"); + else + b64[b64_len] = '\0'; + + PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", + srtp->rx_policy.name.ptr, b64)); + if (srtp->rx_policy.flags) { + PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", + (cr_rx_idx?"":" enc"), + (au_rx_idx?"":" auth"))); + } } +#endif on_return: pj_lock_release(srtp->mutex); @@ -777,7 +807,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp, if (srtp->bypass_srtp) return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size); - if (size > sizeof(srtp->rtp_tx_buffer)) + if (size > sizeof(srtp->rtp_tx_buffer) - 10) return PJ_ETOOBIG; pj_memcpy(srtp->rtp_tx_buffer, pkt, size); @@ -791,7 +821,8 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp, pj_lock_release(srtp->mutex); if (err == err_status_ok) { - status = pjmedia_transport_send_rtp(srtp->member_tp, srtp->rtp_tx_buffer, len); + status = pjmedia_transport_send_rtp(srtp->member_tp, + srtp->rtp_tx_buffer, len); } else { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); } @@ -822,7 +853,7 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, pkt, size); } - if (size > sizeof(srtp->rtcp_tx_buffer)) + if (size > sizeof(srtp->rtcp_tx_buffer) - 10) return PJ_ETOOBIG; pj_memcpy(srtp->rtcp_tx_buffer, pkt, size); diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c index 76f9bbb..458424d 100644 --- a/pjmedia/src/pjmedia/transport_udp.c +++ b/pjmedia/src/pjmedia/transport_udp.c @@ -1,4 +1,4 @@ -/* $Id: transport_udp.c 3841 2011-10-24 09:28:13Z ming $ */ +/* $Id: transport_udp.c 4197 2012-07-05 07:26:29Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -29,7 +29,7 @@ /* Maximum size of incoming RTP packet */ -#define RTP_LEN PJMEDIA_MAX_MTU +#define RTP_LEN PJMEDIA_MAX_MRU /* Maximum size of incoming RTCP packet */ #define RTCP_LEN 600 @@ -42,7 +42,7 @@ static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; /* Pending write buffer */ typedef struct pending_write { - char buffer[RTP_LEN]; + char buffer[PJMEDIA_MAX_MTU]; pj_ioqueue_op_key_t op_key; } pending_write; @@ -752,7 +752,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp, PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP); /* Check that the size is supported */ - PJ_ASSERT_RETURN(size <= RTP_LEN, PJ_ETOOBIG); + PJ_ASSERT_RETURN(size <= PJMEDIA_MAX_MTU, PJ_ETOOBIG); /* Simulate packet lost on TX direction */ if (udp->tx_drop_pct) { diff --git a/pjmedia/src/pjmedia/types.c b/pjmedia/src/pjmedia/types.c index b280ea8..732f9b5 100644 --- a/pjmedia/src/pjmedia/types.c +++ b/pjmedia/src/pjmedia/types.c @@ -1,4 +1,4 @@ -/* $Id: types.c 3715 2011-08-19 09:35:25Z nanang $ */ +/* $Id: types.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -37,10 +37,10 @@ PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t) "unknown" }; - pj_assert(t < PJ_ARRAY_SIZE(type_names)); + pj_assert(t < (int)PJ_ARRAY_SIZE(type_names)); pj_assert(PJMEDIA_TYPE_UNKNOWN == 4); - if (t < PJ_ARRAY_SIZE(type_names)) + if (t < (int)PJ_ARRAY_SIZE(type_names)) return type_names[t]; else return "??"; diff --git a/pjmedia/src/pjmedia/vid_codec_util.c b/pjmedia/src/pjmedia/vid_codec_util.c index e51fff4..8cc241b 100644 --- a/pjmedia/src/pjmedia/vid_codec_util.c +++ b/pjmedia/src/pjmedia/vid_codec_util.c @@ -1,4 +1,4 @@ -/* $Id: vid_codec_util.c 3995 2012-03-29 10:54:01Z nanang $ */ +/* $Id: vid_codec_util.c 4362 2013-02-21 14:51:56Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -38,6 +38,14 @@ */ #define H264_STRICT_SDP_NEGO 0 +/* Default frame rate, if not specified */ +#define DEFAULT_H264_FPS_NUM 15 +#define DEFAULT_H264_FPS_DENUM 1 + +/* Default aspect ratio, if not specified */ +#define DEFAULT_H264_RATIO_NUM 16 +#define DEFAULT_H264_RATIO_DENUM 9 + /* ITU resolution definition */ struct mpi_resolution_t @@ -55,6 +63,10 @@ mpi_resolutions [] = }; +#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16)) +#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum + + /* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */ static pj_status_t parse_custom_res_fmtp(const pj_str_t *fmtp_val, pjmedia_rect_size *size, @@ -291,6 +303,75 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp( } +/* Declaration of H.264 level info */ +typedef struct h264_level_info_t +{ + unsigned id; /* Level id. */ + unsigned max_mbps; /* Max macroblocks per second. */ + unsigned max_mb; /* Max macroblocks. */ + unsigned max_br; /* Max bitrate (kbps). */ +} h264_level_info_t; + + +/* Init H264 parameters based on profile-level-id */ +static pj_status_t init_h264_profile(const pj_str_t *profile, + pjmedia_vid_codec_h264_fmtp *fmtp) +{ + const h264_level_info_t level_info[] = + { + { 10, 1485, 99, 64 }, + { 9, 1485, 99, 128 }, /*< level 1b */ + { 11, 3000, 396, 192 }, + { 12, 6000, 396, 384 }, + { 13, 11880, 396, 768 }, + { 20, 11880, 396, 2000 }, + { 21, 19800, 792, 4000 }, + { 22, 20250, 1620, 4000 }, + { 30, 40500, 1620, 10000 }, + { 31, 108000, 3600, 14000 }, + { 32, 216000, 5120, 20000 }, + { 40, 245760, 8192, 20000 }, + { 41, 245760, 8192, 50000 }, + { 42, 522240, 8704, 50000 }, + { 50, 589824, 22080, 135000 }, + { 51, 983040, 36864, 240000 }, + }; + unsigned i, tmp; + pj_str_t endst; + const h264_level_info_t *li = NULL; + + if (profile->slen != 6) + return PJMEDIA_SDP_EINFMTP; + + tmp = pj_strtoul2(profile, &endst, 16); + if (endst.slen) + return PJMEDIA_SDP_EINFMTP; + + fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF); + fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF); + fmtp->level = (pj_uint8_t)(tmp & 0xFF); + + for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) { + if (level_info[i].id == fmtp->level) { + li = &level_info[i]; + break; + } + } + if (li == NULL) + return PJMEDIA_SDP_EINFMTP; + + /* Init profile level spec */ + if (fmtp->max_br == 0) + fmtp->max_br = li->max_br; + if (fmtp->max_mbps == 0) + fmtp->max_mbps = li->max_mbps; + if (fmtp->max_fs == 0) + fmtp->max_fs = li->max_mb; + + return PJ_SUCCESS; +} + + /* H264 fmtp parser */ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp( const pjmedia_codec_fmtp *fmtp, @@ -305,24 +386,17 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp( const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18}; const pj_str_t SPROP_PARAMETER_SETS = {"sprop-parameter-sets", 20}; unsigned i; + pj_status_t status; pj_bzero(h264_fmtp, sizeof(*h264_fmtp)); for (i=0; i<fmtp->cnt; ++i) { unsigned tmp; if (pj_stricmp(&fmtp->param[i].name, &PROFILE_LEVEL_ID)==0) { - pj_str_t endst; - - if (fmtp->param[i].val.slen != 6) - return PJMEDIA_SDP_EINFMTP; - - tmp = pj_strtoul2(&fmtp->param[i].val, &endst, 16); - if (endst.slen) - return PJMEDIA_SDP_EINFMTP; - - h264_fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF); - h264_fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF); - h264_fmtp->level = (pj_uint8_t)(tmp & 0xFF); + /* Init H264 parameters based on level, if not set yet */ + status = init_h264_profile(&fmtp->param[i].val, h264_fmtp); + if (status != PJ_SUCCESS) + return status; } else if (pj_stricmp(&fmtp->param[i].name, &PACKETIZATION_MODE)==0) { tmp = pj_strtoul(&fmtp->param[i].val); if (tmp >= 0 && tmp <= 2) @@ -331,19 +405,19 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp( return PJMEDIA_SDP_EINFMTP; } else if (pj_stricmp(&fmtp->param[i].name, &MAX_MBPS)==0) { tmp = pj_strtoul(&fmtp->param[i].val); - h264_fmtp->max_mbps = tmp; + h264_fmtp->max_mbps = PJ_MAX(tmp, h264_fmtp->max_mbps); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_FS)==0) { tmp = pj_strtoul(&fmtp->param[i].val); - h264_fmtp->max_fs = tmp; + h264_fmtp->max_fs = PJ_MAX(tmp, h264_fmtp->max_fs); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_CPB)==0) { tmp = pj_strtoul(&fmtp->param[i].val); - h264_fmtp->max_cpb = tmp; + h264_fmtp->max_cpb = PJ_MAX(tmp, h264_fmtp->max_cpb); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_DPB)==0) { tmp = pj_strtoul(&fmtp->param[i].val); - h264_fmtp->max_dpb = tmp; + h264_fmtp->max_dpb = PJ_MAX(tmp, h264_fmtp->max_dpb); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_BR)==0) { tmp = pj_strtoul(&fmtp->param[i].val); - h264_fmtp->max_br = tmp; + h264_fmtp->max_br = PJ_MAX(tmp, h264_fmtp->max_br); } else if (pj_stricmp(&fmtp->param[i].name, &SPROP_PARAMETER_SETS)==0) { pj_str_t sps_st; @@ -507,66 +581,80 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_match_sdp(pj_pool_t *pool, } -/* Declaration of H.264 level info */ -typedef struct h264_level_info_t -{ - unsigned id; /* Level id. */ - unsigned max_mbps; /* Max macroblocks per second. */ - unsigned max_mb; /* Max macroblocks. */ - unsigned bitrate; /* Max bitrate (kbps). */ - unsigned def_w; /* Default width. */ - unsigned def_h; /* Default height. */ - unsigned def_fps; /* Default fps. */ -} h264_level_info_t; - +/* Find greatest common divisor (GCD) */ +static unsigned gcd (unsigned a, unsigned b) { + unsigned c; + while (b) { + c = a % b; + a = b; + b = c; + } + return a; +} -/* Get H.264 level info from specified level ID */ -static pj_status_t get_h264_level_info(unsigned id, h264_level_info_t *level) +/* Find highest resolution possible for the specified H264 fmtp and + * aspect ratio. + */ +static pj_status_t find_highest_res(pjmedia_vid_codec_h264_fmtp *fmtp, + const pjmedia_ratio *fps, + const pjmedia_ratio *ratio, + pjmedia_rect_size *size) { - unsigned i; - const h264_level_info_t level_info[] = - { - { 10, 1485, 99, 64, 176, 144, 15 }, - { 9, 1485, 99, 128, 176, 144, 15 }, /*< level 1b */ - { 11, 3000, 396, 192, 320, 240, 10 }, - { 12, 6000, 396, 384, 352, 288, 15 }, - { 13, 11880, 396, 768, 352, 288, 15 }, - { 20, 11880, 396, 2000, 352, 288, 30 }, - { 21, 19800, 792, 4000, 352, 288, 30 }, - { 22, 20250, 1620, 4000, 352, 288, 30 }, - { 30, 40500, 1620, 10000, 720, 480, 30 }, - { 31, 108000, 3600, 14000, 1280, 720, 30 }, - { 32, 216000, 5120, 20000, 1280, 720, 30 }, - { 40, 245760, 8192, 20000, 1920, 1080, 30 }, - { 41, 245760, 8192, 50000, 1920, 1080, 30 }, - { 42, 522240, 8704, 50000, 1920, 1080, 30 }, - { 50, 589824, 22080, 135000, 1920, 1080, 30 }, - { 51, 983040, 36864, 240000, 1920, 1080, 30 }, - }; + pjmedia_ratio def_ratio = { DEFAULT_H264_RATIO_NUM, + DEFAULT_H264_RATIO_DENUM }; + pjmedia_ratio def_fps = { DEFAULT_H264_FPS_NUM, + DEFAULT_H264_FPS_DENUM }; + pjmedia_ratio asp_ratio, the_fps; + unsigned max_fs, g, scale; + + pj_assert(size); + + /* Get the ratio, or just use default if not provided. */ + if (ratio && ratio->num && ratio->denum) { + asp_ratio = *ratio; + } else { + asp_ratio = def_ratio; + } - for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) { - if (level_info[i].id == id) { - *level = level_info[i]; - return PJ_SUCCESS; - } + /* Normalize the aspect ratio */ + g = gcd(asp_ratio.num, asp_ratio.denum); + asp_ratio.num /= g; + asp_ratio.denum /= g; + + /* Get the frame rate, or just use default if not provided. */ + if (fps && fps->num && fps->denum) { + the_fps = *fps; + } else { + the_fps = def_fps; } - return PJ_ENOTFOUND; -} + /* Calculate maximum size (in macroblocks) */ + max_fs = fmtp->max_mbps * fps->denum / fps->num; + max_fs = PJ_MIN(max_fs, fmtp->max_fs); -#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16)) -#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum + /* Check if the specified ratio is using big numbers + * (not normalizable), override it with default ratio! + */ + if ((int)max_fs < asp_ratio.num * asp_ratio.denum) + asp_ratio = def_ratio; + + /* Calculate the scale factor for size */ + scale = pj_isqrt(max_fs / asp_ratio.denum / asp_ratio.num); + + /* Calculate the size, note that the frame size is in macroblock units */ + size->w = asp_ratio.num * scale * 16; + size->h = asp_ratio.denum * scale * 16; + + return PJ_SUCCESS; +} PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( pjmedia_vid_codec_param *param) { - const unsigned default_fps = 30; - if (param->dir & PJMEDIA_DIR_ENCODING) { pjmedia_vid_codec_h264_fmtp fmtp; pjmedia_video_format_detail *vfd; - h264_level_info_t level_info; pj_status_t status; /* Get remote param */ @@ -575,36 +663,42 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( if (status != PJ_SUCCESS) return status; - status = get_h264_level_info(fmtp.level, &level_info); - if (status != PJ_SUCCESS) - return status; - - /* Size and fps for encoding direction must conform to H.264 level + /* Adjust fps, size, and bitrate to conform to H.264 level * specified by remote SDP fmtp. */ vfd = pjmedia_format_get_video_format_detail(¶m->enc_fmt, PJ_TRUE); + + if (vfd->fps.num == 0 || vfd->fps.denum == 0) { + vfd->fps.num = DEFAULT_H264_FPS_NUM; + vfd->fps.denum = DEFAULT_H264_FPS_DENUM; + } + if (vfd->size.w && vfd->size.h) { unsigned mb, mbps; - if (vfd->fps.num == 0 || vfd->fps.denum == 0) { - vfd->fps.num = default_fps; - vfd->fps.denum = 1; - } + /* Scale down the resolution if it exceeds profile spec */ mb = CALC_H264_MB_NUM(vfd->size); mbps = CALC_H264_MBPS(vfd->size, vfd->fps); - if (mb > level_info.max_mb || mbps > level_info.max_mbps) { - vfd->size.w = level_info.def_w; - vfd->size.h = level_info.def_h; - vfd->fps.num = level_info.def_fps; - vfd->fps.denum = 1; + if (mb > fmtp.max_fs || mbps > fmtp.max_mbps) { + pjmedia_ratio r; + r.num = vfd->size.w; + r.denum = vfd->size.h; + find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size); } } else { - vfd->size.w = level_info.def_w; - vfd->size.h = level_info.def_h; - vfd->fps.num = level_info.def_fps; - vfd->fps.denum = 1; + /* When not specified, just use the highest res possible*/ + pjmedia_ratio r; + r.num = vfd->size.w; + r.denum = vfd->size.h; + find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size); } + + /* Encoding bitrate must not be higher than H264 level spec */ + if (vfd->avg_bps > fmtp.max_br * 1000) + vfd->avg_bps = fmtp.max_br * 1000; + if (vfd->max_bps > fmtp.max_br * 1000) + vfd->max_bps = fmtp.max_br * 1000; } if (param->dir & PJMEDIA_DIR_DECODING) { @@ -613,7 +707,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( */ pjmedia_vid_codec_h264_fmtp fmtp; pjmedia_video_format_detail *vfd; - h264_level_info_t level_info; + pjmedia_ratio r; + pjmedia_rect_size highest_size; pj_status_t status; status = pjmedia_vid_codec_h264_parse_fmtp(¶m->dec_fmtp, @@ -621,22 +716,29 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( if (status != PJ_SUCCESS) return status; - status = get_h264_level_info(fmtp.level, &level_info); - if (status != PJ_SUCCESS) - return status; - vfd = pjmedia_format_get_video_format_detail(¶m->dec_fmt, PJ_TRUE); - if (vfd->size.w * vfd->size.h < level_info.def_w * level_info.def_h) { - vfd->size.w = level_info.def_w; - vfd->size.h = level_info.def_h; - } - if (vfd->fps.num == 0 || vfd->fps.denum == 0) { - vfd->fps.num = default_fps; - vfd->fps.denum = 1; + vfd->fps.num = DEFAULT_H264_FPS_NUM; + vfd->fps.denum = DEFAULT_H264_FPS_DENUM; } + + /* Normalize decoding resolution, i.e: it must not be lower than + * the H264 profile level setting used, as this may be used by + * app to allocate buffer. + */ + r.num = vfd->size.w; + r.denum = vfd->size.h; + find_highest_res(&fmtp, &vfd->fps, &r, &highest_size); + if (vfd->size.w * vfd->size.h < highest_size.w * highest_size.h) + vfd->size = highest_size; + + /* Normalize decoding bitrate based on H264 level spec */ + if (vfd->avg_bps < fmtp.max_br * 1000) + vfd->avg_bps = fmtp.max_br * 1000; + if (vfd->max_bps < fmtp.max_br * 1000) + vfd->max_bps = fmtp.max_br * 1000; } return PJ_SUCCESS; diff --git a/pjmedia/src/pjmedia/vid_port.c b/pjmedia/src/pjmedia/vid_port.c index 289e3e5..017e867 100644 --- a/pjmedia/src/pjmedia/vid_port.c +++ b/pjmedia/src/pjmedia/vid_port.c @@ -1,4 +1,4 @@ -/* $Id: vid_port.c 4168 2012-06-18 05:59:08Z ming $ */ +/* $Id: vid_port.c 4290 2012-11-01 03:06:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * @@ -332,9 +332,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool, PJMEDIA_SIG_VID_PORT, prm->vidparam.dir, &prm->vidparam.fmt); - if (vp->stream_role == ROLE_ACTIVE) { - need_frame_buf = PJ_TRUE; - } + need_frame_buf = PJ_TRUE; } if (need_frame_buf) { @@ -654,8 +652,10 @@ static pj_status_t convert_frame(pjmedia_vid_port *vp, pj_status_t status = PJ_SUCCESS; if (vp->conv.conv) { - dst_frame->buf = vp->conv.conv_buf; - dst_frame->size = vp->conv.conv_buf_size; + if (!dst_frame->buf || dst_frame->size < vp->conv.conv_buf_size) { + dst_frame->buf = vp->conv.conv_buf; + dst_frame->size = vp->conv.conv_buf_size; + } status = pjmedia_converter_convert(vp->conv.conv, src_frame, dst_frame); } @@ -922,6 +922,7 @@ static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port, pj_status_t status; pjmedia_frame frame_; + pj_bzero(&frame_, sizeof(frame_)); status = convert_frame(vp, frame, &frame_); if (status != PJ_SUCCESS) return status; diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index d899408..e101d7b 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -1,4 +1,4 @@ -/* $Id: vid_stream.c 4123 2012-05-14 11:17:31Z ming $ */ +/* $Id: vid_stream.c 4197 2012-07-05 07:26:29Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * @@ -68,6 +68,13 @@ # define PJMEDIA_VSTREAM_INC 1000 #endif +/* Due to network MTU limitation, a picture bitstream may be splitted into + * several chunks for RTP delivery. The chunk number may vary depend on the + * picture resolution and MTU. This constant specifies the minimum chunk + * number to be allocated to store a picture bitstream in decoding direction. + */ +#define MIN_CHUNKS_PER_FRM 30 + /* Video stream keep-alive feature is currently disabled. */ #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 # undef PJMEDIA_STREAM_ENABLE_KA @@ -1357,7 +1364,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( int frm_ptime, chunks_per_frm; pjmedia_video_format_detail *vfd_enc, *vfd_dec; char *p; - unsigned dec_mtu; pj_status_t status; if (!pool) { @@ -1410,9 +1416,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( if (info->codec_param->enc_mtu > PJMEDIA_MAX_MTU) info->codec_param->enc_mtu = PJMEDIA_MAX_MTU; - /* MTU estimation for decoding direction */ - dec_mtu = PJMEDIA_MAX_MTU; - + /* Packet size estimation for decoding direction */ vfd_enc = pjmedia_format_get_video_format_detail( &info->codec_param->enc_fmt, PJ_TRUE); vfd_dec = pjmedia_format_get_video_format_detail( @@ -1528,8 +1532,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Init jitter buffer parameters: */ frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num; - chunks_per_frm = stream->frame_size / dec_mtu; - if (chunks_per_frm == 0) chunks_per_frm = 1; + chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MRU; + if (chunks_per_frm < MIN_CHUNKS_PER_FRM) + chunks_per_frm = MIN_CHUNKS_PER_FRM; /* JB max count, default 500ms */ if (info->jb_max >= frm_ptime) @@ -1564,7 +1569,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Create jitter buffer */ status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name, - dec_mtu + PJMEDIA_STREAM_RESV_PAYLOAD_LEN, + PJMEDIA_MAX_MRU, 1000 * vfd_enc->fps.denum / vfd_enc->fps.num, jb_max, &stream->jb); if (status != PJ_SUCCESS) diff --git a/pjmedia/src/pjmedia/vid_stream_info.c b/pjmedia/src/pjmedia/vid_stream_info.c index 51b688f..37dae5b 100644 --- a/pjmedia/src/pjmedia/vid_stream_info.c +++ b/pjmedia/src/pjmedia/vid_stream_info.c @@ -1,4 +1,4 @@ -/* $Id: vid_stream_info.c 3982 2012-03-22 09:56:52Z bennylp $ */ +/* $Id: vid_stream_info.c 4257 2012-09-17 03:11:44Z ming $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * @@ -22,6 +22,7 @@ #include <pj/ctype.h> #include <pj/rand.h> +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) static const pj_str_t ID_VIDEO = { "video", 5}; static const pj_str_t ID_IN = { "IN", 2 }; @@ -382,4 +383,4 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( return status; } - +#endif /* PJMEDIA_HAS_VIDEO */ diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index b0b146d..df68511 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -1,4 +1,4 @@ -/* $Id: mips_test.c 3982 2012-03-22 09:56:52Z bennylp $ */ +/* $Id: mips_test.c 4335 2013-01-29 08:09:15Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -943,6 +943,23 @@ static pjmedia_port* amr_encode_decode(pj_pool_t *pool, } #endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */ +#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +/* AMR-WB benchmark benchmark */ +static pjmedia_port* amrwb_encode_decode(pj_pool_t *pool, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned flags, + struct test_entry *te) +{ + return codec_encode_decode(pool, "AMR/16000", + &pjmedia_codec_opencore_amr_init_default, + &pjmedia_codec_opencore_amr_deinit, + clock_rate, channel_count, + samples_per_frame, flags, te); +} +#endif /* PJMEDIA_HAS_OPENCORE_AMRWB_CODEC */ + #if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0 static pj_status_t init_l16_default(pjmedia_endpt *endpt) { @@ -2024,7 +2041,25 @@ static pjmedia_port* create_stream_amr( pj_pool_t *pool, clock_rate, channel_count, samples_per_frame, flags, te); } -#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */ +#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */ + +/* AMR-WB stream */ +#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +static pjmedia_port* create_stream_amrwb( pj_pool_t *pool, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned flags, + struct test_entry *te) +{ + return create_stream(pool, "AMR/16000", + &pjmedia_codec_opencore_amr_init_default, + &pjmedia_codec_opencore_amr_deinit, + PJ_FALSE, PJ_FALSE, PJ_FALSE, + clock_rate, channel_count, + samples_per_frame, flags, te); +} +#endif /* PJMEDIA_HAS_OPENCORE_AMRWB_CODEC */ /***************************************************************************/ /* Delay buffer */ @@ -2419,6 +2454,9 @@ int mips_test(void) #if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC { "codec encode/decode - AMR-NB", OP_PUT, K8, &amr_encode_decode}, #endif +#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC + { "codec encode/decode - AMR-WB", OP_PUT, K16, &amrwb_encode_decode}, +#endif #if PJMEDIA_HAS_L16_CODEC { "codec encode/decode - L16/8000/1", OP_PUT, K8, &l16_8_encode_decode}, { "codec encode/decode - L16/16000/1", OP_PUT, K16, &l16_16_encode_decode}, @@ -2447,6 +2485,9 @@ int mips_test(void) #if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC { "stream TX/RX - AMR-NB", OP_PUT_GET, K8, &create_stream_amr}, #endif +#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC + { "stream TX/RX - AMR-WB", OP_PUT_GET, K16, &create_stream_amrwb}, +#endif }; unsigned i, c, k[3] = {K8, K16, K32}, clock_rates[3] = {8000, 16000, 32000}; |