diff --git a/aconfigure b/aconfigure index 0ef178b110..77346d94da 100755 --- a/aconfigure +++ b/aconfigure @@ -658,6 +658,8 @@ ac_webrtc_instset ac_no_webrtc ac_no_yuv ac_no_srtp +ac_lyra_model_path +ac_no_lyra_codec ac_no_bcg729 opus_present opus_h_present @@ -885,6 +887,8 @@ with_opus enable_opus with_bcg729 enable_bcg729 +with_lyra +enable_lyra enable_libsrtp enable_libyuv enable_libwebrtc @@ -1574,6 +1578,7 @@ Optional Features: autodetect) --disable-bcg729 Disable bcg729 (default: not disabled) + --disable-lyra Disable lyra (default: not disabled) --disable-libsrtp Exclude libsrtp in the build --disable-libyuv Exclude libyuv in the build --disable-libwebrtc Exclude libwebrtc in the build @@ -1641,6 +1646,7 @@ Optional Packages: --with-silk=DIR Specify alternate SILK prefix --with-opus=DIR Specify alternate OPUS prefix --with-bcg729=DIR Specify alternate bcg729 prefix + --with-lyra=DIR Specify alternate lyra prefix Some influential environment variables: CC C compiler command @@ -10118,6 +10124,118 @@ fi +# Check whether --with-lyra was given. +if test ${with_lyra+y} +then : + withval=$with_lyra; +else $as_nop + with_lyra=no + +fi + + +if test "x$ac_cross_compile" != "x" -a "x$with_lyra" = "xno"; then + enable_lyra=no +fi + + + +# Check whether --enable-lyra was given. +if test ${enable_lyra+y} +then : + enableval=$enable_lyra; + if test "$enable_lyra" = "no"; then + ac_no_lyra_codec=1 + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 0" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Checking if lyra is disabled... yes" >&5 +printf "%s\n" "Checking if lyra is disabled... yes" >&6; } + fi + +else $as_nop + + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using lyra prefix... $with_lyra" >&5 +printf "%s\n" "Using lyra prefix... $with_lyra" >&6; } + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking lyra usability" >&5 +printf %s "checking lyra usability... " >&6; } + + LYRA_LIBS="-llyra" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" + + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include "lyra_decoder.h" + +int +main (void) +{ +std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,""); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO" +then : + + ac_lyra_model_path="$LYRA_PREFIX/model_coeffs" + printf "%s\n" "#define PJMEDIA_HAS_LYRA_CODEC 1" >>confdefs.h + + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + +else $as_nop + + ac_no_lyra_codec=1 + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + CPPFLAGS="$SAVED_CPPFLAGS" + +fi + + + + # Check whether --enable-libsrtp was given. if test ${enable_libsrtp+y} diff --git a/aconfigure.ac b/aconfigure.ac index e49148fd79..cea9d52090 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -2311,6 +2311,84 @@ AC_ARG_ENABLE(bcg729, ]) +dnl # lyra prefix +AC_ARG_WITH(lyra, + AS_HELP_STRING([--with-lyra=DIR], + [Specify alternate lyra prefix]), + [], + [with_lyra=no] + ) + +dnl # Do not use default lyra installation if we are cross-compiling +if test "x$ac_cross_compile" != "x" -a "x$with_lyra" = "xno"; then + enable_lyra=no +fi + +dnl # lyra +AC_SUBST(ac_no_lyra_codec) +AC_SUBST(ac_lyra_model_path) +AC_ARG_ENABLE(lyra, + AS_HELP_STRING([--disable-lyra], + [Disable lyra (default: not disabled)]), + [ + if test "$enable_lyra" = "no"; then + [ac_no_lyra_codec=1] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,0) + AC_MSG_RESULT([Checking if lyra is disabled... yes]) + fi + ], + [ + if test "x$with_lyra" != "xno" -a "x$with_lyra" != "x"; then + LYRA_PREFIX=$with_lyra + dnl # inside autoconf, single quoted preprocessor definition will raise error + LYRA_CPPFLAGS="-DGLOG_DEPRECATED=__attribute__((deprecated)) -DGLOG_EXPORT=__attribute__((visibility(\"default\"))) -DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))" + LYRA_CXXFLAGS="-std=c++17 -Wno-deprecated-builtins -I$LYRA_PREFIX/include/com_google_absl -I$LYRA_PREFIX/include/gulrak_filesystem -I$LYRA_PREFIX/include/com_google_glog/src -I$LYRA_PREFIX" + LYRA_LDFLAGS="-L$LYRA_PREFIX/lib" + AC_MSG_RESULT([Using lyra prefix... $with_lyra]) + else + LYRA_CXXFLAGS="" + LYRA_LDFLAGS="" + fi + + AC_MSG_CHECKING([lyra usability]) + + LYRA_LIBS="-llyra" + + SAVED_LIBS="$LIBS" + SAVED_LDFLAGS="$LDFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + SAVED_CPPFLAGS="$CPPFLAGS" + + LIBS="$LYRA_LIBS $LIBS" + LDFLAGS="$LYRA_LDFLAGS $LDFLAGS" + CXXFLAGS="$LYRA_CXXFLAGS $CXXFLAGS" + CPPFLAGS="$LYRA_CPPFLAGS $CPPFLAGS" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "lyra_decoder.h" + ]], + [std::unique_ptr dec = chromemedia::codec::LyraDecoder::Create(8000,1,"");] + )], + [ + [ac_lyra_model_path="$LYRA_PREFIX/model_coeffs"] + AC_DEFINE(PJMEDIA_HAS_LYRA_CODEC,1) + dnl # use single quoted preprocessor definition + LYRA_CPPFLAGS="'-DGLOG_DEPRECATED=__attribute__((deprecated))' '-DGLOG_EXPORT=__attribute__((visibility(\"default\")))' '-DGLOG_NO_EXPORT=__attribute__((visibility(\"default\")))'" + CXXFLAGS="$LYRA_CPPFLAGS $CXXFLAGS" + AC_MSG_RESULT(yes) + ], + [ + [ac_no_lyra_codec=1] + LIBS="$SAVED_LIBS" + LDFLAGS="$SAVED_LDFLAGS" + CXXFLAGS="$SAVED_CXXFLAGS" + AC_MSG_RESULT(no) + ]) + AC_LANG_POP([C++]) + CPPFLAGS="$SAVED_CPPFLAGS" + ]) + + dnl # Include libsrtp AC_SUBST(ac_no_srtp) diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in index 20a5b6f023..d815a330ba 100644 --- a/pjmedia/build/os-auto.mak.in +++ b/pjmedia/build/os-auto.mak.in @@ -74,6 +74,7 @@ AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@ AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@ AC_NO_BCG729=@ac_no_bcg729@ AC_NO_ANDROID_MEDIACODEC=@ac_no_mediacodec@ +AC_NO_LYRA_CODEC=@ac_no_lyra_codec@ export CODEC_OBJS= @@ -152,6 +153,13 @@ else export CODEC_OBJS += and_aud_mediacodec.o and_vid_mediacodec.o endif +ifeq ($(AC_NO_LYRA_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_LYRA_CODEC=0 +else +export CODEC_OBJS += lyra.o +export CFLAGS += -DPJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH='"@ac_lyra_model_path@"' +endif + # # SRTP # diff --git a/pjmedia/build/pjmedia_codec.vcxproj b/pjmedia/build/pjmedia_codec.vcxproj index 1d4cac128a..c11a4422b0 100644 --- a/pjmedia/build/pjmedia_codec.vcxproj +++ b/pjmedia/build/pjmedia_codec.vcxproj @@ -467,7 +467,6 @@ ../include;../../pjlib/include;../../third_party/speex/include;../../third_party;%(AdditionalIncludeDirectories) _LIB;%(PreprocessorDefinitions) - ..\lib\pjmedia-codec-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).lib @@ -689,6 +688,12 @@ + + GLOG_DEPRECATED=__declspec(deprecated);GLOG_EXPORT=;GLOG_NO_ABBREVIATED_SEVERITIES;GLOG_NO_EXPORT=; +EIGEN_MPL2_ONLY;EIGEN_MAX_ALIGN_BYTES=64;XNN_ENABLE_ASSEMBLY=1;XNN_ENABLE_JIT=0;XNN_ENABLE_SPARSE=1;XNN_LOG_LEVEL=2;PTHREADPOOL_NO_DEPRECATED_API;XNN_ENABLE_MEMOPT=1;XNN_WASMSIMD_VERSION=87;EIGEN_ALTIVEC_USE_CUSTOM_PACK=0;TFLITE_BUILD_WITH_XNNPACK_DELEGATE;_USE_MATH_DEFINES;__DATE__="redacted";__TIMESTAMP__="redacted";__TIME__="redacted";NOGDI;__PRETTY_FUNCTION__=__FUNCSIG__;_HAS_DEPRECATED_RESULT_OF=1;%(PreprocessorDefinitions) + stdcpp17 + 4117;4244;4267%(DisableSpecificWarnings) + @@ -715,6 +720,7 @@ + diff --git a/pjmedia/build/pjmedia_codec.vcxproj.filters b/pjmedia/build/pjmedia_codec.vcxproj.filters index 2f888d6a54..484f4d9768 100644 --- a/pjmedia/build/pjmedia_codec.vcxproj.filters +++ b/pjmedia/build/pjmedia_codec.vcxproj.filters @@ -83,6 +83,9 @@ Source Files + + Source Files + @@ -163,5 +166,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h index 6609458087..49ef723931 100644 --- a/pjmedia/include/pjmedia-codec.h +++ b/pjmedia/include/pjmedia-codec.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h index 048ba2935b..5d7a15b43f 100644 --- a/pjmedia/include/pjmedia-codec/config.h +++ b/pjmedia/include/pjmedia-codec/config.h @@ -540,6 +540,75 @@ #endif + /** + * Enable Lyra codec. + * + * Default: 0 + */ +#ifndef PJMEDIA_HAS_LYRA_CODEC +# define PJMEDIA_HAS_LYRA_CODEC 0 +#endif + +/** + * Lyra default bitrate setting + * + * Default: 3200 (available bitrate:3200, 6000, 9200) + */ +#ifndef PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE +# define PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE 3200 +#endif + +/** + * Lyra default model path containing lyra_config.binarypb, lyragan.tflite, + * quantizer.tflite and soundstream_encoder.tflite file. If autoconf is used, + * it will be set to "[lyra src folder]/model_coeffs". + * + * Default: "model_coeffs" + */ +#ifndef PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH +# define PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH "model_coeffs" +#endif + +/** + * Settings to enable Lyra codec 8KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_8KHZ +# define PJMEDIA_CODEC_LYRA_HAS_8KHZ 0 +#endif + +/** + * Settings to enable Lyra codec 16KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 1 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_16KHZ +# define PJMEDIA_CODEC_LYRA_HAS_16KHZ 1 +#endif + +/** + * Settings to enable Lyra codec 32KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_32KHZ +# define PJMEDIA_CODEC_LYRA_HAS_32KHZ 0 +#endif + +/** + * Settings to enable Lyra codec 48KHz. This option is only used + * when PJMEDIA_HAS_LYRA_CODEC is enabled. + * + * Default: 0 + */ +#ifndef PJMEDIA_CODEC_LYRA_HAS_48KHZ +# define PJMEDIA_CODEC_LYRA_HAS_48KHZ 0 +#endif + /** * Specify if FFMPEG codecs are available. * diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in index 3cc91263ef..2a2301e914 100644 --- a/pjmedia/include/pjmedia-codec/config_auto.h.in +++ b/pjmedia/include/pjmedia-codec/config_auto.h.in @@ -98,6 +98,11 @@ #undef PJMEDIA_HAS_ANDROID_MEDIACODEC #endif +/* Lyra codec */ +#ifndef PJMEDIA_HAS_LYRA_CODEC +#undef PJMEDIA_HAS_LYRA_CODEC +#endif + #endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */ diff --git a/pjmedia/include/pjmedia-codec/lyra.h b/pjmedia/include/pjmedia-codec/lyra.h new file mode 100755 index 0000000000..70e6941215 --- /dev/null +++ b/pjmedia/include/pjmedia-codec/lyra.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 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_LYRA_H__ +#define __PJMEDIA_CODEC_LYRA_H__ + + /** + * @file pjmedia-codec/lyra.hpp + * @brief lyra codec. + */ + +#include + + /** + * @defgroup PJMED_LYRA lyra Codec + * @ingroup PJMEDIA_CODEC_CODECS + * @brief Implementation of lyra Codec + * @{ + * + * This section describes functions to initialize and register lyra codec + * factory to the codec manager. After the codec factory has been registered, + * application can use @ref PJMEDIA_CODEC API to manipulate the codec. + * + * Lyra codec supports 16-bit PCM audio signal with sampling rate of (8000Hz, + * 16000Hz, 32000Hz and 48000Hz), frame length 20ms, and resulting in + * bitrate 3200bps, 6000bps and 9200bps. + */ + +PJ_BEGIN_DECL + +/** + * Lyra codec setting; + */ +typedef struct pjmedia_codec_lyra_config +{ + /** + * The value represents the decoder bitrate requested by the receiver. + * Endpoints can be configured with different bitrates. For example, + * the local endpoint might be set to a bitrate of 3200, while + * the remote endpoint is set to 6000. In this scenario, the remote + * endpoint will send data at 3200 bitrate, while the local endpoint + * will send data at 6000 bitrate. Valid bitrate: 3200, 6000, 9200. + * By default it is set to PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE. + */ + unsigned bit_rate; + + /** + * Lyra required some additional (model) files, including + * \b lyra_config.binarypb , \b lyragan.tflite , \b quantizer.tflite and + * \b soundstream_encoder.tflite . + * This setting represents the folder containing the above files. + * The specified folder should contain these files. If an invalid folder + * is provided, the codec creation will fail. + */ + pj_str_t model_path; + +} pjmedia_codec_lyra_config; + +/** + * Initialize and register lyra codec factory to pjmedia endpoint. + * + * @param endpt The pjmedia endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_lyra_init(pjmedia_endpt *endpt); + +/** + * Unregister lyra codec factory from pjmedia endpoint and deinitialize + * the lyra codec library. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_codec_lyra_deinit(void); + +/** + * Get the default Lyra configuration. + * + * @param cfg Lyra codec configuration. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_codec_lyra_get_config( pjmedia_codec_lyra_config *cfg); + +/** + * Set the default Lyra configuration. + * + * @param cfg Lyra codec configuration. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_codec_lyra_set_config(const pjmedia_codec_lyra_config *cfg); + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJMEDIA_CODEC_LYRA_H__ */ + diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h index c3bad71e94..256aea9c55 100644 --- a/pjmedia/include/pjmedia-codec/types.h +++ b/pjmedia/include/pjmedia-codec/types.h @@ -83,6 +83,10 @@ enum pjmedia_audio_pt PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */ PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */ PJMEDIA_RTP_PT_OPUS, /**< OPUS */ + PJMEDIA_RTP_PT_LYRA_8, /**< LYRA @ 8KHz */ + PJMEDIA_RTP_PT_LYRA_16, /**< LYRA @ 16KHz */ + PJMEDIA_RTP_PT_LYRA_32, /**< LYRA @ 32KHz */ + PJMEDIA_RTP_PT_LYRA_48, /**< LYRA @ 48KHz */ #if PJMEDIA_CODEC_L16_HAS_8KHZ_MONO PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ #endif diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c index 0405d2994a..d928520f6d 100644 --- a/pjmedia/src/pjmedia-codec/audio_codecs.c +++ b/pjmedia/src/pjmedia-codec/audio_codecs.c @@ -142,6 +142,13 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, return status; #endif +#if PJMEDIA_HAS_LYRA_CODEC + /* Register Lyra */ + status = pjmedia_codec_lyra_init(endpt); + if (status != PJ_SUCCESS) + return status; +#endif + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia-codec/lyra.cpp b/pjmedia/src/pjmedia-codec/lyra.cpp new file mode 100755 index 0000000000..33fefd152d --- /dev/null +++ b/pjmedia/src/pjmedia-codec/lyra.cpp @@ -0,0 +1,726 @@ +/* + * Copyright (C)2024 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 +#include +#include +#include + +#if defined(PJMEDIA_HAS_LYRA_CODEC) && PJMEDIA_HAS_LYRA_CODEC != 0 + +#include "lyra_encoder.h" +#include "lyra_decoder.h" + +#define THIS_FILE "lyra.cpp" + +// Available bitrate : 3200, 6000, 9200 +#define LYRA_MAX_BITRATE 9200 +#define LYRA_DEFAULT_VAD 1 +#define LYRA_MAX_PATH_LEN 64 +#define LYRA_DEFAULT_PATH "model_coeffs" + +static char LYRA_STR[] = "lyra"; +static char BIT_RATE_STR[] = "bitrate"; + +using namespace chromemedia::codec; + +/* Prototypes for lyra factory */ +static pj_status_t lyra_test_alloc(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id); +static pj_status_t lyra_default_attr(pjmedia_codec_factory *factory, + const pjmedia_codec_info* id, + pjmedia_codec_param *attr); +static pj_status_t lyra_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]); +static pj_status_t lyra_alloc_codec(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec); +static pj_status_t lyra_dealloc_codec(pjmedia_codec_factory *factory, + pjmedia_codec *codec); + +/* Prototypes for lyra implementation. */ +static pj_status_t lyra_codec_init(pjmedia_codec *codec, + pj_pool_t *pool); +static pj_status_t lyra_codec_open(pjmedia_codec *codec, + pjmedia_codec_param *attr); +static pj_status_t lyra_codec_close(pjmedia_codec *codec); +static pj_status_t lyra_codec_modify(pjmedia_codec *codec, + const pjmedia_codec_param *attr); +static pj_status_t lyra_codec_parse(pjmedia_codec *codec, + void *pkt, + pj_size_t pkt_size, + const pj_timestamp *ts, + unsigned *frame_cnt, + pjmedia_frame frames[]); +static pj_status_t lyra_codec_encode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t lyra_codec_decode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output); +static pj_status_t lyra_codec_recover(pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output); + +/* Definition for lyra codec operations. */ +static pjmedia_codec_op lyra_op = +{ + &lyra_codec_init, + &lyra_codec_open, + &lyra_codec_close, + &lyra_codec_modify, + &lyra_codec_parse, + &lyra_codec_encode, + &lyra_codec_decode, + &lyra_codec_recover +}; + +/* Definition for lyra codec factory operations. */ +static pjmedia_codec_factory_op lyra_factory_op = +{ + &lyra_test_alloc, + &lyra_default_attr, + &lyra_enum_codecs, + &lyra_alloc_codec, + &lyra_dealloc_codec, + &pjmedia_codec_lyra_deinit +}; + +struct lyra_param +{ + int enabled; /* Is this mode enabled? */ + int pt; /* Payload type. */ + unsigned clock_rate; /* Default sampling rate to be used.*/ +}; + +/* Lyra factory */ +struct lyra_factory +{ + pjmedia_codec_factory base; + pjmedia_endpt *endpt; + pj_pool_t *pool; + char model_path_buf[LYRA_MAX_PATH_LEN]; + struct lyra_param param[4]; +}; + +/* lyra codec private data. */ +struct lyra_data +{ + pj_pool_t *pool; + pj_mutex_t *mutex; + pj_bool_t vad_enabled; + pj_bool_t plc_enabled; + unsigned samples_per_frame; + unsigned enc_bit_rate; + unsigned dec_bit_rate; + + std::unique_ptr enc; + std::unique_ptr dec; +}; + +/* Codec factory instance */ +static struct lyra_factory lyra_factory; + +static pjmedia_codec_lyra_config lyra_cfg; + +/* + * Initialize and register lyra codec factory to pjmedia endpoint. + */ +PJ_DEF(pj_status_t) pjmedia_codec_lyra_init(pjmedia_endpt *endpt) +{ + pj_status_t status; + pjmedia_codec_mgr *codec_mgr; + + PJ_ASSERT_RETURN(endpt, PJ_EINVAL); + + if (lyra_factory.pool != NULL) + return PJ_SUCCESS; + + /* Create the Lyra codec factory */ + lyra_factory.base.op = &lyra_factory_op; + lyra_factory.base.factory_data = &lyra_factory; + lyra_factory.endpt = endpt; + + lyra_factory.pool = pjmedia_endpt_create_pool(endpt, "lyra-factory", + 1024, 1024); + if (!lyra_factory.pool) { + PJ_LOG(2, (THIS_FILE, "Unable to create memory pool for Lyra codec")); + return PJ_ENOMEM; + } + lyra_factory.param[0].enabled = PJMEDIA_CODEC_LYRA_HAS_8KHZ; + lyra_factory.param[0].pt = PJMEDIA_RTP_PT_LYRA_8; + lyra_factory.param[0].clock_rate = 8000; + + lyra_factory.param[1].enabled = PJMEDIA_CODEC_LYRA_HAS_16KHZ; + lyra_factory.param[1].pt = PJMEDIA_RTP_PT_LYRA_16; + lyra_factory.param[1].clock_rate = 16000; + + lyra_factory.param[2].enabled = PJMEDIA_CODEC_LYRA_HAS_32KHZ; + lyra_factory.param[2].pt = PJMEDIA_RTP_PT_LYRA_32; + lyra_factory.param[2].clock_rate = 32000; + + lyra_factory.param[3].enabled = PJMEDIA_CODEC_LYRA_HAS_48KHZ; + lyra_factory.param[3].pt = PJMEDIA_RTP_PT_LYRA_48; + lyra_factory.param[3].clock_rate = 48000; + + /* Get the codec manager */ + codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); + if (!codec_mgr) { + PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); + status = PJ_EINVALIDOP; + goto on_codec_factory_error; + } + + /* Register the codec factory */ + status = pjmedia_codec_mgr_register_factory(codec_mgr, + &lyra_factory.base); + if (status != PJ_SUCCESS) { + PJ_LOG(2, (THIS_FILE, "Unable to register the codec factory")); + goto on_codec_factory_error; + } + + lyra_cfg.bit_rate = PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE; + pj_ansi_strxcpy(lyra_factory.model_path_buf, + PJMEDIA_CODEC_LYRA_DEFAULT_MODEL_PATH , + sizeof(lyra_factory.model_path_buf)); + lyra_cfg.model_path = pj_str(lyra_factory.model_path_buf); + + return PJ_SUCCESS; + +on_codec_factory_error: + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + return status; +} + +/* + * Unregister lyra codec factory from pjmedia endpoint and deinitialize + * the lyra codec library. + */ +PJ_DEF(pj_status_t) pjmedia_codec_lyra_deinit(void) +{ + pj_status_t status; + pjmedia_codec_mgr *codec_mgr; + + if (lyra_factory.pool == NULL) + return PJ_SUCCESS; + + /* Get the codec manager */ + codec_mgr = pjmedia_endpt_get_codec_mgr(lyra_factory.endpt); + if (!codec_mgr) { + PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + return PJ_EINVALIDOP; + } + + /* Unregister the codec factory */ + status = pjmedia_codec_mgr_unregister_factory(codec_mgr, + &lyra_factory.base); + if (status != PJ_SUCCESS) + PJ_LOG(2, (THIS_FILE, "Unable to unregister the codec factory")); + + /* Release the memory pool */ + pj_pool_release(lyra_factory.pool); + lyra_factory.pool = NULL; + lyra_factory.endpt = NULL; + + return status; +} + +/* Prototypes for lyra factory */ +static pj_status_t lyra_test_alloc(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info) +{ + unsigned i; + + PJ_UNUSED_ARG(factory); + + /* Type MUST be audio. */ + if (info->type != PJMEDIA_TYPE_AUDIO) + return PJMEDIA_CODEC_EUNSUP; + + /* Check encoding name. */ + if (pj_strcmp2(&info->encoding_name, LYRA_STR) != 0) + return PJMEDIA_CODEC_EUNSUP; + + /* Check clock-rate */ + for (i = 0; i < PJ_ARRAY_SIZE(lyra_factory.param); ++i) { + if (info->clock_rate == lyra_factory.param[i].clock_rate) { + return PJ_SUCCESS; + } + } + + /* Unsupported, or mode is disabled. */ + return PJMEDIA_CODEC_EUNSUP; +} + +static char *get_bit_rate_val(unsigned bit_rate) +{ + static char buf_3200[] = "3200"; + static char buf_6000[] = "6000"; + static char buf_9200[] = "9200"; + + switch (bit_rate) { + case 3200: + return buf_3200; + case 6000: + return buf_6000; + case 9200: + return buf_9200; + } + return 0; +} + +static pj_status_t lyra_default_attr(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec_param *attr) +{ + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + unsigned idx = 0; + pj_bzero(attr, sizeof(pjmedia_codec_param)); + attr->info.pt = (pj_uint8_t)id->pt; + attr->info.channel_cnt = 1; + + for (; idx < PJ_ARRAY_SIZE(lyra_factory.param); ++idx) { + if (lyra_factory.param[idx].enabled && + id->clock_rate == lyra_factory.param[idx].clock_rate) + { + break; + } + } + if (idx == PJ_ARRAY_SIZE(lyra_factory.param)) { + PJ_EINVAL; + } + attr->info.clock_rate = lyra_factory.param[idx].clock_rate; + attr->info.avg_bps = lyra_cfg.bit_rate; + attr->info.max_bps = LYRA_MAX_BITRATE; + + attr->info.pcm_bits_per_sample = 16; + attr->info.frm_ptime = 20; + attr->setting.frm_per_pkt = 1; + attr->setting.dec_fmtp.cnt = 1; + pj_strset2(&attr->setting.dec_fmtp.param[0].name, BIT_RATE_STR); + pj_strset2(&attr->setting.dec_fmtp.param[0].val, + get_bit_rate_val(lyra_cfg.bit_rate)); + + /* Default flags. */ + attr->setting.cng = 0; + attr->setting.plc = 0; + attr->setting.penh = 0; + attr->setting.vad = LYRA_DEFAULT_VAD; + + return PJ_SUCCESS; +} + +static pj_status_t lyra_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]) +{ + int i; + unsigned max_cnt; + + PJ_UNUSED_ARG(factory); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); + + max_cnt = *count; + *count = 0; + + for (i = PJ_ARRAY_SIZE(lyra_factory.param) - 1; + i >= 0 && *count < max_cnt; --i) + { + if (!lyra_factory.param[i].enabled) + continue; + + pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); + pj_strset2(&codecs[*count].encoding_name, LYRA_STR); + codecs[*count].pt = lyra_factory.param[i].pt; + codecs[*count].type = PJMEDIA_TYPE_AUDIO; + codecs[*count].clock_rate = lyra_factory.param[i].clock_rate; + codecs[*count].channel_cnt = 1; + + ++*count; + } + + return PJ_SUCCESS; +} + +static pj_status_t lyra_alloc_codec(pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec) +{ + pj_pool_t *pool; + struct lyra_data *lyra_data; + pjmedia_codec *codec; + pj_status_t status; + + PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + pool = pjmedia_endpt_create_pool(lyra_factory.endpt, "lyra%p", 2000, 2000); + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); + + lyra_data = PJ_POOL_ZALLOC_T(pool, struct lyra_data); + codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); + + status = pj_mutex_create_simple(pool, "lyra_mutex", &lyra_data->mutex); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + lyra_data->pool = pool; + lyra_data->dec_bit_rate = lyra_cfg.bit_rate; + + codec->op = &lyra_op; + codec->codec_data = lyra_data; + codec->factory = factory; + + *p_codec = codec; + return PJ_SUCCESS; +} + +static pj_status_t lyra_dealloc_codec(pjmedia_codec_factory *factory, + pjmedia_codec *codec) +{ + struct lyra_data *lyra_data; + + PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); + PJ_UNUSED_ARG(factory); + PJ_ASSERT_RETURN(factory == &lyra_factory.base, PJ_EINVAL); + + lyra_data = (struct lyra_data*)codec->codec_data; + if (lyra_data) { + pj_mutex_destroy(lyra_data->mutex); + lyra_data->mutex = NULL; + pj_pool_release(lyra_data->pool); + } + + return PJ_SUCCESS; +} + +/* Prototypes for lyra implementation. */ +static pj_status_t lyra_codec_init(pjmedia_codec *codec, + pj_pool_t *pool) +{ + PJ_UNUSED_ARG(codec); + PJ_UNUSED_ARG(pool); + return PJ_SUCCESS; +} + +static unsigned get_bit_rate_from_fmtp(pj_bool_t is_enc, + pjmedia_codec_param *attr) +{ + unsigned bit_rate = 0; + + pjmedia_codec_fmtp *fmtp = (is_enc?&attr->setting.enc_fmtp: + &attr->setting.dec_fmtp); + /* Get bit_rate from fmpt */ + for (unsigned i = 0; i < fmtp->cnt; ++i) { + if (pj_strcmp2(&fmtp->param[i].name, BIT_RATE_STR) == 0) + { + bit_rate = (pj_uint16_t) pj_strtoul(&fmtp->param[i].val); + break; + } + } + if (bit_rate == 0 || + (bit_rate != 3200 && bit_rate != 6000 && bit_rate != 9200)) + { + bit_rate = PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE; + } + return bit_rate; +} + +static pj_status_t lyra_codec_open(pjmedia_codec *codec, + pjmedia_codec_param *attr) +{ + ghc::filesystem::path model_path = lyra_cfg.model_path.ptr; + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + pj_mutex_lock(lyra_data->mutex); + + lyra_data->enc_bit_rate = get_bit_rate_from_fmtp(PJ_TRUE, attr); + lyra_data->dec_bit_rate = get_bit_rate_from_fmtp(PJ_FALSE, attr); + + lyra_data->vad_enabled = (attr->setting.vad != 0); + lyra_data->plc_enabled = (attr->setting.plc != 0); + + PJ_LOG(4, (THIS_FILE, "Opening codec, model_path=%.*s, chan_cnt=%d, " + "enc_bit_rate=%d, dec_bit_rate=%d, clockrate=%d, vad=%d", + (int)lyra_cfg.model_path.slen, lyra_cfg.model_path.ptr, + attr->info.channel_cnt, lyra_data->enc_bit_rate, + lyra_data->dec_bit_rate, attr->info.clock_rate, + lyra_data->vad_enabled)); + + lyra_data->enc = LyraEncoder::Create(attr->info.clock_rate, + attr->info.channel_cnt, + lyra_data->enc_bit_rate, + lyra_data->vad_enabled, + model_path); + if (lyra_data->enc == nullptr) { + PJ_LOG(2, (THIS_FILE, "Could not create lyra encoder")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + + lyra_data->dec = LyraDecoder::Create(attr->info.clock_rate, + attr->info.channel_cnt, + model_path); + if (lyra_data->dec == nullptr) { + PJ_LOG(2, (THIS_FILE, "Could not create lyra decoder")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + lyra_data->samples_per_frame = + attr->info.clock_rate / lyra_data->enc->frame_rate(); + + PJ_LOG(4, (THIS_FILE, "Done opening codec")); + + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_close(pjmedia_codec *codec) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + /* Destroy encoder*/ + if (lyra_data->enc) { + lyra_data->enc.reset(); + } + + /* Destroy decoder */ + if (lyra_data->dec) { + lyra_data->dec.reset(); + } + + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_modify(pjmedia_codec *codec, + const pjmedia_codec_param *attr) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + + lyra_data->plc_enabled = (attr->setting.plc != 0); + lyra_data->vad_enabled = (attr->setting.vad != 0); + + return PJ_SUCCESS; +} + +static pj_status_t lyra_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; + struct lyra_data* lyra_data = (struct lyra_data*)codec->codec_data; + unsigned frm_size = lyra_data->dec_bit_rate / (50 * 8); + pj_size_t orig_pkt_size = pkt_size; + + PJ_UNUSED_ARG(codec); + + PJ_ASSERT_RETURN(ts && frame_cnt && frames, PJ_EINVAL); + + pj_mutex_lock(lyra_data->mutex); + while (pkt_size >= frm_size && count < *frame_cnt) { + frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; + frames[count].buf = pkt; + frames[count].size = frm_size; + frames[count].timestamp.u64 = ts->u64 + + lyra_data->samples_per_frame * count; + pkt = ((char*)pkt) + frm_size; + pkt_size -= frm_size; + + ++count; + } + if (pkt_size != 0 && count == 1) { + frames[0].size = orig_pkt_size; + } + + *frame_cnt = count; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_encode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + int i = 0; + pj_int16_t *pcm_in = (pj_int16_t*)input->buf; + pj_size_t in_size = input->size >> 1; + std::vector encoded_data; + + pj_mutex_lock(lyra_data->mutex); + + if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { + output->size = 0; + output->buf = NULL; + output->timestamp = input->timestamp; + output->type = input->type; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; + } + + for (int proc_size = 0; + proc_size + samples_per_frame <= in_size; + proc_size += samples_per_frame, ++i) + { + auto encoded = lyra_data->enc->Encode( + absl::MakeConstSpan(pcm_in+proc_size, + samples_per_frame)); + if (!encoded.has_value()) { + PJ_LOG(2, (THIS_FILE, + "Unable to encode starting at samples at byte %d.", + proc_size)); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + encoded_data.insert(encoded_data.end(), + encoded.value().begin(), + encoded.value().end()); + + } + PJ_ASSERT_ON_FAIL(encoded_data.size() <= output_buf_len, + { pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFRMTOOSHORT; }); + + output->size = encoded_data.size(); + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + output->timestamp = input->timestamp; + std::copy(encoded_data.begin(), encoded_data.end(), (char*)output->buf); + + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_decode(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + unsigned samples_decoded = 0; + std::vector decoded_data; + + if (output_buf_len < (samples_per_frame << 1)) + return PJMEDIA_CODEC_EPCMTOOSHORT; + + pj_mutex_lock(lyra_data->mutex); + if (input) { + pj_uint8_t *in_data = (pj_uint8_t*)input->buf; + if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { + pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); + output->size = samples_per_frame << 1; + output->timestamp.u64 = input->timestamp.u64; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; + } + if (!lyra_data->dec->SetEncodedPacket( + absl::MakeConstSpan(in_data, input->size))) + { + PJ_LOG(4, (THIS_FILE, "Unable to set encoded packet")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + } + + while (samples_decoded < samples_per_frame) { + unsigned samples_to_request = samples_per_frame - samples_decoded; + + auto decoded = lyra_data->dec->DecodeSamples(samples_to_request); + if (!decoded.has_value()) { + PJ_LOG(4, (THIS_FILE, "Decode failed!")); + pj_mutex_unlock(lyra_data->mutex); + return PJMEDIA_CODEC_EFAILED; + } + samples_decoded += (unsigned)decoded->size(); + decoded_data.insert(decoded_data.end(), decoded.value().begin(), + decoded.value().end()); + } + std::copy(decoded_data.begin(), decoded_data.end(), + (pj_int16_t*)output->buf); + output->type = PJMEDIA_FRAME_TYPE_AUDIO; + output->size = samples_per_frame << 1; + if (input) { + output->timestamp.u64 = input->timestamp.u64; + } + pj_mutex_unlock(lyra_data->mutex); + return PJ_SUCCESS; +} + +static pj_status_t lyra_codec_recover(pjmedia_codec *codec, + unsigned output_buf_len, + struct pjmedia_frame *output) +{ + struct lyra_data *lyra_data = (struct lyra_data*)codec->codec_data; + unsigned samples_per_frame = lyra_data->samples_per_frame; + pj_status_t status; + + /* output_buf_len is unreferenced when building in Release mode */ + PJ_UNUSED_ARG(output_buf_len); + + pj_assert(samples_per_frame <= output_buf_len / 2); + + status = lyra_codec_decode(codec, NULL, output_buf_len, output); + + output->size = samples_per_frame << 1; + + return status; +} + +PJ_DEF(pj_status_t) +pjmedia_codec_lyra_get_config(pjmedia_codec_lyra_config *cfg) +{ + PJ_ASSERT_RETURN(cfg, PJ_EINVAL); + + pj_memcpy(cfg, &lyra_cfg, sizeof(pjmedia_codec_lyra_config)); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) +pjmedia_codec_lyra_set_config(const pjmedia_codec_lyra_config *cfg) +{ + if (cfg->bit_rate != 3200 && cfg->bit_rate != 6000 && + cfg->bit_rate != 9200) + { + return PJ_EINVAL; + } + lyra_cfg.bit_rate = cfg->bit_rate; + pj_strncpy_with_null(&lyra_cfg.model_path, &cfg->model_path, + PJ_ARRAY_SIZE(lyra_factory.model_path_buf)); + return PJ_SUCCESS; +} + +#if defined(_MSC_VER) +# pragma comment(lib, "liblyra") +#endif + +#endif diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp index a40c38df57..5c78de026e 100644 --- a/pjsip/include/pjsua2/endpoint.hpp +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -1794,6 +1794,21 @@ class Endpoint void setCodecOpusConfig(const CodecOpusConfig &opus_cfg) PJSUA2_THROW(Error); + /** + * Get codec Lyra config. + * + */ + CodecLyraConfig getCodecLyraConfig() const PJSUA2_THROW(Error); + + /** + * Set codec Lyra config. + * + * @param lyra_cfg Codec Lyra configuration. + * + */ + void setCodecLyraConfig(const CodecLyraConfig &lyra_cfg) + PJSUA2_THROW(Error); + /** * Enumerate all SRTP crypto-suite names. * diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index ef9edde9d0..0d1631908f 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -2641,6 +2641,36 @@ struct CodecOpusConfig void fromPj(const pjmedia_codec_opus_config &config); }; +/** + * Lyra codec setting; + */ +struct CodecLyraConfig +{ + /** + * The value represents the decoder bitrate requested by the receiver. + * Endpoints can be configured with different bitrates. For example, + * the local endpoint might be set to a bitrate of 3200, while + * the remote endpoint is set to 6000. In this scenario, the remote + * endpoint will send data at 3200 bitrate, while the local endpoint + * will send data at 6000 bitrate. Valid bitrate: 3200, 6000, 9200. + * By default it is set to PJMEDIA_CODEC_LYRA_DEFAULT_BIT_RATE. + */ + unsigned bitRate; + + /** + * Lyra required some additional (model) files, including + * \b lyra_config.binarypb , \b lyragan.tflite , \b quantizer.tflite and + * \b soundstream_encoder.tflite . + * This setting represents the folder containing the above files. + * The specified folder should contain these files. If an invalid folder + * is provided, the codec creation will fail. + */ + string modelPath; + + pjmedia_codec_lyra_config toPj() const; + void fromPj(const pjmedia_codec_lyra_config &config); +}; + /** * Detailed codec attributes used in configuring a video codec and in querying * the capability of video codec factories. diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp index 417a123cfd..487b095166 100644 --- a/pjsip/src/pjsua2/endpoint.cpp +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -2526,6 +2526,36 @@ void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg) #endif } +CodecLyraConfig Endpoint::getCodecLyraConfig() const PJSUA2_THROW(Error) +{ + CodecLyraConfig config; +#if defined(PJMEDIA_HAS_LYRA_CODEC) && (PJMEDIA_HAS_LYRA_CODEC!=0) + pjmedia_codec_lyra_config lyra_cfg; + + PJSUA2_CHECK_EXPR(pjmedia_codec_lyra_get_config(&lyra_cfg)); + config.fromPj(lyra_cfg); +#else + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); +#endif + + return config; +} + +void Endpoint::setCodecLyraConfig(const CodecLyraConfig &lyra_cfg) + PJSUA2_THROW(Error) +{ +#if defined(PJMEDIA_HAS_LYRA_CODEC) && (PJMEDIA_HAS_LYRA_CODEC!=0) + pjmedia_codec_lyra_config new_lyra_cfg; + new_lyra_cfg = lyra_cfg.toPj(); + + PJSUA2_CHECK_EXPR(pjmedia_codec_lyra_set_config(&new_lyra_cfg)); +#else + PJ_UNUSED_ARG(lyra_cfg); + + PJSUA2_RAISE_ERROR(PJ_ENOTSUP); +#endif +} + void Endpoint::clearCodecInfoList(CodecInfoVector &codec_list) { for (unsigned i=0;ionFrameRequested(frame_); frame->type = frame_.type; frame->size = PJ_MIN(frame_.buf.size(), frame_.size); - -#if ((defined(_MSVC_LANG) && _MSVC_LANG <= 199711L) || __cplusplus <= 199711L) - /* C++98 does not have Vector::data() */ - if (frame->size > 0) + +#if ((defined(_MSVC_LANG) && _MSVC_LANG <= 199711L) || __cplusplus <= 199711L) + /* C++98 does not have Vector::data() */ + if (frame->size > 0) pj_memcpy(frame->buf, &frame_.buf[0], frame->size); #else - /* Newer than C++98 */ + /* Newer than C++98 */ pj_memcpy(frame->buf, frame_.buf.data(), frame->size); -#endif +#endif return PJ_SUCCESS; @@ -2032,6 +2032,22 @@ void CodecOpusConfig::fromPj(const pjmedia_codec_opus_config &config) cbr = PJ2BOOL(config.cbr); } +pjmedia_codec_lyra_config CodecLyraConfig::toPj() const +{ + pjmedia_codec_lyra_config config; + + config.bit_rate = bitRate; + config.model_path = str2Pj(modelPath); + + return config; +} + +void CodecLyraConfig::fromPj(const pjmedia_codec_lyra_config &config) +{ + bitRate = config.bit_rate; + modelPath = pj2Str(config.model_path); +} + /////////////////////////////////////////////////////////////////////////////// void VidCodecParam::fromPj(const pjmedia_vid_codec_param ¶m) {