diff options
| author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-02-01 23:07:54 +0000 | 
|---|---|---|
| committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-02-01 23:07:54 +0000 | 
| commit | ab17079a991428c5c382b5467c16ed1ec1db200f (patch) | |
| tree | dd9ae80d1b2ea56bee67472c1bcaf80317703efa | |
| parent | c5bc7f7cedf38c276e0acb9cb4bc796c9073f0fb (diff) | |
[pjsip]
* update to version 1.8.5
* add code to allow selection of features via menuconfig
* add sound device backend for lantiq voip hardware
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@25298 3c298f89-4303-0410-b956-a3cf2f4a3e73
| -rw-r--r-- | package/pjsip/Config.in | 27 | ||||
| -rw-r--r-- | package/pjsip/Makefile | 44 | ||||
| -rw-r--r-- | package/pjsip/patches/000-aconf.patch | 42 | ||||
| -rw-r--r-- | package/pjsip/patches/100-pjsua_acc__snprintf.patch | 90 | ||||
| -rw-r--r-- | package/pjsip/patches/200-pjmedia_tapi_audiodev.patch | 1027 | ||||
| -rw-r--r-- | package/pjsip/patches/210-pjmedia_audiodev.patch | 23 | 
6 files changed, 1241 insertions, 12 deletions
| diff --git a/package/pjsip/Config.in b/package/pjsip/Config.in new file mode 100644 index 000000000..c5c709389 --- /dev/null +++ b/package/pjsip/Config.in @@ -0,0 +1,27 @@ +config PJSIP_SPEEX +	bool "Speex support" +	depends on PACKAGE_pjsip +	default n +	help +		Enable speex codec. + +config PJSIP_OSS +	bool "OSS support" +	depends on PACKAGE_pjsip +	default n +	help +		Enable OSS. + +config PJSIP_SOUND +	bool "Sound support" +	depends on PACKAGE_pjsip +	default n +	help +		Enable sound. + +config PJSIP_LTQ_TAPI +	bool "Lantiq TAPI sound device support" +	depends on PACKAGE_pjsip && TARGET_lantiq_xway +	default n +	help +		Enable Lantiq Tapi. diff --git a/package/pjsip/Makefile b/package/pjsip/Makefile index 6216ebfc9..e9acaeb81 100644 --- a/package/pjsip/Makefile +++ b/package/pjsip/Makefile @@ -1,13 +1,19 @@ +# +# Copyright (C) 2010-2011 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +#  include $(TOPDIR)/rules.mk  PKG_NAME:=pjsip -PKG_VERSION:=1.6 -PKG_RELEASE:=1 +PKG_VERSION:=1.8.5 +PKG_RELEASE:=2  PKG_SOURCE:=pjproject-$(PKG_VERSION).tar.bz2 -PKG_SOURCE_URL:=http://www.pjsip.org/release/1.6/ -#PKG_MD5SUM:=9b7dc52656f5cbec846a7ba3299f73bd +PKG_SOURCE_URL:=http://www.pjsip.org/release/$(PKG_VERSION)/ +PKG_MD5SUM:=f9aa9e58b4d697245eb4bfa7d81a54a3  PKG_INSTALL:=1  PKG_BUILD_DIR:=$(BUILD_DIR)/pjproject-$(PKG_VERSION) @@ -20,23 +26,36 @@ define Package/pjsip    TITLE:=Voip lib    URL:=http://www.pjsip.org/    DEPENDS:=+libuuid +  MAINTAINER:=John Crispin <blogic@openwrt.org> +endef + +define Package/pjsip/config +	source "$(SOURCE)/Config.in"  endef  CONFIGURE_ARGS += \ -	--disable-sound \ -	--disable-oss \ -	--disable-ext-sound \ -	--disable-speex-aec \  	--enable-g711-codec \  	--disable-l16-codec \ -	--disable-gsm-codec \  	--disable-g722-codec \  	--disable-g7221-codec \ -	--disable-speex-codec \  	--disable-ilbc-coder \  	--disable-libsamplerate \  	--disable-ipp \  	--disable-ssl \ +	$(call autoconf_bool,CONFIG_PJSIP_GSM,gsm-codec) \ +	$(call autoconf_bool,CONFIG_PJSIP_SPEEX,speex-aec) \ +	$(call autoconf_bool,CONFIG_PJSIP_OSS,oss) \ +	$(call autoconf_bool,CONFIG_PJSIP_SOUND,sound) \ +	$(call autoconf_bool,CONFIG_PJSIP_DEV_TAPI,ltq-tapi) + +ifeq ($(CONFIG_PJSIP_DEV_TAPI),y) +EXTRA_CFLAGS:=-I$(STAGING_DIR)/usr/include/drv_tapi -I$(STAGING_DIR)/usr/include/drv_vmmc +endif + +define Build/Configure +	(cd $(PKG_BUILD_DIR); autoconf aconfigure.ac > aconfigure) +	$(call Build/Configure/Default) +endef  define Build/InstallDev  	$(INSTALL_DIR) $(1)/usr/{include,lib} @@ -51,8 +70,9 @@ define Package/pjsip/install  endef  define Build/Compile -	$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR)/$(MAKE_PATH) +	CFLAGS="$(EXTRA_CFLAGS) $(TARGET_CPPFLAGS) $(EXTRA_CPPFLAGS)" \ +	LDFLAGS="$(TARGET_LDFLAGS) $(EXTRA_LDFLAGS)" \ +		$(MAKE) -C $(PKG_BUILD_DIR)/$(MAKE_PATH)  endef -  $(eval $(call BuildPackage,pjsip)) diff --git a/package/pjsip/patches/000-aconf.patch b/package/pjsip/patches/000-aconf.patch new file mode 100644 index 000000000..96c519aea --- /dev/null +++ b/package/pjsip/patches/000-aconf.patch @@ -0,0 +1,42 @@ +--- a/aconfigure.ac ++++ b/aconfigure.ac +@@ -48,7 +48,7 @@ +     CROSS_COMPILE=`echo ${CC} | sed 's/gcc//'` + fi +  +-if test "$AR" = ""; then AR="${CROSS_COMPILE}ar rv"; fi ++AR="${CROSS_COMPILE}ar rv" + AC_SUBST(AR) + if test "$LD" = ""; then LD="$CC"; fi + AC_SUBST(LD) +@@ -590,6 +590,15 @@ + 		AC_MSG_RESULT([Checking if external sound is set... yes]) + 	       fi] + 	      ) ++ ++AC_ARG_ENABLE(ltq_tapi, ++	      AC_HELP_STRING([--enable-ltq-tapi], ++			     [PJMEDIA will use ltq tapi backend]), ++	      [if test "$enable_ltq_tapi" = "yes"; then ++		[ac_pjmedia_snd=ltqtapi] ++		AC_MSG_RESULT([Checking if external sound is set... yes]) ++	       fi] ++	      ) +  + dnl # Include resampling small filter + AC_SUBST(ac_no_small_filter) +--- a/pjmedia/build/os-auto.mak.in ++++ b/pjmedia/build/os-auto.mak.in +@@ -118,4 +118,11 @@ + export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 + endif +  +- ++# ++# Lantiq tapi backend ++# ++ifeq ($(AC_PJMEDIA_SND),ltqtapi) ++export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 ++export PJMEDIA_AUDIODEV_OBJS += tapi_dev.o ++export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE=1 ++endif diff --git a/package/pjsip/patches/100-pjsua_acc__snprintf.patch b/package/pjsip/patches/100-pjsua_acc__snprintf.patch new file mode 100644 index 000000000..cb347213d --- /dev/null +++ b/package/pjsip/patches/100-pjsua_acc__snprintf.patch @@ -0,0 +1,90 @@ +--- a/pjsip/src/pjsua-lib/pjsua_acc.c ++++ b/pjsip/src/pjsua-lib/pjsua_acc.c +@@ -430,7 +430,7 @@ + 		     "<sip:%s%.*s%s:%d%s>",  + 		     beginquote, + 		     (int)t->local_name.host.slen, +-		     t->local_name.host.ptr, ++		     t->local_name.host.slen ? t->local_name.host.ptr : "", + 		     endquote, + 		     t->local_name.port, + 		     transport_param); +@@ -1158,18 +1158,18 @@ + 	len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE, + 			       "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s>%.*s", + 			       (int)acc->user_part.slen, +-			       acc->user_part.ptr, ++			       acc->user_part.slen ? acc->user_part.ptr : "", + 			       (acc->user_part.slen? "@" : ""), + 			       beginquote, + 			       (int)via_addr->slen, +-			       via_addr->ptr, ++			       via_addr->slen ? via_addr->ptr : "", + 			       endquote, + 			       rport, + 			       tp->type_name, + 			       (int)acc->cfg.contact_uri_params.slen, +-			       acc->cfg.contact_uri_params.ptr, ++			       acc->cfg.contact_uri_params.slen ? acc->cfg.contact_uri_params.ptr : "", + 			       (int)acc->cfg.contact_params.slen, +-			       acc->cfg.contact_params.ptr); ++			       acc->cfg.contact_params.slen ? acc->cfg.contact_params.ptr : ""); + 	if (len < 1) { + 	    PJ_LOG(1,(THIS_FILE, "URI too long")); + 	    pj_pool_release(pool); +@@ -2233,22 +2233,22 @@ +     contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, + 				     "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + 				     (int)acc->display.slen, +-				     acc->display.ptr, ++				     acc->display.slen ? acc->display.ptr : "", + 				     (acc->display.slen?" " : ""), + 				     (secure ? PJSUA_SECURE_SCHEME : "sip"), + 				     (int)acc->user_part.slen, +-				     acc->user_part.ptr, ++				     acc->user_part.slen ? acc->user_part.ptr : "", + 				     (acc->user_part.slen?"@":""), + 				     beginquote, + 				     (int)local_addr.slen, +-				     local_addr.ptr, ++				     local_addr.slen ? local_addr.ptr : "", + 				     endquote, + 				     local_port, + 				     transport_param, + 				     (int)acc->cfg.contact_uri_params.slen, +-				     acc->cfg.contact_uri_params.ptr, ++				     acc->cfg.contact_uri_params.slen ? acc->cfg.contact_uri_params.ptr : "", + 				     (int)acc->cfg.contact_params.slen, +-				     acc->cfg.contact_params.ptr); ++				     acc->cfg.contact_params.slen ? acc->cfg.contact_params.ptr : ""); +  +     return PJ_SUCCESS; + } +@@ -2390,22 +2390,22 @@ +     contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, + 				     "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + 				     (int)acc->display.slen, +-				     acc->display.ptr, ++				     acc->display.slen ? acc->display.ptr : "", + 				     (acc->display.slen?" " : ""), + 				     (secure ? PJSUA_SECURE_SCHEME : "sip"), + 				     (int)acc->user_part.slen, +-				     acc->user_part.ptr, ++				     acc->user_part.slen ? acc->user_part.ptr : "", + 				     (acc->user_part.slen?"@":""), + 				     beginquote, + 				     (int)local_addr.slen, +-				     local_addr.ptr, ++				     local_addr.slen ? local_addr.ptr : "", + 				     endquote, + 				     local_port, + 				     transport_param, + 				     (int)acc->cfg.contact_uri_params.slen, +-				     acc->cfg.contact_uri_params.ptr, ++				     acc->cfg.contact_uri_params.slen ? acc->cfg.contact_uri_params.ptr : "", + 				     (int)acc->cfg.contact_params.slen, +-				     acc->cfg.contact_params.ptr); ++				     acc->cfg.contact_params.slen ? acc->cfg.contact_params.ptr : ""); +  +     return PJ_SUCCESS; + } diff --git a/package/pjsip/patches/200-pjmedia_tapi_audiodev.patch b/package/pjsip/patches/200-pjmedia_tapi_audiodev.patch new file mode 100644 index 000000000..dc5ff2d5d --- /dev/null +++ b/package/pjsip/patches/200-pjmedia_tapi_audiodev.patch @@ -0,0 +1,1027 @@ +--- /dev/null ++++ b/pjmedia/src/pjmedia-audiodev/tapi_dev.c +@@ -0,0 +1,1024 @@ ++/****************************************************************************** ++ ++                               Copyright (c) 2010 ++                            Lantiq Deutschland GmbH ++                     Am Campeon 3; 85579 Neubiberg, Germany ++ ++  For licensing information, see the file 'LICENSE' in the root folder of ++  this software module. ++ ++******************************************************************************/ ++#include <pjmedia-audiodev/audiodev_imp.h> ++#include <pjmedia/errno.h> ++#include <pj/assert.h> ++#include <pj/pool.h> ++#include <pj/log.h> ++#include <pj/os.h> ++ ++/* Linux includes*/ ++#include <stdio.h> ++#include <string.h> ++#include <stdlib.h> ++#include <ctype.h> ++#include <sys/stat.h>  ++#include <fcntl.h> ++#include <sys/types.h> ++#include <sys/ioctl.h> ++#include <sys/select.h> ++#include <sys/time.h> ++#include <unistd.h> ++ ++#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE ++ ++/* TAPI includes*/ ++#include "drv_tapi_io.h" ++#include "vmmc_io.h" ++ ++/* Maximum 2 devices*/ ++#define TAPI_AUDIO_DEV_NUM          (1) ++#define TAPI_AUDIO_MAX_DEV_NUM      (2) ++#define TAPI_BASE_NAME              "TAPI" ++#define TAPI_LL_DEV_BASE_PATH       "/dev/vmmc" ++#define TAPI_LL_DEV_FIRMWARE_NAME   "/lib/firmware/danube_firmware.bin"  ++ ++#define TAPI_LL_DEV_SELECT_TIMEOUT_MS      (2000) ++#define TAPI_LL_DEV_MAX_PACKET_SIZE        (800) ++#define TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE   (12) ++#define TAPI_LL_DEV_ENC_FRAME_LEN_MS       (20) ++#define TAPI_LL_DEV_ENC_SMPL_PER_SEC       (8000) ++#define TAPI_LL_DEV_ENC_BITS_PER_SMPLS     (16) ++#define TAPI_LL_DEV_ENC_SMPL_PER_FRAME     (160) ++#define TAPI_LL_DEV_ENC_BYTES_PER_FRAME    (TAPI_LL_DEV_ENC_SMPL_PER_FRAME * (TAPI_LL_DEV_ENC_BITS_PER_SMPLS / 8)) ++ ++ ++#define FD_WIDTH_SET(fd, maxfd)   (maxfd) < (fd) ? (fd) : maxfd  ++ ++#define THIS_FILE     "tapi_dev.c" ++ ++#if 1 ++#   define TRACE_(x)    PJ_LOG(1,x) ++#else ++#   define TRACE_(x) ++#endif ++ ++typedef struct ++{ ++   pj_int32_t dev_fd; ++   pj_int32_t ch_fd[TAPI_AUDIO_DEV_NUM]; ++   pj_int8_t  data2phone_map[TAPI_AUDIO_DEV_NUM]; ++    ++} tapi_ctx; ++ ++ ++/* TAPI factory */ ++struct tapi_aud_factory ++{ ++   pjmedia_aud_dev_factory  base; ++   pj_pool_t         *pool; ++   pj_pool_factory   *pf; ++ ++   pj_uint32_t             dev_count; ++   pjmedia_aud_dev_info   *dev_info; ++ ++   tapi_ctx   dev_ctx; ++}; ++ ++typedef struct tapi_aud_factory tapi_aud_factory_t; ++ ++/* ++   Sound stream descriptor. ++**/ ++struct tapi_aud_stream ++{ ++   /* Base*/ ++   pjmedia_aud_stream   base;      /**< Base class.  */ ++   /* Pool*/ ++   pj_pool_t            *pool;     /**< Memory pool.       */ ++   /* Common settings.*/ ++   pjmedia_aud_param    param;     /**< Stream param.  */ ++   pjmedia_aud_rec_cb   rec_cb;    /**< Record callback.   */ ++   pjmedia_aud_play_cb  play_cb;   /**< Playback callback. */ ++   void                *user_data; /**< Application data.  */ ++ ++   pj_thread_desc       thread_desc; ++   pj_thread_t         *thread; ++   tapi_ctx            *dev_ctx; ++   pj_uint8_t           run_flag; ++   pj_timestamp         timestamp; ++}; ++ ++typedef struct tapi_aud_stream tapi_aud_stream_t; ++ ++/* Factory prototypes */ ++static pj_status_t factory_init(pjmedia_aud_dev_factory *f); ++static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); ++static unsigned    factory_get_dev_count(pjmedia_aud_dev_factory *f); ++static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, ++          unsigned index, ++          pjmedia_aud_dev_info *info); ++static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, ++           unsigned index, ++           pjmedia_aud_param *param); ++static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, ++           const pjmedia_aud_param *param, ++           pjmedia_aud_rec_cb rec_cb, ++           pjmedia_aud_play_cb play_cb, ++           void *user_data, ++           pjmedia_aud_stream **p_aud_strm); ++ ++/* Stream prototypes */ ++static pj_status_t stream_get_param(pjmedia_aud_stream *strm, ++          pjmedia_aud_param *param); ++static pj_status_t stream_get_cap(pjmedia_aud_stream *strm, ++              pjmedia_aud_dev_cap cap, ++              void *value); ++static pj_status_t stream_set_cap(pjmedia_aud_stream *strm, ++              pjmedia_aud_dev_cap cap, ++              const void *value); ++static pj_status_t stream_start(pjmedia_aud_stream *strm); ++static pj_status_t stream_stop(pjmedia_aud_stream *strm); ++static pj_status_t stream_destroy(pjmedia_aud_stream *strm); ++ ++static pjmedia_aud_dev_factory_op tapi_fact_op = ++  { ++      &factory_init, ++      &factory_destroy, ++      &factory_get_dev_count, ++      &factory_get_dev_info, ++      &factory_default_param, ++      &factory_create_stream ++  }; ++ ++static pjmedia_aud_stream_op tapi_strm_op = ++{ ++    &stream_get_param, ++    &stream_get_cap, ++    &stream_set_cap, ++    &stream_start, ++    &stream_stop, ++    &stream_destroy ++}; ++ ++static pj_int32_t tapi_dev_open(char* dev_path, const pj_int32_t ch_num) ++{ ++   char devname[128] = {0}; ++ ++   pj_ansi_sprintf(devname,"%s%u%u", dev_path, 1, ch_num); ++ ++   return open((const char*)devname, O_RDWR, 0644); ++} ++ ++static pj_status_t tapi_dev_binary_buffer_create( ++                     const char *pPath, ++                     pj_uint8_t **ppBuf, ++                     pj_uint32_t *pBufSz) ++{ ++   pj_status_t status = PJ_SUCCESS; ++   FILE *fd; ++   struct stat file_stat; ++ ++   /* Open binary file for reading*/ ++   fd = fopen(pPath, "rb"); ++   if (fd == NULL) { ++      TRACE_((THIS_FILE, "ERROR -  binary file %s open failed!\n", pPath)); ++      return PJ_EUNKNOWN; ++   } ++ ++   /* Get file statistics*/ ++   if (stat(pPath, &file_stat) != 0) { ++      TRACE_((THIS_FILE, "ERROR -  file %s statistics get failed!\n", pPath)); ++      return PJ_EUNKNOWN; ++   } ++ ++   *ppBuf = malloc(file_stat.st_size); ++   if (*ppBuf == NULL) { ++      TRACE_((THIS_FILE, "ERROR -  binary file %s memory allocation failed!\n", pPath)); ++      status = PJ_EUNKNOWN; ++ ++      goto on_exit; ++   } ++ ++   if (fread (*ppBuf, sizeof(pj_uint8_t), file_stat.st_size, fd) <= 0) { ++      TRACE_((THIS_FILE, "ERROR - file %s read failed!\n", pPath)); ++      status = PJ_EUNKNOWN; ++ ++      goto on_exit; ++   } ++ ++   *pBufSz = file_stat.st_size;  ++ ++on_exit: ++   if (fd != NULL) { ++      fclose(fd); ++   } ++ ++   if (*ppBuf != NULL && status != PJ_SUCCESS) { ++      free(*ppBuf); ++   } ++ ++   return status; ++} ++ ++static void tapi_dev_binary_buffer_delete(pj_uint8_t *pBuf) ++{ ++   if (pBuf != NULL) ++      free(pBuf); ++} ++ ++static pj_status_t tapi_dev_firmware_download( ++                     pj_int32_t fd, ++                     const char *pPath) ++{ ++   pj_status_t status = PJ_SUCCESS; ++   pj_uint8_t *pFirmware = NULL; ++   pj_uint32_t binSz = 0; ++   VMMC_IO_INIT vmmc_io_init; ++ ++   /* Create binary buffer*/ ++   status = tapi_dev_binary_buffer_create(pPath, &pFirmware, &binSz); ++   if (status != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - binary buffer create failed!\n")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   /* Download Voice Firmware*/ ++   memset(&vmmc_io_init, 0, sizeof(VMMC_IO_INIT)); ++   vmmc_io_init.pPRAMfw   = pFirmware; ++   vmmc_io_init.pram_size = binSz; ++ ++   status = ioctl(fd, FIO_FW_DOWNLOAD, &vmmc_io_init); ++   if (status != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR -  FIO_FW_DOWNLOAD ioctl failed!")); ++   } ++ ++   /* Delete binary buffer*/ ++   tapi_dev_binary_buffer_delete(pFirmware); ++ ++   return status; ++} ++ ++ ++static pj_status_t tapi_dev_start(tapi_aud_factory_t *f) ++{ ++   pj_status_t status = PJ_SUCCESS; ++   pj_uint8_t c; ++   IFX_TAPI_DEV_START_CFG_t tapistart; ++   IFX_TAPI_MAP_DATA_t datamap; ++   IFX_TAPI_ENC_CFG_t enc_cfg; ++   IFX_TAPI_LINE_VOLUME_t vol; ++ ++   /* Open device*/ ++   f->dev_ctx.dev_fd = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, 0); ++ ++   if (f->dev_ctx.dev_fd < 0) { ++      TRACE_((THIS_FILE, "ERROR - TAPI device open failed!")); ++      return PJ_EUNKNOWN; ++   } ++ ++   for (c = 0; c < TAPI_AUDIO_DEV_NUM; c++) { ++      f->dev_ctx.ch_fd[c] = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, TAPI_AUDIO_MAX_DEV_NUM - c); ++ ++      if (f->dev_ctx.dev_fd < 0) { ++         TRACE_((THIS_FILE, "ERROR - TAPI channel%d open failed!", c)); ++         return PJ_EUNKNOWN; ++      } ++       ++      f->dev_ctx.data2phone_map[c] = c & 0x1 ? 0 : 1; ++   } ++ ++   status = tapi_dev_firmware_download(f->dev_ctx.dev_fd, TAPI_LL_DEV_FIRMWARE_NAME); ++   if (status != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - Voice Firmware Download failed!")); ++      return PJ_EUNKNOWN; ++   } ++ ++   memset(&tapistart, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t)); ++   tapistart.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER; ++ ++   /* Start TAPI*/ ++   status = ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_START, &tapistart); ++   if (status != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_START ioctl failed")); ++      return PJ_EUNKNOWN; ++   } ++ ++ ++   for (c = 0; c < TAPI_AUDIO_DEV_NUM; c++) { ++      /* Perform mapping*/ ++      memset(&datamap, 0x0, sizeof(IFX_TAPI_MAP_DATA_t)); ++      datamap.nDstCh  = f->dev_ctx.data2phone_map[c]; ++      datamap.nChType = IFX_TAPI_MAP_TYPE_PHONE; ++ ++      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &datamap); ++ ++      if (status != PJ_SUCCESS) { ++         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_MAP_DATA_ADD ioctl failed")); ++         return PJ_EUNKNOWN; ++      } ++ ++      /* Set Line feed*/ ++      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY); ++ ++      if (status != PJ_SUCCESS) { ++         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed")); ++         return PJ_EUNKNOWN; ++      } ++ ++      /* Config encoder for linear stream*/ ++      memset(&enc_cfg, 0x0, sizeof(IFX_TAPI_ENC_CFG_t)); ++ ++      enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; ++      enc_cfg.nEncType  = IFX_TAPI_COD_TYPE_LIN16_8; ++ ++      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_ENC_CFG_SET, &enc_cfg); ++      if (status != PJ_SUCCESS) { ++         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_CFG_SET ioctl failed")); ++         return PJ_EUNKNOWN; ++      } ++ ++ ++      /* Suppress TAPI volume, otherwise PJSIP starts autogeneration!!!*/ ++      vol.nGainRx = -8; ++      vol.nGainTx = -8; ++ ++      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_PHONE_VOLUME_SET, &vol); ++      if (status != PJ_SUCCESS) { ++         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_PHONE_VOLUME_SET ioctl failed")); ++         return PJ_EUNKNOWN; ++      } ++   } ++ ++ ++   return status; ++} ++ ++static pj_status_t tapi_dev_stop(tapi_aud_factory_t *f) ++{ ++   pj_status_t status = PJ_SUCCESS; ++   pj_uint8_t c; ++    ++   /* Stop TAPI device*/ ++   if (ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_STOP ioctl failed")); ++      status = PJ_EUNKNOWN; ++   } ++ ++   /* Close device FD*/ ++   close(f->dev_ctx.dev_fd); ++ ++   /* Close channel FD*/ ++   for (c = TAPI_AUDIO_DEV_NUM; c > 0; c--) { ++      close(f->dev_ctx.ch_fd[TAPI_AUDIO_DEV_NUM-c]); ++   } ++ ++ ++   return status; ++} ++ ++static pj_status_t tapi_dev_codec_control(pj_int32_t fd, pj_uint8_t start) ++{ ++   if (ioctl(fd, start ? IFX_TAPI_ENC_START : IFX_TAPI_ENC_STOP, 0) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_%s ioctl failed!", ++         start ? "START" : "STOP")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   if (ioctl(fd, start ? IFX_TAPI_DEC_START : IFX_TAPI_DEC_STOP, 0) != IFX_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEC_%s ioctl failed!", ++         start ? "START" : "STOP")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++static pj_status_t tapi_dev_event_ONHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx) ++{ ++   PJ_LOG(1,(THIS_FILE, "TAPI: ONHOOK")); ++ ++   if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET, ++               IFX_TAPI_LINE_FEED_STANDBY) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   /* enc/dec stop*/ ++   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - codec start failed!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++static pj_status_t tapi_dev_event_OFFHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx) ++{ ++   PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK")); ++ ++   if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET, ++               IFX_TAPI_LINE_FEED_ACTIVE) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   /* enc/dec stop*/ ++   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - codec start failed!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++static pj_status_t tapi_dev_event_handler( ++                     tapi_aud_stream_t *stream) ++{ ++   tapi_ctx *dev_ctx = stream->dev_ctx; ++   pj_uint32_t dev_idx = stream->param.rec_id; ++   pj_status_t status = PJ_SUCCESS; ++   IFX_TAPI_EVENT_t tapiEvent; ++ ++   memset (&tapiEvent, 0, sizeof(tapiEvent)); ++ ++   tapiEvent.ch = dev_ctx->data2phone_map[dev_idx]; ++ ++   /* Get event*/ ++   status = ioctl(dev_ctx->dev_fd, IFX_TAPI_EVENT_GET, &tapiEvent); ++ ++   if ((status == PJ_SUCCESS) && (tapiEvent.id != IFX_TAPI_EVENT_NONE)) { ++      switch(tapiEvent.id) { ++         case IFX_TAPI_EVENT_FXS_ONHOOK: ++            status = tapi_dev_event_ONHOOK(dev_ctx, dev_idx); ++            break; ++         case IFX_TAPI_EVENT_FXS_OFFHOOK: ++            status = tapi_dev_event_OFFHOOK(dev_ctx, dev_idx); ++            break; ++         default: ++            break; ++      } ++   } ++ ++   return status; ++} ++ ++static pj_status_t tapi_dev_data_handler( ++                     tapi_aud_stream_t *stream) ++{ ++   pj_status_t status = PJ_SUCCESS; ++   tapi_ctx *dev_ctx = stream->dev_ctx; ++   pj_uint32_t dev_idx = stream->param.rec_id; ++   pj_uint8_t buf_rec[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0}; ++   pj_uint8_t buf_play[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0}; ++   pjmedia_frame frame_rec, frame_play; ++   pj_int32_t ret; ++ ++   /* Get data from driver*/ ++   ret = read(dev_ctx->ch_fd[dev_idx], buf_rec, sizeof(buf_rec)); ++   if (ret < 0) { ++      TRACE_((THIS_FILE, "ERROR - no data available from device!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   if (ret > 0) { ++      frame_rec.type = PJMEDIA_FRAME_TYPE_AUDIO; ++      frame_rec.buf  = buf_rec + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; ++      frame_rec.size = ret - TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; ++      frame_rec.timestamp.u64 = stream->timestamp.u64; ++ ++      status = stream->rec_cb(stream->user_data, &frame_rec); ++      if (status != PJ_SUCCESS) ++      { ++        PJ_LOG(1, (THIS_FILE, "rec_cb() failed %d", status)); ++      } ++ ++      frame_play.type = PJMEDIA_FRAME_TYPE_AUDIO; ++      frame_play.buf  = buf_play + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; ++      frame_play.size = TAPI_LL_DEV_ENC_BYTES_PER_FRAME; ++      frame_play.timestamp.u64 = stream->timestamp.u64; ++ ++      status = (*stream->play_cb)(stream->user_data, &frame_play); ++      if (status != PJ_SUCCESS) ++      { ++         PJ_LOG(1, (THIS_FILE, "play_cb() failed %d", status)); ++      } ++      else ++      { ++         memcpy(buf_play, buf_rec, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE); ++ ++         ret = write(dev_ctx->ch_fd[dev_idx], buf_play, sizeof(buf_play)); ++ ++         if (ret < 0) { ++            PJ_LOG(1, (THIS_FILE, "ERROR - device data writing failed!")); ++            return PJ_EUNKNOWN; ++         } ++ ++         if (ret == 0) { ++            PJ_LOG(1, (THIS_FILE, "ERROR - no data written to device!")); ++            return PJ_EUNKNOWN; ++         } ++      } ++ ++      stream->timestamp.u64 += TAPI_LL_DEV_ENC_SMPL_PER_FRAME; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++/* TAPI capture and playback thread. */ ++static int PJ_THREAD_FUNC tapi_dev_thread(void *arg) ++{ ++   tapi_aud_stream_t *strm = (struct tapi_aud_stream*)arg; ++   tapi_ctx *dev_ctx = strm->dev_ctx; ++   fd_set rfds, trfds; ++   pj_uint32_t width = 0; ++   struct timeval tv; ++   pj_uint32_t sretval; ++   pj_uint32_t dev_idx; ++ ++   PJ_LOG(1,(THIS_FILE, "TAPI: thread starting...")); ++ ++   if (strm->param.rec_id != strm->param.play_id) { ++      PJ_LOG(1,(THIS_FILE, "TAPI: thread exit - incorrect play/rec IDs")); ++      return 0; ++   } ++ ++   dev_idx = strm->param.rec_id; ++ ++   FD_ZERO(&rfds); ++ ++   FD_SET(dev_ctx->dev_fd, &rfds); ++   width = FD_WIDTH_SET(dev_ctx->dev_fd, width); ++ ++   FD_SET(dev_ctx->ch_fd[dev_idx], &rfds); ++   width = FD_WIDTH_SET(dev_ctx->ch_fd[dev_idx], width); ++ ++   tv.tv_sec = TAPI_LL_DEV_SELECT_TIMEOUT_MS / 1000; ++   tv.tv_usec = (TAPI_LL_DEV_SELECT_TIMEOUT_MS % 1000) * 1000;  ++ ++   strm->run_flag = 1; ++ ++   while(1) ++   { ++      /* Update the local file descriptor by the copy in the task parameter */ ++      memcpy((void *) &trfds, (void*) &rfds, sizeof(fd_set)); ++ ++      sretval = select(width + 1, &trfds, NULL, NULL, &tv);  ++ ++      if (!strm->run_flag) { ++         break; ++      } ++ ++      /* error or timeout on select */ ++      if (sretval <= 0) { ++         continue; ++      } ++ ++      /* Check device control channel*/ ++      if (FD_ISSET(dev_ctx->dev_fd, &trfds)) { ++         if (tapi_dev_event_handler(strm) != PJ_SUCCESS) { ++            PJ_LOG(1,(THIS_FILE, "TAPI: event hanldler failed!")); ++            break; ++         } ++      } ++ ++      /* Check device data channel*/ ++      if (FD_ISSET(dev_ctx->ch_fd[dev_idx], &trfds)) { ++         if (tapi_dev_data_handler(strm) != PJ_SUCCESS) { ++            PJ_LOG(1,(THIS_FILE, "TAPI: data hanldler failed!")); ++            break; ++         } ++      } ++   } ++ ++   PJ_LOG(1,(THIS_FILE, "TAPI: thread stopping...")); ++ ++   return 0; ++} ++ ++/**************************************************************************** ++ Factory operations ++ ****************************************************************************/ ++ ++/*  Init tapi audio driver. */ ++pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf) ++{ ++    struct tapi_aud_factory *f; ++    pj_pool_t *pool; ++ ++    TRACE_((THIS_FILE, "pjmedia_tapi_factory()")); ++ ++    pool = pj_pool_create(pf, "tapi", 512, 512, NULL); ++    f = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_factory); ++    f->pf = pf; ++    f->pool = pool; ++    f->base.op = &tapi_fact_op; ++ ++    return &f->base; ++} ++ ++/* API: init factory */ ++static pj_status_t factory_init(pjmedia_aud_dev_factory *f) ++{ ++   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++   pj_uint8_t c; ++ ++   TRACE_((THIS_FILE, "factory_init()")); ++ ++   /* Enumerate sound devices */ ++   af->dev_count = TAPI_AUDIO_DEV_NUM; ++ ++   af->dev_info = (pjmedia_aud_dev_info*) ++         pj_pool_calloc(af->pool, af->dev_count, sizeof(pjmedia_aud_dev_info)); ++ ++   for (c = 0; c < af->dev_count; c++) { ++      pj_ansi_sprintf(af->dev_info[c].name,"%s_%02d", TAPI_BASE_NAME, c); ++ ++      af->dev_info[c].input_count = af->dev_info[c].output_count = 1; ++      af->dev_info[c].default_samples_per_sec = TAPI_LL_DEV_ENC_SMPL_PER_SEC; ++      pj_ansi_strcpy(af->dev_info[c].driver, "/dev/vmmc"); ++ ++      af->dev_info[c].caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | ++                             PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | ++                             PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; ++ ++      af->dev_info[c].routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT ; ++   } ++ ++   /* Initialize TAPI device(s)*/ ++   if (tapi_dev_start(af) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - TAPI device init failed!")); ++      return PJ_EUNKNOWN; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++/* API: destroy factory */ ++static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) ++{ ++   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++   pj_pool_t *pool; ++   pj_status_t status = PJ_SUCCESS; ++ ++   TRACE_((THIS_FILE, "factory_destroy()")); ++ ++   /* Stop TAPI device*/ ++   if (tapi_dev_stop(f) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - TAPI device stop failed!")); ++      status = PJ_EUNKNOWN; ++   } ++ ++   pool = af->pool; ++   af->pool = NULL; ++   pj_pool_release(pool); ++ ++   return status; ++} ++ ++/* API: get number of devices */ ++static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) ++{ ++  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++  TRACE_((THIS_FILE, "factory_get_dev_count()")); ++ ++  return af->dev_count; ++} ++ ++/* API: get device info */ ++static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, ++          unsigned index, ++          pjmedia_aud_dev_info *info) ++{ ++  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++ ++  TRACE_((THIS_FILE, "factory_get_dev_info()")); ++  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV); ++ ++  pj_memcpy(info, &af->dev_info[index], sizeof(*info)); ++ ++  return PJ_SUCCESS; ++} ++ ++/* API: create default device parameter */ ++static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, ++           unsigned index, ++           pjmedia_aud_param *param) ++{ ++  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++  struct pjmedia_aud_dev_info *di = &af->dev_info[index]; ++ ++  TRACE_((THIS_FILE, "factory_default_param.")); ++  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV); ++ ++  pj_bzero(param, sizeof(*param)); ++  if (di->input_count && di->output_count) { ++    param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; ++    param->rec_id = index; ++    param->play_id = index; ++  } else if (di->input_count) { ++    param->dir = PJMEDIA_DIR_CAPTURE; ++    param->rec_id = index; ++    param->play_id = PJMEDIA_AUD_INVALID_DEV; ++  } else if (di->output_count) { ++    param->dir = PJMEDIA_DIR_PLAYBACK; ++    param->play_id = index; ++    param->rec_id = PJMEDIA_AUD_INVALID_DEV; ++  } else { ++    return PJMEDIA_EAUD_INVDEV; ++  } ++ ++  param->clock_rate        = di->default_samples_per_sec; ++  param->channel_count     = 1; ++  param->samples_per_frame = TAPI_LL_DEV_ENC_SMPL_PER_FRAME; ++  param->bits_per_sample   = TAPI_LL_DEV_ENC_BITS_PER_SMPLS; ++  param->flags             = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | di->caps; ++  param->output_route      = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; ++ ++  return PJ_SUCCESS; ++} ++ ++/* API: create stream */ ++static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, ++           const pjmedia_aud_param *param, ++           pjmedia_aud_rec_cb rec_cb, ++           pjmedia_aud_play_cb play_cb, ++           void *user_data, ++           pjmedia_aud_stream **p_aud_strm) ++{ ++   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; ++   pj_pool_t *pool; ++   struct tapi_aud_stream *strm; ++   pj_status_t status; ++ ++   TRACE_((THIS_FILE, "factory_create_stream()")); ++ ++   /* Can only support 16bits per sample */ ++   PJ_ASSERT_RETURN(param->bits_per_sample == TAPI_LL_DEV_ENC_BITS_PER_SMPLS, PJ_EINVAL); ++ ++   PJ_ASSERT_RETURN(param->clock_rate == TAPI_LL_DEV_ENC_SMPL_PER_SEC, PJ_EINVAL); ++ ++   PJ_ASSERT_RETURN(param->samples_per_frame == TAPI_LL_DEV_ENC_SMPL_PER_FRAME, PJ_EINVAL); ++ ++   /* Can only support bidirectional stream */ ++   PJ_ASSERT_RETURN(param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL); ++ ++   /* Initialize our stream data */ ++   pool = pj_pool_create(af->pf, "tapi-dev", 1000, 1000, NULL); ++   PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); ++ ++   strm = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_stream); ++   strm->pool      = pool; ++   strm->rec_cb    = rec_cb; ++   strm->play_cb   = play_cb; ++   strm->user_data = user_data; ++   pj_memcpy(&strm->param, param, sizeof(*param)); ++ ++   if ((strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) == 0) { ++      strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16; ++   } ++ ++   strm->timestamp.u64 = 0; ++   strm->dev_ctx = &(af->dev_ctx); ++ ++   /* Create and start the thread */ ++   status = pj_thread_create(pool, "tapi", &tapi_dev_thread, strm, 0, 0,  ++			      &strm->thread); ++   if (status != PJ_SUCCESS) { ++      stream_destroy(&strm->base); ++      return status; ++   } ++ ++   /* Done */ ++   strm->base.op = &tapi_strm_op; ++   *p_aud_strm = &strm->base; ++ ++   return PJ_SUCCESS; ++} ++ ++/**************************************************************************** ++ * Stream operations ++ */ ++/* API: Get stream info. */ ++static pj_status_t stream_get_param(pjmedia_aud_stream *s, ++                                    pjmedia_aud_param *pi) ++{ ++  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; ++ ++  PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); ++ ++  pj_memcpy(pi, &strm->param, sizeof(*pi)); ++  /* Update the volume setting */ ++  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, ++        &pi->output_vol) == PJ_SUCCESS) ++  { ++    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; ++  } ++ ++  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, ++        &pi->output_latency_ms) == PJ_SUCCESS) ++  { ++    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; ++  } ++ ++  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, ++        &pi->input_latency_ms) == PJ_SUCCESS) ++  { ++    pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; ++  } ++ ++  return PJ_SUCCESS; ++} ++ ++/* API: get capability */ ++static pj_status_t stream_get_cap(pjmedia_aud_stream *s, ++          pjmedia_aud_dev_cap cap, ++          void *pval) ++{ ++  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; ++#ifdef OLD_IMPL ++  OSStatus status = 0; ++  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL); ++ ++  if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm->queue) ++  { ++    Float32 vol; ++    status = AudioQueueGetParameter(strm->play_strm->queue, ++                                    kAudioQueueParam_Volume, &vol); ++    if (!status) ++    { ++      *(unsigned*)pval = (vol * 100); ++      return PJ_SUCCESS; ++    } ++  } ++  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm->queue) ++  { ++    Float32 lat; ++    UInt32 size = sizeof(lat); ++    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, ++                                     &size, &lat); ++    if (!status) ++    { ++      *(unsigned*)pval = lat * 1000; ++      return PJ_SUCCESS; ++    } ++  } ++  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && strm->play_strm->queue) ++  { ++    *(pjmedia_aud_dev_route*)pval = strm->param.output_route; ++    return PJ_SUCCESS; ++  } ++  else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm->queue) ++  { ++    Float32 lat; ++    UInt32 size = sizeof(lat); ++    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency, ++                                     &size, &lat); ++    if (!status) ++    { ++      *(unsigned*)pval = lat * 1000; ++      return PJ_SUCCESS; ++    } ++  } ++ ++  if (status) ++    PJ_LOG(1, (THIS_FILE, "AudioQueueGetParameter/AudioSessionGetProperty err %d", status)); ++  return PJMEDIA_EAUD_INVCAP; ++#else ++  return PJ_SUCCESS; ++#endif ++} ++ ++/* API: set capability */ ++static pj_status_t stream_set_cap(pjmedia_aud_stream *s, ++          pjmedia_aud_dev_cap cap, ++          const void *pval) ++{ ++  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; ++#ifdef OLD_IMPL ++  OSStatus status = 0; ++  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL); ++ ++  if (strm->play_strm->queue) ++    switch (cap) ++    { ++      case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING: ++      { ++        /* Output volume setting */ ++        unsigned vol = *(unsigned*)pval; ++        Float32 volume; ++ ++        if (vol > 100) ++          vol = 100; ++        volume = vol / 100.; ++        status = AudioQueueSetParameter(strm->play_strm->queue, kAudioQueueParam_Volume, ++                                        volume); ++        if (!status) ++        { ++          PJ_LOG(1, (THIS_FILE, "AudioQueueSetParameter err %d", status)); ++          return PJMEDIA_EAUD_SYSERR; ++        } ++        strm->param.output_vol = *(unsigned*)pval; ++        return PJ_SUCCESS; ++      } ++      case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE: ++      { ++        pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval; ++        UInt32 route = (r == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ? ++            kAudioSessionOverrideAudioRoute_Speaker : ++            kAudioSessionOverrideAudioRoute_None); ++ ++        status = AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, ++                                          sizeof(route), &route); ++        if (status) ++        { ++          PJ_LOG(1, (THIS_FILE, "AudioSessionSetProperty err %d", status)); ++          return PJMEDIA_EAUD_SYSERR; ++        } ++        strm->param.output_route = r; ++        return PJ_SUCCESS; ++      } ++      default: ++        return PJMEDIA_EAUD_INVCAP; ++    } ++ ++ ++  return PJMEDIA_EAUD_INVCAP; ++#else ++  return PJ_SUCCESS; ++#endif ++} ++ ++/* API: Start stream. */ ++static pj_status_t stream_start(pjmedia_aud_stream *s) ++{ ++   struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; ++   tapi_ctx *dev_ctx = strm->dev_ctx; ++   pj_uint32_t dev_idx; ++ ++   TRACE_((THIS_FILE, "stream_start()")); ++ ++   dev_idx = strm->param.rec_id; ++ ++   return PJ_SUCCESS; ++} ++ ++/* API: Stop stream. */ ++static pj_status_t stream_stop(pjmedia_aud_stream *s) ++{ ++   struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; ++   tapi_ctx *dev_ctx = strm->dev_ctx; ++   pj_uint32_t dev_idx; ++ ++   TRACE_((THIS_FILE, "stream_stop()")); ++ ++   dev_idx = strm->param.rec_id; ++ ++   /* enc/dec stop*/ ++   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) { ++      TRACE_((THIS_FILE, "ERROR - codec start failed!")); ++ ++      return PJ_EUNKNOWN; ++   } ++ ++   return PJ_SUCCESS; ++} ++ ++/* API: Destroy stream. */ ++static pj_status_t stream_destroy(pjmedia_aud_stream *s) ++{ ++   pj_status_t state = PJ_SUCCESS; ++   struct tapi_aud_stream *stream = (struct tapi_aud_stream*)s; ++   pj_pool_t *pool; ++ ++   PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); ++ ++   TRACE_((THIS_FILE, "stream_destroy()")); ++ ++   stream_stop(stream); ++ ++   stream->run_flag = 0; ++ ++   /* Stop the stream thread */ ++   if (stream->thread) ++   { ++      pj_thread_join(stream->thread); ++      pj_thread_destroy(stream->thread); ++      stream->thread = NULL; ++   } ++ ++   pool = stream->pool; ++   pj_bzero(stream, sizeof(stream)); ++   pj_pool_release(pool); ++ ++   return state; ++} ++ ++#endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */ diff --git a/package/pjsip/patches/210-pjmedia_audiodev.patch b/package/pjsip/patches/210-pjmedia_audiodev.patch new file mode 100644 index 000000000..8acba6ef4 --- /dev/null +++ b/package/pjsip/patches/210-pjmedia_audiodev.patch @@ -0,0 +1,23 @@ +--- a/pjmedia/src/pjmedia-audiodev/audiodev.c ++++ b/pjmedia/src/pjmedia-audiodev/audiodev.c +@@ -98,6 +98,10 @@ + pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf); + #endif +  ++#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE ++pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf); ++#endif ++ + #define MAX_DRIVERS	16 + #define MAX_DEVS	64 +  +@@ -404,6 +408,9 @@ + #if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO +     aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory; + #endif ++#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE ++    aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_tapi_factory; ++#endif +  +     /* Initialize each factory and build the device ID list */ +     for (i=0; i<aud_subsys.drv_cnt; ++i) { | 
