Re #2029: Support for G.729 backend implementation from bcg729.

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@5630 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Riza Sulistyo 2017-07-19 10:29:10 +00:00
parent 55e2570a9b
commit 10882ddde2
11 changed files with 950 additions and 2 deletions

View File

@ -627,6 +627,7 @@ ac_webrtc_cflags
ac_webrtc_instset
ac_no_webrtc
ac_no_yuv
ac_no_bcg729
opus_present
opus_h_present
ac_no_opus
@ -826,6 +827,8 @@ with_silk
enable_silk
with_opus
enable_opus
with_bcg729
enable_bcg729
enable_libyuv
enable_libwebrtc
'
@ -1506,6 +1509,7 @@ Optional Features:
--disable-opus Exclude OPUS support from the build (default:
autodetect)
--disable-bcg729 Disable bcg729 (default: not disabled)
--disable-libyuv Exclude libyuv in the build
--disable-libwebrtc Exclude libwebrtc in the build
@ -1558,6 +1562,7 @@ Optional Packages:
Specify alternate libvo-amrwbenc prefix
--with-silk=DIR Specify alternate SILK prefix
--with-opus=DIR Specify alternate OPUS prefix
--with-bcg729=DIR Specify alternate bcg729 prefix
Some influential environment variables:
CC C compiler command
@ -8472,6 +8477,99 @@ fi
# Check whether --with-bcg729 was given.
if test "${with_bcg729+set}" = set; then :
withval=$with_bcg729;
else
with_bcg729=no
fi
if test "x$ac_cross_compile" != "x" -a "x$with_bcg729" = "xno"; then
enable_bcg729=no
fi
# Check whether --enable-bcg729 was given.
if test "${enable_bcg729+set}" = set; then :
enableval=$enable_bcg729;
if test "$enable_bcg729" = "no"; then
ac_no_bcg729=1
$as_echo "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if bcg729 is disabled... yes" >&5
$as_echo "Checking if bcg729 is disabled... yes" >&6; }
fi
else
if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then
BCG729_PREFIX=$with_bcg729
BCG729_CFLAGS="-I$BCG729_PREFIX/include"
BCG729_LDFLAGS="-L$BCG729_PREFIX/lib"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Using bcg729 prefix... $with_bcg729" >&5
$as_echo "Using bcg729 prefix... $with_bcg729" >&6; }
else
BCG729_CFLAGS=""
BCG729_LDFLAGS=""
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking bcg729 usability" >&5
$as_echo_n "checking bcg729 usability... " >&6; }
BCG729_LIBS="-lbcg729"
SAVED_LIBS="$LIBS"
SAVED_LDFLAGS="$LDFLAGS"
SAVED_CFLAGS="$CFLAGS"
LIBS="$BCG729_LIBS $LIBS"
LDFLAGS="$BCG729_LDFLAGS $LDFLAGS"
CFLAGS="$BCG729_CFLAGS $CFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <bcg729/encoder.h>
#include <bcg729/decoder.h>
int
main ()
{
initBcg729EncoderChannel(0);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
$as_echo "#define PJMEDIA_HAS_BCG729 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
$as_echo "ok" >&6; }
else
ac_no_bcg729=1
$as_echo "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h
LIBS="$SAVED_LIBS"
LDFLAGS="$SAVED_LDFLAGS"
CFLAGS="$SAVED_CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
# Check whether --enable-libyuv was given.
if test "${enable_libyuv+set}" = set; then :
enableval=$enable_libyuv; if test "$enable_libyuv" = "no"; then

View File

@ -1787,6 +1787,75 @@ AC_ARG_ENABLE(opus,
fi
])
dnl # bcg729 prefix
AC_ARG_WITH(bcg729,
AS_HELP_STRING([--with-bcg729=DIR],
[Specify alternate bcg729 prefix]),
[],
[with_bcg729=no]
)
dnl # Do not use default bcg729 installation if we are cross-compiling
if test "x$ac_cross_compile" != "x" -a "x$with_bcg729" = "xno"; then
enable_bcg729=no
fi
dnl # bcg729
AC_SUBST(ac_no_bcg729)
AC_ARG_ENABLE(bcg729,
AS_HELP_STRING([--disable-bcg729],
[Disable bcg729 (default: not disabled)]),
[
if test "$enable_bcg729" = "no"; then
[ac_no_bcg729=1]
AC_DEFINE(PJMEDIA_HAS_BCG729,0)
AC_MSG_RESULT([Checking if bcg729 is disabled... yes])
fi
],
[
if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then
BCG729_PREFIX=$with_bcg729
BCG729_CFLAGS="-I$BCG729_PREFIX/include"
BCG729_LDFLAGS="-L$BCG729_PREFIX/lib"
AC_MSG_RESULT([Using bcg729 prefix... $with_bcg729])
else
BCG729_CFLAGS=""
BCG729_LDFLAGS=""
fi
AC_MSG_CHECKING([bcg729 usability])
BCG729_LIBS="-lbcg729"
SAVED_LIBS="$LIBS"
SAVED_LDFLAGS="$LDFLAGS"
SAVED_CFLAGS="$CFLAGS"
LIBS="$BCG729_LIBS $LIBS"
LDFLAGS="$BCG729_LDFLAGS $LDFLAGS"
CFLAGS="$BCG729_CFLAGS $CFLAGS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <bcg729/encoder.h>
#include <bcg729/decoder.h>
]],
[initBcg729EncoderChannel(0);]
)],
[
AC_DEFINE(PJMEDIA_HAS_BCG729,1)
AC_MSG_RESULT(ok)
],
[
[ac_no_bcg729=1]
AC_DEFINE(PJMEDIA_HAS_BCG729,0)
LIBS="$SAVED_LIBS"
LDFLAGS="$SAVED_LDFLAGS"
CFLAGS="$SAVED_CFLAGS"
AC_MSG_RESULT(no)
])
])
dnl # Include libyuv
AC_SUBST(ac_no_yuv)
AC_ARG_ENABLE(libyuv,

View File

@ -71,6 +71,7 @@ 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@
AC_NO_BCG729=@ac_no_bcg729@
export CODEC_OBJS=
@ -139,6 +140,10 @@ export CODEC_OBJS += opencore_amr.o
endif
endif
ifeq ($(AC_NO_BCG729),)
export CODEC_OBJS += bcg729.o
endif
#
# SRTP

View File

@ -194,8 +194,9 @@
<!-- Override the PlatformToolset -->
<PropertyGroup>
<PlatformToolset>$(BuildToolset)</PlatformToolset>
<CharacterSet Condition="'$(API_Family)'!='WinDesktop'"></CharacterSet>
</PropertyGroup>
<CharacterSet Condition="'$(API_Family)'!='WinDesktop'">
</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
@ -510,6 +511,7 @@
<ItemGroup>
<ClCompile Include="..\src\pjmedia-codec\amr_sdp_match.c" />
<ClCompile Include="..\src\pjmedia-codec\audio_codecs.c" />
<ClCompile Include="..\src\pjmedia-codec\bcg729.c" />
<ClCompile Include="..\src\pjmedia-codec\ffmpeg_vid_codecs.c" />
<ClCompile Include="..\src\pjmedia-codec\g722.c" />
<ClCompile Include="..\src\pjmedia-codec\g7221.c" />
@ -534,6 +536,7 @@
<ClInclude Include="..\include\pjmedia-codec\amr_helper.h" />
<ClInclude Include="..\include\pjmedia-codec\amr_sdp_match.h" />
<ClInclude Include="..\include\pjmedia-codec\audio_codecs.h" />
<ClInclude Include="..\include\pjmedia-codec\bcg729.h" />
<ClInclude Include="..\include\pjmedia-codec\config.h" />
<ClInclude Include="..\include\pjmedia-codec\ffmpeg_vid_codecs.h" />
<ClInclude Include="..\include\pjmedia-codec\g722.h" />

View File

@ -74,6 +74,9 @@
<ClCompile Include="..\src\pjmedia-codec\opus.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\pjmedia-codec\bcg729.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\pjmedia-codec\g722\g722_dec.h">
@ -145,5 +148,8 @@
<ClInclude Include="..\include\pjmedia-codec\opus.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\pjmedia-codec\bcg729.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -40,6 +40,7 @@
#include <pjmedia-codec/passthrough.h>
#include <pjmedia-codec/silk.h>
#include <pjmedia-codec/opus.h>
#include <pjmedia-codec/bcg729.h>
#endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */

View File

@ -0,0 +1,121 @@
/* $Id$ */
/*
* Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
*
* 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_BCG729_H__
#define __PJMEDIA_CODEC_BCG729_H__
/**
* @file bcg729.h
* @brief BCG729 codec.
*/
#include <pjmedia-codec/types.h>
/**
* @defgroup PJMED_BCG729 BCG729 Codec
* @ingroup PJMEDIA_CODEC_CODECS
* @brief Implementation of BCG729 codecs.
* @{
*
* This section describes functions to initialize and register BCG729 codec
* factory to the codec manager. After the codec factory has been registered,
* application can use @ref PJMEDIA_CODEC API to manipulate the codec.
*
* This codec factory contains G.729 codec.
*
* \section pjmedia_codec_bcg729 BCG729
*
* BCG729 is compliant with ITU-T G.729 and Annexes A, B specifications.
*
* BCGG729 supports 16-bit PCM audio signal with sampling rate 8000Hz,
* frame length 10ms, and resulting in bitrate 8000bps.
*
* \subsection codec_setting Codec 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.
*
* Note that G.729 VAD status should be signalled in SDP, see more
* description below.
*
* \subsubsection annexb Annex B
*
* The capability of VAD/DTX is specified in Annex B.
*
* By default, Annex B is enabled. This default setting of Annex B can
* be modified using #pjmedia_codec_mgr_set_default_param().
*
* In #pjmedia_codec_param, Annex B is configured via VAD setting and
* format parameter "annexb" in the SDP "a=fmtp" attribute in
* decoding fmtp field. Valid values are "yes" and "no",
* the implementation default is "yes". When this parameter is omitted
* in the SDP, the value will be "yes" (RFC 4856 Section 2.1.9).
*
* Here is an example of modifying default setting of Annex B to
* be disabled using #pjmedia_codec_mgr_set_default_param():
\code
pjmedia_codec_param param;
pjmedia_codec_mgr_get_default_param(.., &param);
...
// Set VAD
param.setting.vad = 0;
// Set SDP format parameter
param.setting.dec_fmtp.cnt = 1;
param.setting.dec_fmtp.param[0].name = pj_str("annexb");
param.setting.dec_fmtp.param[0].val = pj_str("no");
...
pjmedia_codec_mgr_set_default_param(.., &param);
\endcode
*
* \note
* The difference of Annex B status in SDP offer/answer may be considered as
* incompatible codec in SDP negotiation.
*
*/
PJ_BEGIN_DECL
/**
* Initialize and register BCG729 codec factory to pjmedia endpoint.
*
* @param endpt The pjmedia endpoint.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_codec_bcg729_init(pjmedia_endpt *endpt);
/**
* Unregister BCG729 codec factory from pjmedia endpoint and deinitialize
* the BCG729 codec library.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_codec_bcg729_deinit(void);
PJ_END_DECL
/**
* @}
*/
#endif /* __PJMEDIA_CODEC_BCG729_H__ */

View File

@ -469,6 +469,17 @@
# define PJMEDIA_CODEC_OPUS_DEFAULT_CBR PJ_FALSE
#endif
/**
* Enable G.729 codec using BCG729 backend.
*
* Default: 0
*/
#ifndef PJMEDIA_HAS_BCG729
# define PJMEDIA_HAS_BCG729 0
#endif
/**
* Specify if FFMPEG codecs are available.
*

View File

@ -89,6 +89,11 @@
#undef PJMEDIA_HAS_OPUS_CODEC
#endif
/* G.729 codec with BCG729 backend */
#ifndef PJMEDIA_HAS_BCG729
#undef PJMEDIA_HAS_BCG729
#endif
#endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */

View File

@ -128,6 +128,13 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
return status;
#endif
#if PJMEDIA_HAS_BCG729
/* Register BCG729 */
status = pjmedia_codec_bcg729_init(endpt);
if (status != PJ_SUCCESS)
return status;
#endif
return PJ_SUCCESS;
}

View File

@ -0,0 +1,622 @@
/* $Id$ */
/*
* Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
*
* 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/bcg729.h>
#include <pjmedia/codec.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_BCG729) && (PJMEDIA_HAS_BCG729!=0)
#include <bcg729/encoder.h>
#include <bcg729/decoder.h>
#define THIS_FILE "bcg729.c"
/* Prototypes for BCG729 factory */
static pj_status_t bcg729_test_alloc(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id);
static pj_status_t bcg729_default_attr(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
pjmedia_codec_param *attr);
static pj_status_t bcg729_enum_codecs (pjmedia_codec_factory *factory,
unsigned *count,
pjmedia_codec_info codecs[]);
static pj_status_t bcg729_alloc_codec(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
pjmedia_codec **p_codec);
static pj_status_t bcg729_dealloc_codec(pjmedia_codec_factory *factory,
pjmedia_codec *codec);
/* Prototypes for BCG729 implementation. */
static pj_status_t bcg729_codec_init(pjmedia_codec *codec,
pj_pool_t *pool );
static pj_status_t bcg729_codec_open(pjmedia_codec *codec,
pjmedia_codec_param *attr);
static pj_status_t bcg729_codec_close(pjmedia_codec *codec);
static pj_status_t bcg729_codec_modify(pjmedia_codec *codec,
const pjmedia_codec_param *attr);
static pj_status_t bcg729_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 bcg729_codec_encode(pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output);
static pj_status_t bcg729_codec_decode(pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output);
static pj_status_t bcg729_codec_recover(pjmedia_codec *codec,
unsigned output_buf_len,
struct pjmedia_frame *output);
/* Codec const. */
#define G729_TAG "G729"
#define G729_CLOCK_RATE 8000
#define G729_CHANNEL_COUNT 1
#define G729_SAMPLES_PER_FRAME 80
#define G729_DEFAULT_BIT_RATE 8000
#define G729_MAX_BIT_RATE 11800
#define G729_FRAME_PER_PACKET 2
#define G729_FRAME_SIZE 10
/* Definition for BCG729 codec operations. */
static pjmedia_codec_op bcg729_op =
{
&bcg729_codec_init,
&bcg729_codec_open,
&bcg729_codec_close,
&bcg729_codec_modify,
&bcg729_codec_parse,
&bcg729_codec_encode,
&bcg729_codec_decode,
&bcg729_codec_recover
};
/* Definition for BCG729 codec factory operations. */
static pjmedia_codec_factory_op bcg729_factory_op =
{
&bcg729_test_alloc,
&bcg729_default_attr,
&bcg729_enum_codecs,
&bcg729_alloc_codec,
&bcg729_dealloc_codec,
&pjmedia_codec_bcg729_deinit
};
/* BCG729 factory private data */
static struct bcg729_factory
{
pjmedia_codec_factory base;
pjmedia_endpt *endpt;
pj_pool_t *pool;
pj_mutex_t *mutex;
} bcg729_factory;
/* BCG729 codec private data. */
typedef struct bcg729_private
{
pj_pool_t *pool; /**< Pool for each instance. */
bcg729EncoderChannelContextStruct *encoder;
bcg729DecoderChannelContextStruct *decoder;
pj_bool_t vad_enabled;
pj_bool_t plc_enabled;
} bcg729_private;
PJ_DEF(pj_status_t) pjmedia_codec_bcg729_init(pjmedia_endpt *endpt)
{
pjmedia_codec_mgr *codec_mgr;
pj_status_t status;
if (bcg729_factory.endpt != NULL) {
/* Already initialized. */
return PJ_SUCCESS;
}
/* Init factory */
pj_bzero(&bcg729_factory, sizeof(bcg729_factory));
bcg729_factory.base.op = &bcg729_factory_op;
bcg729_factory.base.factory_data = NULL;
bcg729_factory.endpt = endpt;
/* Create pool */
bcg729_factory.pool = pjmedia_endpt_create_pool(endpt, "bcg729", 4000,
4000);
if (!bcg729_factory.pool)
return PJ_ENOMEM;
/* Create mutex. */
status = pj_mutex_create_simple(bcg729_factory.pool, "bcg729",
&bcg729_factory.mutex);
if (status != PJ_SUCCESS)
goto on_error;
/* Get the codec manager. */
codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
if (!codec_mgr) {
status = PJ_EINVALIDOP;
goto on_error;
}
/* Register codec factory to endpoint. */
status = pjmedia_codec_mgr_register_factory(codec_mgr,
&bcg729_factory.base);
if (status != PJ_SUCCESS)
goto on_error;
PJ_LOG(4,(THIS_FILE, "BCG729 codec initialized"));
return PJ_SUCCESS;
on_error:
if (bcg729_factory.mutex) {
pj_mutex_destroy(bcg729_factory.mutex);
bcg729_factory.mutex = NULL;
}
if (bcg729_factory.pool) {
pj_pool_release(bcg729_factory.pool);
bcg729_factory.pool = NULL;
}
return status;
}
/*
* Unregister BCG729 codec factory from pjmedia endpoint and deinitialize
* the BCG729 codec library.
*/
PJ_DEF(pj_status_t) pjmedia_codec_bcg729_deinit(void)
{
pjmedia_codec_mgr *codec_mgr;
pj_status_t status;
if (bcg729_factory.endpt == NULL) {
/* Not registered. */
return PJ_SUCCESS;
}
/* Lock mutex. */
pj_mutex_lock(bcg729_factory.mutex);
/* Get the codec manager. */
codec_mgr = pjmedia_endpt_get_codec_mgr(bcg729_factory.endpt);
if (!codec_mgr) {
bcg729_factory.endpt = NULL;
pj_mutex_unlock(bcg729_factory.mutex);
return PJ_EINVALIDOP;
}
/* Unregister bcg729 codec factory. */
status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
&bcg729_factory.base);
bcg729_factory.endpt = NULL;
/* Destroy mutex. */
pj_mutex_unlock(bcg729_factory.mutex);
pj_mutex_destroy(bcg729_factory.mutex);
bcg729_factory.mutex = NULL;
/* Release pool. */
pj_pool_release(bcg729_factory.pool);
bcg729_factory.pool = NULL;
return status;
}
/*
* Check if factory can allocate the specified codec.
*/
static pj_status_t bcg729_test_alloc(pjmedia_codec_factory *factory,
const pjmedia_codec_info *info )
{
pj_str_t g729_tag = pj_str(G729_TAG);
PJ_UNUSED_ARG(factory);
PJ_ASSERT_RETURN(factory==&bcg729_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, &g729_tag) != 0)
return PJMEDIA_CODEC_EUNSUP;
/* Channel count must be one */
if (info->channel_cnt != G729_CHANNEL_COUNT)
return PJMEDIA_CODEC_EUNSUP;
/* Check clock-rate */
if (info->clock_rate != G729_CLOCK_RATE)
return PJMEDIA_CODEC_EUNSUP;
return PJ_SUCCESS;
}
/*
* Generate default attribute.
*/
static pj_status_t bcg729_default_attr(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
pjmedia_codec_param *attr )
{
PJ_ASSERT_RETURN(factory==&bcg729_factory.base, PJ_EINVAL);
if (id->pt != PJMEDIA_RTP_PT_G729)
return PJMEDIA_CODEC_EUNSUP;
pj_bzero(attr, sizeof(pjmedia_codec_param));
attr->info.pt = PJMEDIA_RTP_PT_G729;
attr->info.channel_cnt = G729_CHANNEL_COUNT;
attr->info.clock_rate = G729_CLOCK_RATE;
attr->info.avg_bps = G729_DEFAULT_BIT_RATE;
attr->info.max_bps = G729_MAX_BIT_RATE;
attr->info.pcm_bits_per_sample = G729_FRAME_PER_PACKET * 8;
attr->info.frm_ptime = (pj_uint16_t)
(G729_SAMPLES_PER_FRAME * 1000 /
G729_CHANNEL_COUNT /
G729_CLOCK_RATE);
attr->setting.frm_per_pkt = G729_FRAME_PER_PACKET;
/* Default flags. */
attr->setting.plc = 1;
attr->setting.penh= 0;
attr->setting.vad = 1;
attr->setting.cng = attr->setting.vad;
if (attr->setting.vad == 0) {
attr->setting.dec_fmtp.cnt = 1;
pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
}
return PJ_SUCCESS;
}
/*
* Enum codecs supported by this factory.
*/
static pj_status_t bcg729_enum_codecs(pjmedia_codec_factory *factory,
unsigned *count,
pjmedia_codec_info codecs[])
{
PJ_ASSERT_RETURN(factory==&bcg729_factory.base, PJ_EINVAL);
PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
codecs[0].encoding_name = pj_str(G729_TAG);
codecs[0].pt = PJMEDIA_RTP_PT_G729;
codecs[0].type = PJMEDIA_TYPE_AUDIO;
codecs[0].clock_rate = G729_CLOCK_RATE;
codecs[0].channel_cnt = G729_CHANNEL_COUNT;
*count = 1;
return PJ_SUCCESS;
}
/*
* Allocate a new BCG729 codec instance.
*/
static pj_status_t bcg729_alloc_codec(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
pjmedia_codec **p_codec)
{
pj_pool_t *pool;
pjmedia_codec *codec;
bcg729_private *bcg729_data;
PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &bcg729_factory.base, PJ_EINVAL);
pj_mutex_lock(bcg729_factory.mutex);
/* Create pool for codec instance */
pool = pjmedia_endpt_create_pool(bcg729_factory.endpt, "bcg729", 512, 512);
/* Allocate codec */
codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
codec->op = &bcg729_op;
codec->factory = factory;
codec->codec_data = PJ_POOL_ZALLOC_T(pool, bcg729_private);
bcg729_data = (bcg729_private*) codec->codec_data;
bcg729_data->pool = pool;
pj_mutex_unlock(bcg729_factory.mutex);
*p_codec = codec;
return PJ_SUCCESS;
}
/*
* Free codec.
*/
static pj_status_t bcg729_dealloc_codec(pjmedia_codec_factory *factory,
pjmedia_codec *codec )
{
bcg729_private *bcg729_data;
PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &bcg729_factory.base, PJ_EINVAL);
bcg729_data = (bcg729_private*)codec->codec_data;
/* Close codec, if it's not closed. */
if (bcg729_data->encoder || bcg729_data->decoder) {
bcg729_codec_close(codec);
}
pj_pool_release(bcg729_data->pool);
return PJ_SUCCESS;
}
/*
* Init codec.
*/
static pj_status_t bcg729_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 bcg729_codec_open(pjmedia_codec *codec,
pjmedia_codec_param *attr )
{
bcg729_private *bcg729_data;
unsigned i;
PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
bcg729_data = (bcg729_private*) codec->codec_data;
/* Already open? */
if (bcg729_data->encoder && bcg729_data->decoder)
return PJ_SUCCESS;
bcg729_data->vad_enabled = (attr->setting.vad != 0);
bcg729_data->plc_enabled = (attr->setting.plc != 0);
/* Check if G729 Annex B is signaled to be disabled */
for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].name, "annexb")==0)
{
if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].val, "no")==0)
{
attr->setting.vad = 0;
bcg729_data->vad_enabled = PJ_FALSE;
}
break;
}
}
bcg729_data->encoder = initBcg729EncoderChannel(
bcg729_data->vad_enabled?1:0);
if (!bcg729_data->encoder)
return PJMEDIA_CODEC_EFAILED;
bcg729_data->decoder = initBcg729DecoderChannel();
if (!bcg729_data->decoder)
return PJMEDIA_CODEC_EFAILED;
return PJ_SUCCESS;
}
/*
* Close codec.
*/
static pj_status_t bcg729_codec_close( pjmedia_codec *codec )
{
bcg729_private *bcg729_data;
PJ_ASSERT_RETURN(codec, PJ_EINVAL);
bcg729_data = (bcg729_private *)codec->codec_data;
if (bcg729_data->encoder) {
closeBcg729EncoderChannel(bcg729_data->encoder);
bcg729_data->encoder = NULL;
}
if (bcg729_data->decoder) {
closeBcg729DecoderChannel(bcg729_data->decoder);
bcg729_data->decoder = NULL;
}
return PJ_SUCCESS;
}
/*
* Get frames in the packet.
*/
static pj_status_t bcg729_codec_parse(pjmedia_codec *codec,
void *pkt,
pj_size_t pkt_size,
const pj_timestamp *ts,
unsigned *frame_cnt,
pjmedia_frame frames[])
{
unsigned count = 0;
PJ_UNUSED_ARG(codec);
PJ_ASSERT_RETURN(codec && ts && frames && frame_cnt, PJ_EINVAL);
while (pkt_size >= G729_FRAME_SIZE && count < *frame_cnt) {
frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
frames[count].buf = pkt;
frames[count].size = G729_FRAME_SIZE;
frames[count].timestamp.u64 = ts->u64 + count * G729_SAMPLES_PER_FRAME;
pkt = ((char*)pkt) + 10;
pkt_size -= 10;
++count;
}
*frame_cnt = count;
return PJ_SUCCESS;
}
/*
* Modify codec settings.
*/
static pj_status_t bcg729_codec_modify(pjmedia_codec *codec,
const pjmedia_codec_param *attr )
{
PJ_UNUSED_ARG(codec);
PJ_UNUSED_ARG(attr);
return PJ_SUCCESS;
}
/*
* Encode frame.
*/
static pj_status_t bcg729_codec_encode(pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output)
{
bcg729_private *bcg729_data;
pj_int16_t *pcm_in;
unsigned nsamples;
pj_uint8_t stream_len;
PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
bcg729_data = (bcg729_private*)codec->codec_data;
pcm_in = (pj_int16_t*)input->buf;
/* Check frame in size */
nsamples = input->size >> 1;
output->size = 0;
/* Encode */
while (nsamples >= G729_SAMPLES_PER_FRAME) {
bcg729Encoder(bcg729_data->encoder, pcm_in,
(unsigned char*)output->buf + output->size, &stream_len);
pcm_in += G729_SAMPLES_PER_FRAME;
nsamples -= G729_SAMPLES_PER_FRAME;
if (stream_len == 0) {
/* Untransmitted */
break;
} else {
output->size += stream_len;
if (stream_len == 2) {
/* SID */
break;
}
}
}
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
output->timestamp = input->timestamp;
return PJ_SUCCESS;
}
static pj_status_t bcg729_codec_decode(pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output)
{
bcg729_private *bcg729_data;
PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
bcg729_data = (struct bcg729_private*) codec->codec_data;
bcg729Decoder(bcg729_data->decoder,
(unsigned char*)input->buf,
G729_FRAME_SIZE,
0,
0,
0,
(short*)output->buf);
output->size = G729_SAMPLES_PER_FRAME * G729_FRAME_PER_PACKET;
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
output->timestamp = input->timestamp;
return PJ_SUCCESS;
}
/*
* Recover lost frame.
*/
static pj_status_t bcg729_codec_recover(pjmedia_codec *codec,
unsigned output_buf_len,
struct pjmedia_frame *output)
{
bcg729_private *bcg729_data;
PJ_ASSERT_RETURN(codec && output_buf_len && output, PJ_EINVAL);
bcg729_data = (struct bcg729_private*) codec->codec_data;
output->size = G729_SAMPLES_PER_FRAME * G729_FRAME_PER_PACKET;
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
if (bcg729_data->plc_enabled) {
bcg729Decoder(bcg729_data->decoder,
NULL,
G729_FRAME_SIZE,
1,
0,
0,
(short*)output->buf);
} else {
pjmedia_zero_samples((pj_int16_t*)output->buf, G729_SAMPLES_PER_FRAME);
}
return PJ_SUCCESS;
}
#endif /* PJMEDIA_HAS_BCG729 */