Index: /pjproject/branches/projects/aps-direct/build.symbian/bld.inf
===================================================================
--- /pjproject/branches/projects/aps-direct/build.symbian/bld.inf (revision 2433)
+++ /pjproject/branches/projects/aps-direct/build.symbian/bld.inf (revision 2434)
@@ -1,5 +1,2 @@
-#define SND_USE_NULL 0
-#define SND_USE_APS 0
-
prj_platforms
winscw
@@ -26,11 +23,5 @@
/* Sound device impl */
-#if SND_USE_NULL
- null_audio.mmp
-#elif SND_USE_APS
- symbian_audio_aps.mmp
-#else
- symbian_audio.mmp
-#endif
+symbian_audio.mmp
/* Applications */
Index: /pjproject/branches/projects/aps-direct/build.symbian/pjmedia.mmp
===================================================================
--- /pjproject/branches/projects/aps-direct/build.symbian/pjmedia.mmp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/build.symbian/pjmedia.mmp (revision 2434)
@@ -41,4 +41,5 @@
SOURCE codec.c
SOURCE conference.c
+SOURCE conf_switch.c
SOURCE echo_common.c
SOURCE echo_port.c
Index: /pjproject/branches/projects/aps-direct/build.symbian/symbian_audio.mmp
===================================================================
--- /pjproject/branches/projects/aps-direct/build.symbian/symbian_audio.mmp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/build.symbian/symbian_audio.mmp (revision 2434)
@@ -25,8 +25,4 @@
OPTION CW -lang c++
-
-//
-// GCCE optimization setting
-//
OPTION GCCE -O2 -fno-unit-at-a-time
@@ -34,5 +30,7 @@
MACRO PJ_SYMBIAN=1
+SOURCE nullsound.c
SOURCE symbian_sound.cpp
+SOURCE symbian_sound_aps.cpp
SYSTEMINCLUDE ..\pjlib\include
@@ -41,4 +39,7 @@
SYSTEMINCLUDE \epoc32\include
SYSTEMINCLUDE \epoc32\include\libc
+SYSTEMINCLUDE \epoc32\include\mmf\server
+SYSTEMINCLUDE \epoc32\include\mmf\common
+SYSTEMINCLUDE \epoc32\include\mda\common
SYSTEMINCLUDE \epoc32\include\mmf\plugin
Index: /pjproject/branches/projects/aps-direct/build.symbian/symbian_ua.mmp
===================================================================
--- /pjproject/branches/projects/aps-direct/build.symbian/symbian_ua.mmp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/build.symbian/symbian_ua.mmp (revision 2434)
@@ -1,53 +1,51 @@
-#define SND_USE_NULL 0
-#define SND_USE_APS 0
+#define SND_USE_APS 1
+#define SND_USE_VAS 0
-TARGET symbian_ua.exe
-TARGETTYPE exe
-UID 0x0 0xA000000D
+TARGET symbian_ua.exe
+TARGETTYPE exe
+UID 0x0 0xA000000D
-SOURCEPATH ..\pjsip-apps\src\symbian_ua
+SOURCEPATH ..\pjsip-apps\src\symbian_ua
-MACRO PJ_M_I386=1
-MACRO PJ_SYMBIAN=1
+MACRO PJ_M_I386=1
+MACRO PJ_SYMBIAN=1
// Source files
-SOURCE ua.cpp
-SOURCE main_symbian.cpp
+SOURCE ua.cpp
+SOURCE main_symbian.cpp
-DOCUMENT ua.h
+DOCUMENT ua.h
-START RESOURCE symbian_ua_reg.rss
+START RESOURCE symbian_ua_reg.rss
TARGETPATH \private\10003a3f\apps
END
-SYSTEMINCLUDE ..\pjlib\include
-SYSTEMINCLUDE ..\pjlib-util\include
-SYSTEMINCLUDE ..\pjnath\include
-SYSTEMINCLUDE ..\pjmedia\include
-SYSTEMINCLUDE ..\pjsip\include
+SYSTEMINCLUDE ..\pjlib\include
+SYSTEMINCLUDE ..\pjlib-util\include
+SYSTEMINCLUDE ..\pjnath\include
+SYSTEMINCLUDE ..\pjmedia\include
+SYSTEMINCLUDE ..\pjsip\include
-SYSTEMINCLUDE \epoc32\include
-SYSTEMINCLUDE \epoc32\include\libc
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\libc
-STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
-STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
-STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
-STATICLIBRARY libsrtp.lib
-STATICLIBRARY libgsmcodec.lib libspeexcodec.lib
+STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
+STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
+STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
+STATICLIBRARY libsrtp.lib
+STATICLIBRARY libgsmcodec.lib libspeexcodec.lib
+STATICLIBRARY symbian_audio.lib
-#if SND_USE_NULL
- STATICLIBRARY null_audio.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
-#elif SND_USE_APS
- STATICLIBRARY symbian_audio_aps.lib
- LIBRARY APSSession2.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
- MACRO PJMEDIA_SYM_SND_USE_APS=1
+#if SND_USE_APS
+ LIBRARY APSSession2.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#elif SND_USE_VAS
+// LIBRARY
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
#else
- STATICLIBRARY symbian_audio.lib
- LIBRARY mediaclientaudiostream.lib
- LIBRARY mediaclientaudioinputstream.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+ LIBRARY mediaclientaudiostream.lib
+ LIBRARY mediaclientaudioinputstream.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#endif
@@ -56,5 +54,5 @@
#endif
-LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib
+LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib
// The default 8KB seems to be insufficient with all bells and
Index: /pjproject/branches/projects/aps-direct/build.symbian/symsndtest.mmp
===================================================================
--- /pjproject/branches/projects/aps-direct/build.symbian/symsndtest.mmp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/build.symbian/symsndtest.mmp (revision 2434)
@@ -1,52 +1,44 @@
-#define SND_USE_NULL 0
-#define SND_USE_APS 0
+#define SND_USE_APS 1
+#define SND_USE_VAS 0
-TARGET symsndtest.exe
-TARGETTYPE exe
-UID 0x0 0xA000000E
+TARGET symsndtest.exe
+TARGETTYPE exe
+UID 0x0 0xA000000E
-SOURCEPATH ..\pjsip-apps\src\symsndtest
+SOURCEPATH ..\pjsip-apps\src\symsndtest
-MACRO PJ_M_I386=1
-MACRO PJ_SYMBIAN=1
+MACRO PJ_M_I386=1
+MACRO PJ_SYMBIAN=1
// Test files
-SOURCE app_main.cpp
-SOURCE main_symbian.cpp
+SOURCE app_main.cpp
+SOURCE main_symbian.cpp
-START RESOURCE symsndtest_reg.rss
+START RESOURCE symsndtest_reg.rss
TARGETPATH \private\10003a3f\apps
END
-SYSTEMINCLUDE ..\pjlib\include
-SYSTEMINCLUDE ..\pjmedia\include
+SYSTEMINCLUDE ..\pjlib\include
+SYSTEMINCLUDE ..\pjmedia\include
-SYSTEMINCLUDE \epoc32\include
-SYSTEMINCLUDE \epoc32\include\libc
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\libc
-LIBRARY charconv.lib euser.lib estlib.lib
-LIBRARY esock.lib insock.lib
-STATICLIBRARY pjlib.lib pjmedia.lib
+LIBRARY charconv.lib euser.lib estlib.lib
+LIBRARY esock.lib insock.lib
+STATICLIBRARY pjlib.lib pjmedia.lib
+STATICLIBRARY symbian_audio.lib
-#if SND_USE_NULL
- STATICLIBRARY null_audio.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
-#elif SND_USE_APS
- SOURCEPATH ..\pjmedia\src\pjmedia
- SOURCE symbian_sound_aps.cpp
-
- SYSTEMINCLUDE \epoc32\include\mmf\server
- SYSTEMINCLUDE \epoc32\include\mmf\common
- SYSTEMINCLUDE \epoc32\include\mda\common
-
- //STATICLIBRARY symbian_audio_aps.lib
- LIBRARY APSSession2.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#if SND_USE_APS
+ LIBRARY APSSession2.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#elif SND_USE_VAS
+// LIBRARY
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
#else
- STATICLIBRARY symbian_audio.lib
- LIBRARY mediaclientaudiostream.lib
- LIBRARY mediaclientaudioinputstream.lib
- CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+ LIBRARY mediaclientaudiostream.lib
+ LIBRARY mediaclientaudioinputstream.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#endif
Index: /pjproject/branches/projects/aps-direct/pjmedia/build/pjmedia.vcproj
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/build/pjmedia.vcproj (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/build/pjmedia.vcproj (revision 2434)
@@ -96,76 +96,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia-codec/config.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia-codec/config.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia-codec/config.h (revision 2434)
@@ -195,4 +195,12 @@
#endif
+/**
+ * Enable Passthrough codecs.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_PASSTHROUGH_CODECS
+# define PJMEDIA_HAS_PASSTHROUGH_CODECS 0
+#endif
#endif /* __PJMEDIA_CODEC_CONFIG_H__ */
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/conference.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/conference.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/conference.h (revision 2434)
@@ -57,4 +57,5 @@
unsigned slot; /**< Slot number. */
pj_str_t name; /**< Port name. */
+ pjmedia_fourcc format; /**< Format (FourCC identifier) */
pjmedia_port_op tx_setting; /**< Transmit settings. */
pjmedia_port_op rx_setting; /**< Receive settings. */
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/config.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/config.h (revision 2434)
@@ -45,4 +45,20 @@
#endif
+/**
+ * Specify whether we prefer to use audio switch board rather than
+ * conference bridge.
+ *
+ * Audio switch board is a kind of simplified version of conference
+ * bridge, but not really the subset of conference bridge. It has
+ * stricter rules on audio routing among the pjmedia ports and has
+ * no audio mixing capability. The power of it is it could work with
+ * encoded audio frames where conference brigde couldn't.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_CONF_USE_SWITCH_BOARD
+# define PJMEDIA_CONF_USE_SWITCH_BOARD 0
+#endif
+
/*
* Types of sound stream backends.
@@ -60,4 +76,14 @@
/** Constant for Win32 MME sound backend. */
#define PJMEDIA_SOUND_WIN32_MME_SOUND 3
+
+/** Constant for Symbian Multimedia Audio Stream backend. */
+#define PJMEDIA_SOUND_SYMB_MDA_SOUND 4
+
+/** Constant for Symbian APS backend. */
+#define PJMEDIA_SOUND_SYMB_APS_SOUND 5
+
+/** Constant for Symbian VAS backend. */
+#define PJMEDIA_SOUND_SYMB_VAS_SOUND 6
+
/** When this is set, pjmedia will not provide any sound device backend.
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/port.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/port.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/port.h (revision 2434)
@@ -26,4 +26,5 @@
*/
#include
+#include
#include
@@ -212,4 +213,5 @@
pj_bool_t need_info; /**< Need info on connect? */
unsigned pt; /**< Payload type (can be dynamic). */
+ pjmedia_fourcc format; /**< Format (FourCC identifier) */
pj_str_t encoding_name; /**< Encoding name. */
unsigned clock_rate; /**< Sampling rate. */
@@ -227,5 +229,6 @@
{
PJMEDIA_FRAME_TYPE_NONE, /**< No frame. */
- PJMEDIA_FRAME_TYPE_AUDIO /**< Normal audio frame. */
+ PJMEDIA_FRAME_TYPE_AUDIO, /**< Normal audio frame. */
+ PJMEDIA_FRAME_TYPE_EXTENDED /**< Extended audio frame. */
} pjmedia_frame_type;
@@ -250,4 +253,86 @@
/**
+ * The pjmedia_frame_ext is used to carry a more complex audio frames than
+ * the typical PCM audio frames, and it is signaled by setting the "type"
+ * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
+ * application may typecast pjmedia_frame to pjmedia_frame_ext.
+ *
+ * This structure may contain more than one audio frames, which subsequently
+ * will be called subframes in this structure. The subframes section
+ * immediately follows the end of this structure, and each subframe is
+ * represented by pjmedia_frame_ext_subframe structure. Every next
+ * subframe immediately follows the previous subframe, and all subframes
+ * are byte-aligned although its payload may not be byte-aligned.
+ */
+typedef struct pjmedia_frame_ext {
+ pjmedia_frame base; /**< Base frame info */
+ pj_uint16_t samples_cnt; /**< Number of samples in this frame */
+ pj_uint16_t subframe_cnt; /**< Number of (sub)frames in this frame */
+
+ /* Zero or more (sub)frames follows immediately after this,
+ * each will be represented by pjmedia_frame_ext_subframe
+ */
+} pjmedia_frame_ext;
+
+/**
+ * This structure represents the individual subframes in the
+ * pjmedia_frame_ext structure.
+ */
+typedef struct pjmedia_frame_ext_subframe {
+ pj_uint16_t bitlen; /**< Number of bits in the data */
+ pj_uint8_t data[1]; /**< Start of encoded data */
+} pjmedia_frame_ext_subframe;
+
+
+/* Append one subframe to the frame_ext */
+PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
+ const void *src,
+ pj_uint16_t bitlen,
+ pj_uint16_t samples_cnt)
+{
+ pj_uint8_t *p;
+ unsigned i, tmp;
+
+ p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+ for (i = 0; i < frm->subframe_cnt; ++i) {
+ pjmedia_frame_ext_subframe *fsub;
+ fsub = (pjmedia_frame_ext_subframe*) p;
+ p += fsub->bitlen / 8;
+ if (fsub->bitlen % 8)
+ ++p;
+ }
+
+ tmp = bitlen / 8;
+ if (bitlen % 8) ++tmp;
+ pj_memcpy(p, &bitlen, sizeof(bitlen));
+ pj_memcpy(p + sizeof(bitlen), src, tmp);
+ frm->subframe_cnt++;
+ frm->samples_cnt = frm->samples_cnt + samples_cnt;
+}
+
+/* Get the pointer and length of the n-th subframe */
+PJ_INLINE(pjmedia_frame_ext_subframe*)
+ pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm,
+ unsigned n)
+{
+ pj_uint8_t *p;
+ unsigned i;
+ pjmedia_frame_ext_subframe *tmp;
+
+ pj_assert(n < frm->subframe_cnt);
+
+ p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+ for (i = 0; i < n; ++i) {
+ tmp = (pjmedia_frame_ext_subframe*) p;
+ p += tmp->bitlen / 8;
+ if (tmp->bitlen % 8)
+ ++p;
+ }
+
+ tmp = (pjmedia_frame_ext_subframe*) p;
+ return tmp;
+}
+
+/**
* Port interface.
*/
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/symbian_sound_aps.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/symbian_sound_aps.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/symbian_sound_aps.h (revision 2434)
@@ -32,4 +32,22 @@
/**
+ * Declaration of APS sound setting.
+ */
+typedef struct pjmedia_snd_aps_setting
+{
+ pjmedia_fourcc format; /**< Format (FourCC ID). */
+ pj_uint32_t bitrate; /**< Bitrate (bps). */
+ pj_uint32_t mode; /**< Mode, currently only used
+ for specifying iLBC mode,
+ 20ms or 30ms frame size. */
+ pj_bool_t plc; /**< PLC enabled/disabled. */
+ pj_bool_t vad; /**< VAD enabled/disabled. */
+ pj_bool_t cng; /**< CNG enabled/disabled. */
+ pj_bool_t loudspk; /**< Audio routed to loudspeaker.*/
+
+} pjmedia_snd_aps_setting;
+
+
+/**
* Activate/deactivate loudspeaker, when loudspeaker is inactive, audio
* will be routed to earpiece.
@@ -48,4 +66,15 @@
+/**
+ * Set a codec and its settings to be used on the next sound device session.
+ *
+ * @param setting APS sound device setting, see @pjmedia_snd_aps_setting.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_aps_modify_setting(
+ const pjmedia_snd_aps_setting *setting);
+
+
PJ_END_DECL
Index: /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/types.h
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/types.h (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/include/pjmedia/types.h (revision 2434)
@@ -48,6 +48,6 @@
*/
-/**
- * Top most media type.
+/**
+ * Top most media type.
*/
typedef enum pjmedia_type
@@ -62,5 +62,5 @@
PJMEDIA_TYPE_VIDEO = 2,
- /** Unknown media type, in this case the name will be specified in
+ /** Unknown media type, in this case the name will be specified in
* encoding_name.
*/
@@ -73,6 +73,6 @@
-/**
- * Media transport protocol.
+/**
+ * Media transport protocol.
*/
typedef enum pjmedia_tp_proto
@@ -93,6 +93,6 @@
-/**
- * Media direction.
+/**
+ * Media direction.
*/
typedef enum pjmedia_dir
@@ -139,6 +139,6 @@
-/**
- * Opague declaration of media endpoint.
+/**
+ * Opaque declaration of media endpoint.
*/
typedef struct pjmedia_endpt pjmedia_endpt;
@@ -151,5 +151,5 @@
-/**
+/**
* Media socket info is used to describe the underlying sockets
* to be used as media transport.
@@ -179,8 +179,32 @@
} pjmedia_sock_info;
+/**
+ * Declaration of FourCC type.
+ */
+typedef union pjmedia_fourcc {
+ pj_uint32_t u32;
+ char c[4];
+} pjmedia_fourcc;
+
+
+/**
+ * FourCC packing macro.
+ */
+#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C1<<24 | C2<<16 | C3<<8 | C4 )
+
+/**
+ * FourCC identifier definitions.
+ */
+#define PJMEDIA_FOURCC_L16 PJMEDIA_FOURCC_PACK(' ', 'L', '1', '6')
+#define PJMEDIA_FOURCC_G711A PJMEDIA_FOURCC_PACK('G', '7', '1', '1')
+#define PJMEDIA_FOURCC_G711U PJMEDIA_FOURCC_PACK('U', 'L', 'A', 'W')
+#define PJMEDIA_FOURCC_AMR PJMEDIA_FOURCC_PACK(' ', 'A', 'M', 'R')
+#define PJMEDIA_FOURCC_G729 PJMEDIA_FOURCC_PACK('G', '7', '2', '9')
+#define PJMEDIA_FOURCC_ILBC PJMEDIA_FOURCC_PACK('i', 'L', 'B', 'C')
+
/**
* This is a general purpose function set PCM samples to zero.
- * Since this function is needed by many parts of the library,
+ * Since this function is needed by many parts of the library,
* by putting this functionality in one place, it enables some.
* clever people to optimize this function.
@@ -206,5 +230,5 @@
/**
* This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library,
+ * equal size. Since this function is needed by many parts of the library,
* by putting this functionality in one place, it enables some.
* clever people to optimize this function.
@@ -221,5 +245,5 @@
unsigned i;
count >>= 1;
- for (i=0; i>= 1;
- for (i=0; i
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
+
+/* CONF_DEBUG enables detailed operation of the conference bridge.
+ * Beware that it prints large amounts of logs (several lines per frame).
+ */
+//#define CONF_DEBUG
+#ifdef CONF_DEBUG
+# include
+# define TRACE_(x) PJ_LOG(5,x)
+#else
+# define TRACE_(x)
+#endif
+
+
+/* REC_FILE macro enables recording of the samples written to the sound
+ * device. The file contains RAW PCM data with no header, and has the
+ * same settings (clock rate etc) as the conference bridge.
+ * This should only be enabled when debugging audio quality *only*.
+ */
+//#define REC_FILE "confrec.pcm"
+#ifdef REC_FILE
+static FILE *fhnd_rec;
+#endif
+
+
+#define THIS_FILE "conf_switch.c"
+
+#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'C')
+#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
+#define NORMAL_LEVEL 128
+#define SLOT_TYPE unsigned
+#define INVALID_SLOT ((SLOT_TYPE)-1)
+#define BUFFER_SIZE PJMEDIA_MAX_MTU
+
+/*
+ * DON'T GET CONFUSED WITH TX/RX!!
+ *
+ * TX and RX directions are always viewed from the conference bridge's point
+ * of view, and NOT from the port's point of view. So TX means the bridge
+ * is transmitting to the port, RX means the bridge is receiving from the
+ * port.
+ */
+
+
+/**
+ * This is a port connected to conference bridge.
+ */
+struct conf_port
+{
+ pj_str_t name; /**< Port name. */
+ pjmedia_port *port; /**< get_frame() and put_frame() */
+ pjmedia_port_op rx_setting; /**< Can we receive from this port */
+ pjmedia_port_op tx_setting; /**< Can we transmit to this port */
+ unsigned listener_cnt; /**< Number of listeners. */
+ SLOT_TYPE *listener_slots;/**< Array of listeners. */
+ unsigned transmitter_cnt;/**name, name);
+
+ /* Default has tx and rx enabled. */
+ conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
+ conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
+
+ /* Create transmit flag array */
+ conf_port->listener_slots = (SLOT_TYPE*)
+ pj_pool_zalloc(pool,
+ conf->max_ports * sizeof(SLOT_TYPE));
+ PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
+
+ /* Save some port's infos, for convenience. */
+ if (port) {
+ conf_port->port = port;
+ conf_port->clock_rate = port->info.clock_rate;
+ conf_port->samples_per_frame = port->info.samples_per_frame;
+ conf_port->channel_count = port->info.channel_count;
+ } else {
+ conf_port->port = NULL;
+ conf_port->clock_rate = conf->clock_rate;
+ conf_port->samples_per_frame = conf->samples_per_frame;
+ conf_port->channel_count = conf->channel_count;
+ }
+
+ /* Init pjmedia_frame structure in the TX buffer. */
+ f = (pjmedia_frame*)conf_port->tx_buf;
+ f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
+ f->size = 0;
+
+ /* Done */
+ *p_conf_port = conf_port;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create port zero for the sound device.
+ */
+static pj_status_t create_sound_port( pj_pool_t *pool,
+ pjmedia_conf *conf )
+{
+ struct conf_port *conf_port;
+ pj_str_t name = { "Master/sound", 12 };
+ pj_status_t status;
+
+
+ status = create_conf_port(pool, conf, NULL, &name, &conf_port);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Create sound device port: */
+
+ if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
+ pjmedia_snd_stream *strm;
+ pjmedia_snd_stream_info si;
+
+ /*
+ * If capture is disabled then create player only port.
+ * Otherwise create bidirectional sound device port.
+ */
+ if (conf->options & PJMEDIA_CONF_NO_MIC) {
+ status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,
+ conf->channel_count,
+ conf->samples_per_frame,
+ conf->bits_per_sample,
+ 0, /* options */
+ &conf->snd_dev_port);
+
+ } else {
+ status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate,
+ conf->channel_count,
+ conf->samples_per_frame,
+ conf->bits_per_sample,
+ 0, /* Options */
+ &conf->snd_dev_port);
+
+ }
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
+ status = pjmedia_snd_stream_get_info(strm, &si);
+ if (status == PJ_SUCCESS) {
+ const pjmedia_snd_dev_info *snd_dev_info;
+ if (conf->options & PJMEDIA_CONF_NO_MIC)
+ snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
+ else
+ snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
+ pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
+ }
+ }
+
+
+ /* Add the port to the bridge */
+ conf->ports[0] = conf_port;
+ conf->port_cnt++;
+
+
+ PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
+ unsigned max_ports,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned options,
+ pjmedia_conf **p_conf )
+{
+ pjmedia_conf *conf;
+ const pj_str_t name = { "Conf", 4 };
+ pj_status_t status;
+
+ /* Can only accept 16bits per sample, for now.. */
+ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+ PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
+ max_ports));
+
+ /* Create and init conf structure. */
+ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
+ PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
+
+ conf->ports = (struct conf_port**)
+ pj_pool_zalloc(pool, max_ports*sizeof(void*));
+ PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
+
+ conf->options = options;
+ conf->max_ports = max_ports;
+ conf->clock_rate = clock_rate;
+ conf->channel_count = channel_count;
+ conf->samples_per_frame = samples_per_frame;
+ conf->bits_per_sample = bits_per_sample;
+
+
+ /* Create and initialize the master port interface. */
+ conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
+ PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
+
+ pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
+ clock_rate, channel_count, bits_per_sample,
+ samples_per_frame);
+
+ conf->master_port->port_data.pdata = conf;
+ conf->master_port->port_data.ldata = 0;
+
+ conf->master_port->get_frame = &get_frame;
+ conf->master_port->put_frame = &put_frame;
+ conf->master_port->on_destroy = &destroy_port;
+
+
+ /* Create port zero for sound device. */
+ status = create_sound_port(pool, conf);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create mutex. */
+ status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* If sound device was created, connect sound device to the
+ * master port.
+ */
+ if (conf->snd_dev_port) {
+ status = pjmedia_snd_port_connect( conf->snd_dev_port,
+ conf->master_port );
+ if (status != PJ_SUCCESS) {
+ pjmedia_conf_destroy(conf);
+ return status;
+ }
+ }
+
+ /* Done */
+
+ *p_conf = conf;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Pause sound device.
+ */
+static pj_status_t pause_sound( pjmedia_conf *conf )
+{
+ /* Do nothing. */
+ PJ_UNUSED_ARG(conf);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Resume sound device.
+ */
+static pj_status_t resume_sound( pjmedia_conf *conf )
+{
+ /* Do nothing. */
+ PJ_UNUSED_ARG(conf);
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Destroy conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
+{
+ PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
+
+ /* Destroy sound device port. */
+ if (conf->snd_dev_port) {
+ pjmedia_snd_port_destroy(conf->snd_dev_port);
+ conf->snd_dev_port = NULL;
+ }
+
+ /* Destroy mutex */
+ pj_mutex_destroy(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy the master port (will destroy the conference)
+ */
+static pj_status_t destroy_port(pjmedia_port *this_port)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ return pjmedia_conf_destroy(conf);
+}
+
+/*
+ * Get port zero interface.
+ */
+PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
+{
+ /* Sanity check. */
+ PJ_ASSERT_RETURN(conf != NULL, NULL);
+
+ /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
+ * present in the option.
+ */
+ PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
+
+ return conf->master_port;
+}
+
+
+/*
+ * Set master port name.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
+ const pj_str_t *name)
+{
+ unsigned len;
+
+ /* Sanity check. */
+ PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
+
+ len = name->slen;
+ if (len > sizeof(conf->master_name_buf))
+ len = sizeof(conf->master_name_buf);
+
+ if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
+
+ conf->ports[0]->name.ptr = conf->master_name_buf;
+ conf->ports[0]->name.slen = len;
+
+ if (conf->master_port)
+ conf->master_port->info.name = conf->ports[0]->name;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Add stream port to the conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
+ pj_pool_t *pool,
+ pjmedia_port *strm_port,
+ const pj_str_t *port_name,
+ unsigned *p_port )
+{
+ struct conf_port *conf_port;
+ unsigned index;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
+ PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate,
+ PJMEDIA_ENCCLOCKRATE);
+ PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count,
+ PJMEDIA_ENCCHANNEL);
+ PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
+ PJMEDIA_ENCBITS);
+ PJ_ASSERT_RETURN((conf->samples_per_frame %
+ strm_port->info.samples_per_frame==0) ||
+ (strm_port->info.samples_per_frame %
+ conf->samples_per_frame==0),
+ PJMEDIA_ENCSAMPLESPFRAME);
+
+ /* If port_name is not specified, use the port's name */
+ if (!port_name)
+ port_name = &strm_port->info.name;
+
+ pj_mutex_lock(conf->mutex);
+
+ if (conf->port_cnt >= conf->max_ports) {
+ pj_assert(!"Too many ports");
+ pj_mutex_unlock(conf->mutex);
+ return PJ_ETOOMANY;
+ }
+
+ /* Find empty port in the conference bridge. */
+ for (index=0; index < conf->max_ports; ++index) {
+ if (conf->ports[index] == NULL)
+ break;
+ }
+
+ pj_assert(index != conf->max_ports);
+
+ /* Create conf port structure. */
+ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(conf->mutex);
+ return status;
+ }
+
+ /* Put the port. */
+ conf->ports[index] = conf_port;
+ conf->port_cnt++;
+
+ /* Done. */
+ if (p_port) {
+ *p_port = index;
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Add passive port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
+ pj_pool_t *pool,
+ const pj_str_t *name,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned options,
+ unsigned *p_slot,
+ pjmedia_port **p_port )
+{
+ PJ_UNUSED_ARG(conf);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(clock_rate);
+ PJ_UNUSED_ARG(channel_count);
+ PJ_UNUSED_ARG(samples_per_frame);
+ PJ_UNUSED_ARG(bits_per_sample);
+ PJ_UNUSED_ARG(options);
+ PJ_UNUSED_ARG(p_slot);
+ PJ_UNUSED_ARG(p_port);
+
+ return PJ_ENOTSUP;
+}
+
+
+
+/*
+ * Change TX and RX settings for the port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
+ unsigned slot,
+ pjmedia_port_op tx,
+ pjmedia_port_op rx)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ if (tx != PJMEDIA_PORT_NO_CHANGE)
+ conf_port->tx_setting = tx;
+
+ if (rx != PJMEDIA_PORT_NO_CHANGE)
+ conf_port->rx_setting = rx;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Connect port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
+ unsigned src_slot,
+ unsigned sink_slot,
+ int level )
+{
+ struct conf_port *src_port, *dst_port;
+ pj_bool_t start_sound = PJ_FALSE;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && src_slotmax_ports &&
+ sink_slotmax_ports, PJ_EINVAL);
+
+ /* Ports must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+ /* For now, level MUST be zero. */
+ PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
+
+ pj_mutex_lock(conf->mutex);
+
+ src_port = conf->ports[src_slot];
+ dst_port = conf->ports[sink_slot];
+
+ /* Check if source and sink has compatible format */
+ if (src_slot != 0 && sink_slot != 0 &&
+ src_port->port->info.format.u32 != dst_port->port->info.format.u32)
+ {
+ pj_mutex_unlock(conf->mutex);
+ return PJMEDIA_ENOTCOMPATIBLE;
+ }
+
+ /* Check if sink is listening to other ports */
+ if (dst_port->transmitter_cnt > 0) {
+ pj_mutex_unlock(conf->mutex);
+ return PJ_ETOOMANYCONN;
+ }
+
+ /* Check if connection has been made */
+ for (i=0; ilistener_cnt; ++i) {
+ if (src_port->listener_slots[i] == sink_slot)
+ break;
+ }
+
+ if (i == src_port->listener_cnt) {
+ src_port->listener_slots[src_port->listener_cnt] = sink_slot;
+ ++conf->connect_cnt;
+ ++src_port->listener_cnt;
+ ++dst_port->transmitter_cnt;
+
+ if (conf->connect_cnt == 1)
+ start_sound = 1;
+
+ PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
+ src_slot,
+ (int)src_port->name.slen,
+ src_port->name.ptr,
+ sink_slot,
+ (int)dst_port->name.slen,
+ dst_port->name.ptr));
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ /* Sound device must be started without mutex, otherwise the
+ * sound thread will deadlock (?)
+ */
+ if (start_sound)
+ resume_sound(conf);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Disconnect port
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
+ unsigned src_slot,
+ unsigned sink_slot )
+{
+ struct conf_port *src_port, *dst_port;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && src_slotmax_ports &&
+ sink_slotmax_ports, PJ_EINVAL);
+
+ /* Ports must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+ pj_mutex_lock(conf->mutex);
+
+ src_port = conf->ports[src_slot];
+ dst_port = conf->ports[sink_slot];
+
+ /* Check if connection has been made */
+ for (i=0; ilistener_cnt; ++i) {
+ if (src_port->listener_slots[i] == sink_slot)
+ break;
+ }
+
+ if (i != src_port->listener_cnt) {
+ pj_assert(src_port->listener_cnt > 0 &&
+ src_port->listener_cnt < conf->max_ports);
+ pj_assert(dst_port->transmitter_cnt > 0 &&
+ dst_port->transmitter_cnt < conf->max_ports);
+ pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
+ src_port->listener_cnt, i);
+ --conf->connect_cnt;
+ --src_port->listener_cnt;
+ --dst_port->transmitter_cnt;
+
+ PJ_LOG(4,(THIS_FILE,
+ "Port %d (%.*s) stop transmitting to port %d (%.*s)",
+ src_slot,
+ (int)src_port->name.slen,
+ src_port->name.ptr,
+ sink_slot,
+ (int)dst_port->name.slen,
+ dst_port->name.ptr));
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ if (conf->connect_cnt == 0) {
+ pause_sound(conf);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get number of ports currently registered to the conference bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
+{
+ return conf->port_cnt;
+}
+
+/*
+ * Get total number of ports connections currently set up in the bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
+{
+ return conf->connect_cnt;
+}
+
+
+/*
+ * Remove the specified port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
+ unsigned port )
+{
+ struct conf_port *conf_port;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[port] != NULL, PJ_EINVAL);
+
+ /* Suspend the sound devices.
+ * Don't want to remove port while port is being accessed by sound
+ * device's threads!
+ */
+
+ pj_mutex_lock(conf->mutex);
+
+ conf_port = conf->ports[port];
+ conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
+ conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
+
+ /* Remove this port from transmit array of other ports. */
+ for (i=0; imax_ports; ++i) {
+ unsigned j;
+ struct conf_port *src_port;
+
+ src_port = conf->ports[i];
+
+ if (!src_port)
+ continue;
+
+ if (src_port->listener_cnt == 0)
+ continue;
+
+ for (j=0; jlistener_cnt; ++j) {
+ if (src_port->listener_slots[j] == port) {
+ pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
+ src_port->listener_cnt, j);
+ pj_assert(conf->connect_cnt > 0);
+ --conf->connect_cnt;
+ --src_port->listener_cnt;
+ break;
+ }
+ }
+ }
+
+ /* Update transmitter_cnt of ports we're transmitting to */
+ while (conf_port->listener_cnt) {
+ unsigned dst_slot;
+ struct conf_port *dst_port;
+
+ dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
+ dst_port = conf->ports[dst_slot];
+ --dst_port->transmitter_cnt;
+ --conf_port->listener_cnt;
+ pj_assert(conf->connect_cnt > 0);
+ --conf->connect_cnt;
+ }
+
+ /* Remove the port. */
+ conf->ports[port] = NULL;
+ --conf->port_cnt;
+
+ pj_mutex_unlock(conf->mutex);
+
+
+ /* Stop sound if there's no connection. */
+ if (conf->connect_cnt == 0) {
+ pause_sound(conf);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum ports.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
+ unsigned ports[],
+ unsigned *p_count )
+{
+ unsigned i, count=0;
+
+ PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
+
+ for (i=0; imax_ports && count<*p_count; ++i) {
+ if (!conf->ports[i])
+ continue;
+
+ ports[count++] = i;
+ }
+
+ *p_count = count;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get port info
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
+ unsigned slot,
+ pjmedia_conf_port_info *info)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ pj_bzero(info, sizeof(pjmedia_conf_port_info));
+
+ info->slot = slot;
+ info->name = conf_port->name;
+ info->tx_setting = conf_port->tx_setting;
+ info->rx_setting = conf_port->rx_setting;
+ info->listener_cnt = conf_port->listener_cnt;
+ info->listener_slots = conf_port->listener_slots;
+ info->clock_rate = conf_port->clock_rate;
+ info->channel_count = conf_port->channel_count;
+ info->samples_per_frame = conf_port->samples_per_frame;
+ info->bits_per_sample = conf->bits_per_sample;
+ info->format = slot? conf_port->port->info.format :
+ conf->master_port->info.format;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
+ unsigned *size,
+ pjmedia_conf_port_info info[])
+{
+ unsigned i, count=0;
+
+ PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
+
+ for (i=0; imax_ports && count<*size; ++i) {
+ if (!conf->ports[i])
+ continue;
+
+ pjmedia_conf_get_port_info(conf, i, &info[count]);
+ ++count;
+ }
+
+ *size = count;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get signal level.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
+ unsigned slot,
+ unsigned *tx_level,
+ unsigned *rx_level)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ if (tx_level != NULL) {
+ *tx_level = conf_port->tx_level;
+ }
+
+ if (rx_level != NULL)
+ *rx_level = conf_port->rx_level;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust RX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
+ unsigned slot,
+ int adj_level )
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ /* Value must be from -128 to +127 */
+ /* Disabled, you can put more than +127, at your own risk:
+ PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+ */
+ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ /* Set normalized adjustment level. */
+ conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust TX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
+ unsigned slot,
+ int adj_level )
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ /* Value must be from -128 to +127 */
+ /* Disabled, you can put more than +127,, at your own risk:
+ PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+ */
+ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ return PJ_SUCCESS;
+}
+
+/* Deliver frm_src to a conference port (via frm_dst), eventually call
+ * port's put_frame() when samples count in the frm_dst are equal to
+ * port's samples_per_frame.
+ */
+static pj_status_t deliver_frame(struct conf_port *cport_dst,
+ pjmedia_frame *frm_dst,
+ const pjmedia_frame *frm_src)
+{
+ PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
+
+ if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+ pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
+ unsigned i;
+
+ /* Copy frame to listener's TX buffer. */
+ for (i = 0; i < f_src->subframe_cnt; ++i) {
+ pjmedia_frame_ext_subframe *sf;
+
+ sf = pjmedia_frame_ext_get_subframe(f_src, i);
+ pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
+ f_src->samples_cnt /
+ f_src->subframe_cnt);
+
+ /* Check if it's time to deliver the TX buffer to listener,
+ * i.e: samples count in TX buffer equal to listener's
+ * samples per frame.
+ */
+ if (f_dst->samples_cnt == cport_dst->samples_per_frame)
+ {
+ f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+ if (cport_dst->port) {
+ pjmedia_port_put_frame(cport_dst->port, (pjmedia_frame*)f_dst);
+
+ /* Reset TX buffer. */
+ f_dst->subframe_cnt = 0;
+ f_dst->samples_cnt = 0;
+ }
+ }
+ }
+
+ } else {
+
+ pjmedia_frame *f_dst = (pjmedia_frame*)frm_dst;
+ pj_int16_t *f_start, *f_end;
+
+ f_start = (pj_int16_t*)frm_src->buf;
+ f_end = f_start + (frm_src->size >> 1);
+ while (f_start < f_end) {
+ unsigned nsamples_to_copy, nsamples_req;
+
+ nsamples_to_copy = f_end - f_start;
+ nsamples_req = cport_dst->samples_per_frame - (f_dst->size >> 1);
+ if (nsamples_to_copy > nsamples_req)
+ nsamples_to_copy = nsamples_req;
+ pjmedia_copy_samples((pj_int16_t*)f_dst->buf + (f_dst->size >> 1),
+ f_start,
+ nsamples_to_copy);
+ f_dst->size += nsamples_to_copy << 1;
+ f_start += nsamples_to_copy;
+
+ /* Check if it's time to deliver the TX buffer to listener,
+ * i.e: samples count in TX buffer equal to listener's
+ * samples per frame.
+ */
+ if ((f_dst->size >> 1) == cport_dst->samples_per_frame)
+ {
+ f_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ if (cport_dst->port) {
+ pjmedia_port_put_frame(cport_dst->port, f_dst);
+
+ /* Reset TX buffer. */
+ f_dst->size = 0;
+ }
+ }
+ }
+
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Player callback.
+ */
+static pj_status_t get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ unsigned ci, i;
+
+ PJ_TODO(ADJUST_AND_CALC_RX_TX_LEVEL_FOR_PCM_FRAMES);
+
+ TRACE_((THIS_FILE, "- clock -"));
+
+ /* Must lock mutex */
+ pj_mutex_lock(conf->mutex);
+
+ /* Call get_frame() from all ports (except port 0) that has
+ * receiver and distribute the frame (put the frame to the destination
+ * port's buffer to accommodate different ptime, and ultimately call
+ * put_frame() of that port) to ports that are receiving from this port.
+ */
+ for (i=1, ci=1; imax_ports && ciport_cnt; ++i) {
+ struct conf_port *cport = conf->ports[i];
+
+ /* Skip empty port. */
+ if (!cport)
+ continue;
+
+ /* Var "ci" is to count how many ports have been visited so far. */
+ ++ci;
+
+ /* Skip if we're not allowed to receive from this port. */
+ if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
+ cport->rx_level = 0;
+ continue;
+ }
+
+ /* Also skip if this port doesn't have listeners. */
+ if (cport->listener_cnt == 0) {
+ cport->rx_level = 0;
+ continue;
+ }
+
+ pj_add_timestamp32(&cport->ts_clock, conf->samples_per_frame);
+
+ /* This loop will make sure the ptime between port & conf port
+ * are synchronized.
+ */
+ while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
+ pjmedia_frame *f = (pjmedia_frame*) conf->buf;
+ pj_status_t status;
+ unsigned j;
+
+ pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
+
+ f->buf = &conf->buf[sizeof(pjmedia_frame)];
+ f->size = BUFFER_SIZE - sizeof(pjmedia_frame);
+
+ /* Get frame from port. */
+ status = pjmedia_port_get_frame(cport->port, f);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ if (f->type == PJMEDIA_FRAME_TYPE_NONE) {
+ if (cport->port->info.format.u32 == PJMEDIA_FOURCC_L16) {
+ pjmedia_zero_samples((pj_int16_t*)f->buf,
+ cport->samples_per_frame);
+ f->size = cport->samples_per_frame << 1;
+ f->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ } else {
+ /* Handle DTX */
+ PJ_TODO(HANDLE_DTX);
+ }
+ }
+
+ /* Put the frame to all listeners. */
+ for (j=0; j < cport->listener_cnt; ++j)
+ {
+ struct conf_port *listener;
+ pjmedia_frame *frm_dst;
+
+ listener = conf->ports[cport->listener_slots[j]];
+
+ /* Skip if this listener doesn't want to receive audio */
+ if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
+ continue;
+
+ if (listener->port)
+ frm_dst = frame;
+ else
+ frm_dst = (pjmedia_frame*)listener->tx_buf;
+
+ status = deliver_frame(listener, frm_dst, f);
+ if (status != PJ_SUCCESS)
+ continue;
+ }
+ }
+
+ /* Keep alive mechanism. */
+ PJ_TODO(SEND_KEEP_ALIVE_WHEN_NEEDED);
+ }
+
+ /* Unlock mutex */
+ pj_mutex_unlock(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Recorder callback.
+ */
+static pj_status_t put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ struct conf_port *port = conf->ports[this_port->port_data.ldata];
+ unsigned j;
+
+ /* Check for correct size. */
+ PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
+ conf->bits_per_sample / 8,
+ PJMEDIA_ENCSAMPLESPFRAME);
+
+ /* Skip if this port is muted/disabled. */
+ if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
+ return PJ_SUCCESS;
+ }
+
+ /* Skip if no port is listening to the microphone */
+ if (port->listener_cnt == 0) {
+ return PJ_SUCCESS;
+ }
+
+ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
+ if (this_port->info.format.u32 == PJMEDIA_FOURCC_L16) {
+ pjmedia_frame *f = (pjmedia_frame*)port->tx_buf;
+
+ pjmedia_zero_samples((pj_int16_t*)f->buf,
+ port->samples_per_frame);
+ f->size = port->samples_per_frame << 1;
+ f->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame = f;
+ } else {
+ /* Handle DTX */
+ PJ_TODO(HANDLE_DTX);
+ }
+ }
+
+ /* Put the frame to all listeners. */
+ for (j=0; j < port->listener_cnt; ++j)
+ {
+ struct conf_port *listener;
+ pjmedia_frame *frm_dst;
+ pj_status_t status;
+
+ listener = conf->ports[port->listener_slots[j]];
+
+ /* Skip if this listener doesn't want to receive audio */
+ if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
+ continue;
+
+ /* Skip loopback for now. */
+ if (listener == port)
+ continue;
+
+ frm_dst = (pjmedia_frame*)listener->tx_buf;
+
+ status = deliver_frame(listener, frm_dst, frame);
+ if (status != PJ_SUCCESS)
+ continue;
+ }
+
+ return PJ_SUCCESS;
+}
+
+#endif
Index: /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conference.c
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conference.c (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conference.c (revision 2434)
@@ -34,4 +34,5 @@
#include
+#if !defined(PJMEDIA_CONF_USE_SWITCH_BOARD) || PJMEDIA_CONF_USE_SWITCH_BOARD==0
/* CONF_DEBUG enables detailed operation of the conference bridge.
@@ -1988,2 +1989,3 @@
}
+#endif
Index: /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound.cpp
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound.cpp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound.cpp (revision 2434)
@@ -24,4 +24,5 @@
#include
+#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_MDA_SOUND
/*
@@ -943,2 +944,4 @@
return PJ_SUCCESS;
}
+
+#endif
Index: /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound_aps.cpp
===================================================================
--- /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound_aps.cpp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound_aps.cpp (revision 2434)
@@ -18,4 +18,5 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include
#include
#include
@@ -26,4 +27,6 @@
#include
+#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
+
#include
#include
@@ -46,5 +49,5 @@
#endif
-static pjmedia_snd_dev_info symbian_snd_dev_info =
+static pjmedia_snd_dev_info symbian_snd_dev_info =
{
"Symbian Sound Device (APS)",
@@ -57,29 +60,45 @@
extern TPtrC APP_UID;
-/* Default setting for loudspeaker */
-static pj_bool_t act_loudspeaker = PJ_FALSE;
+/* Default setting */
+static pjmedia_snd_aps_setting def_setting;
+
+/* APS G.711 frame length */
+static pj_uint8_t aps_g711_frame_len;
+
+/* Pool factory */
+static pj_pool_factory *snd_pool_factory;
/* Forward declaration of CPjAudioEngine */
class CPjAudioEngine;
-/*
- * PJMEDIA Sound Stream instance
+/*
+ * PJMEDIA Sound Stream instance
*/
struct pjmedia_snd_stream
{
// Pool
- pj_pool_t *pool;
+ pj_pool_t *pool;
// Common settings.
- pjmedia_dir dir;
- unsigned clock_rate;
- unsigned channel_count;
- unsigned samples_per_frame;
+ pjmedia_dir dir;
+ unsigned clock_rate;
+ unsigned channel_count;
+ unsigned samples_per_frame;
+ pjmedia_snd_rec_cb rec_cb;
+ pjmedia_snd_play_cb play_cb;
+ void *user_data;
// Audio engine
- CPjAudioEngine *engine;
+ CPjAudioEngine *engine;
+
+ pj_timestamp ts_play;
+ pj_timestamp ts_rec;
+
+ pj_int16_t *play_buf;
+ pj_uint16_t play_buf_len;
+ pj_uint16_t play_buf_start;
+ pj_int16_t *rec_buf;
+ pj_uint16_t rec_buf_len;
};
-
-static pj_pool_factory *snd_pool_factory;
@@ -87,12 +106,14 @@
* Utility: print sound device error
*/
-static void snd_perror(const char *title, TInt rc)
+static void snd_perror(const char *title, TInt rc)
{
PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
}
-
+
//////////////////////////////////////////////////////////////////////////////
//
+typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data);
+
/**
* Abstract class for handler of callbacks from APS client.
@@ -101,10 +122,17 @@
{
public:
+ MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_,
+ void *UserData_)
+ : RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_)
+ {}
+
virtual void InputStreamInitialized(const TInt aStatus) = 0;
virtual void OutputStreamInitialized(const TInt aStatus) = 0;
virtual void NotifyError(const TInt aError) = 0;
- virtual void RecCb(TAPSCommBuffer &buffer) = 0;
- virtual void PlayCb(TAPSCommBuffer &buffer) = 0;
+public:
+ PjAudioCallback RecCb;
+ PjAudioCallback PlayCb;
+ void *UserData;
};
@@ -133,9 +161,11 @@
};
- static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
- RMsgQueue* aQ,
+ static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
+ RMsgQueue* aQ,
+ RMsgQueue* aWriteQ,
TQueueHandlerType aType)
{
- CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aType);
+ CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ,
+ aType);
CleanupStack::PushL(self);
self->ConstructL();
@@ -155,9 +185,10 @@
private:
// Constructor
- CQueueHandler(MQueueHandlerObserver* aObserver,
- RMsgQueue* aQ,
- TQueueHandlerType aType)
+ CQueueHandler(MQueueHandlerObserver* aObserver,
+ RMsgQueue* aQ,
+ RMsgQueue* aWriteQ,
+ TQueueHandlerType aType)
: CActive(CActive::EPriorityHigh),
- iQ(aQ), iObserver(aObserver), iType(aType)
+ iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
{
CActiveScheduler::Add(this);
@@ -178,5 +209,5 @@
iObserver->NotifyError(iStatus.Int());
return;
- }
+ }
TAPSCommBuffer buffer;
@@ -191,5 +222,7 @@
case ERecordQueue:
if (buffer.iCommand == EAPSRecordData) {
- iObserver->RecCb(buffer);
+ iObserver->RecCb(buffer, iObserver->UserData);
+ } else {
+ iObserver->NotifyError(buffer.iStatus);
}
break;
@@ -200,5 +233,6 @@
case EAPSPlayData:
if (buffer.iStatus == KErrUnderflow) {
- iObserver->PlayCb(buffer);
+ iObserver->PlayCb(buffer, iObserver->UserData);
+ iWriteQ->Send(buffer);
}
break;
@@ -225,12 +259,7 @@
// sent from the APS main thread through EPlayCommQueue
case EAPSRecorderInitialize:
- if (buffer.iStatus == KErrNone) {
- iObserver->InputStreamInitialized(buffer.iStatus);
- break;
- }
case EAPSRecordData:
+ default:
iObserver->NotifyError(buffer.iStatus);
- break;
- default:
break;
}
@@ -246,10 +275,28 @@
}
+ TInt RunError(TInt) {
+ return 0;
+ }
+
// Data
RMsgQueue *iQ; // (not owned)
+ RMsgQueue *iWriteQ; // (not owned)
MQueueHandlerObserver *iObserver; // (not owned)
TQueueHandlerType iType;
};
+/*
+ * Audio setting for CPjAudioEngine.
+ */
+class CPjAudioSetting
+{
+public:
+ TFourCC fourcc;
+ TAPSCodecMode mode;
+ TBool plc;
+ TBool vad;
+ TBool cng;
+ TBool loudspk;
+};
/*
@@ -269,7 +316,8 @@
static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
+ PjAudioCallback rec_cb,
+ PjAudioCallback play_cb,
+ void *user_data,
+ const CPjAudioSetting &setting);
TInt StartL();
@@ -280,13 +328,14 @@
private:
CPjAudioEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
+ PjAudioCallback rec_cb,
+ PjAudioCallback play_cb,
+ void *user_data,
+ const CPjAudioSetting &setting);
void ConstructL();
-
+
TInt InitPlayL();
TInt InitRecL();
TInt StartStreamL();
-
+
// Inherited from MQueueHandlerObserver
virtual void InputStreamInitialized(const TInt aStatus);
@@ -294,17 +343,12 @@
virtual void NotifyError(const TInt aError);
- virtual void RecCb(TAPSCommBuffer &buffer);
- virtual void PlayCb(TAPSCommBuffer &buffer);
-
State state_;
pjmedia_snd_stream *parentStrm_;
- pjmedia_snd_rec_cb recCb_;
- pjmedia_snd_play_cb playCb_;
- void *userData_;
- pj_uint32_t TsPlay_;
- pj_uint32_t TsRec_;
+ CPjAudioSetting setting_;
RAPSSession iSession;
- TAPSInitSettings iSettings;
+ TAPSInitSettings iPlaySettings;
+ TAPSInitSettings iRecSettings;
+
RMsgQueue iReadQ;
RMsgQueue iReadCommQ;
@@ -315,26 +359,17 @@
CQueueHandler *iRecCommHandler;
CQueueHandler *iRecHandler;
-
- static pj_uint8_t aps_samples_per_frame;
-
- pj_int16_t *play_buf;
- pj_uint16_t play_buf_len;
- pj_uint16_t play_buf_start;
- pj_int16_t *rec_buf;
- pj_uint16_t rec_buf_len;
};
-pj_uint8_t CPjAudioEngine::aps_samples_per_frame = 0;
-
-
CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
+ PjAudioCallback rec_cb,
+ PjAudioCallback play_cb,
+ void *user_data,
+ const CPjAudioSetting &setting)
{
CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
rec_cb, play_cb,
- user_data);
+ user_data,
+ setting);
CleanupStack::PushL(self);
self->ConstructL();
@@ -344,12 +379,12 @@
CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
- : state_(STATE_NULL),
+ PjAudioCallback rec_cb,
+ PjAudioCallback play_cb,
+ void *user_data,
+ const CPjAudioSetting &setting)
+ : MQueueHandlerObserver(rec_cb, play_cb, user_data),
+ state_(STATE_NULL),
parentStrm_(parent_strm),
- recCb_(rec_cb),
- playCb_(play_cb),
- userData_(user_data),
+ setting_(setting),
iPlayCommHandler(0),
iRecCommHandler(0),
@@ -362,8 +397,19 @@
Stop();
+ delete iRecHandler;
delete iPlayCommHandler;
- iPlayCommHandler = NULL;
delete iRecCommHandler;
- iRecCommHandler = NULL;
+
+ // On some devices, immediate closing after stopping may cause APS server
+ // panic KERN-EXEC 0, so let's wait for sometime before really closing
+ // the client session.
+ TTime start, now;
+ enum { APS_CLOSE_WAIT_TIME = 200 };
+ start.UniversalTime();
+ now.UniversalTime();
+ while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000) {
+ pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME);
+ now.UniversalTime();
+ }
iSession.Close();
@@ -384,5 +430,5 @@
return 0;
- TInt err = iSession.InitializePlayer(iSettings);
+ TInt err = iSession.InitializePlayer(iPlaySettings);
if (err != KErrNone) {
snd_perror("Failed to initialize player", err);
@@ -391,7 +437,7 @@
// Open message queues for the output stream
- TBuf<128> buf2 = iSettings.iGlobal;
+ TBuf<128> buf2 = iPlaySettings.iGlobal;
buf2.Append(_L("PlayQueue"));
- TBuf<128> buf3 = iSettings.iGlobal;
+ TBuf<128> buf3 = iPlaySettings.iGlobal;
buf3.Append(_L("PlayCommQueue"));
@@ -402,6 +448,5 @@
// Construct message queue handler
- iPlayCommHandler = CQueueHandler::NewL(this,
- &iWriteCommQ,
+ iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
CQueueHandler::EPlayCommQueue);
@@ -418,13 +463,13 @@
// Initialize input stream device
- TInt err = iSession.InitializeRecorder(iSettings);
- if (err != KErrNone) {
+ TInt err = iSession.InitializeRecorder(iRecSettings);
+ if (err != KErrNone && err != KErrAlreadyExists) {
snd_perror("Failed to initialize recorder", err);
return err;
}
- TBuf<128> buf1 = iSettings.iGlobal;
+ TBuf<128> buf1 = iRecSettings.iGlobal;
buf1.Append(_L("RecordQueue"));
- TBuf<128> buf4 = iSettings.iGlobal;
+ TBuf<128> buf4 = iRecSettings.iGlobal;
buf4.Append(_L("RecordCommQueue"));
@@ -437,11 +482,13 @@
// Construct message queue handlers
- iRecCommHandler = CQueueHandler::NewL(this,
- &iReadCommQ,
+ iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL,
+ CQueueHandler::ERecordQueue);
+ iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL,
CQueueHandler::ERecordCommQueue);
// Start observing APS callbacks from on input stream message queue
+ iRecHandler->Start();
iRecCommHandler->Start();
-
+
return 0;
}
@@ -449,12 +496,8 @@
TInt CPjAudioEngine::StartL()
{
- TInt err = iSession.Connect();
- if (err != KErrNone && err != KErrAlreadyExists)
- return err;
-
if (state_ == STATE_READY)
return StartStreamL();
- // Even if only capturer are opened, playback thread of APS Server need
+ // Even if only capturer are opened, playback thread of APS Server need
// to be run(?). Since some messages will be delivered via play comm queue.
return InitPlayL();
@@ -463,32 +506,31 @@
void CPjAudioEngine::Stop()
{
- iSession.Stop();
-
- delete iRecHandler;
- iRecHandler = NULL;
-
- state_ = STATE_READY;
+ if (state_ == STATE_STREAMING) {
+ iSession.Stop();
+ state_ = STATE_READY;
+ TRACE_((THIS_FILE, "Streaming stopped"));
+ }
}
void CPjAudioEngine::ConstructL()
{
- iSettings.iFourCC = TFourCC(KMCPFourCCIdG711);
- iSettings.iGlobal = APP_UID;
- iSettings.iPriority = TMdaPriority(100);
- iSettings.iPreference = TMdaPriorityPreference(0x05210001);
- iSettings.iSettings.iChannels = EMMFMono;
- iSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
- iSettings.iSettings.iVolume = 0;
-
- /* play_buf size is samples per frame of parent stream. */
- play_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,
- parentStrm_->samples_per_frame << 1);
- play_buf_len = 0;
- play_buf_start = 0;
-
- /* rec_buf size is samples per frame of parent stream. */
- rec_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,
- parentStrm_->samples_per_frame << 1);
- rec_buf_len = 0;
+ // Recorder settings
+ iRecSettings.iFourCC = setting_.fourcc;
+ iRecSettings.iGlobal = APP_UID;
+ iRecSettings.iPriority = TMdaPriority(100);
+ iRecSettings.iPreference = TMdaPriorityPreference(0x05210001);
+ iRecSettings.iSettings.iChannels = EMMFMono;
+ iRecSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
+
+ // Player settings
+ iPlaySettings.iFourCC = setting_.fourcc;
+ iPlaySettings.iGlobal = APP_UID;
+ iPlaySettings.iPriority = TMdaPriority(100);
+ iPlaySettings.iPreference = TMdaPriorityPreference(0x05220001);
+ iPlaySettings.iSettings.iChannels = EMMFMono;
+ iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
+ iPlaySettings.iSettings.iVolume = 0;
+
+ User::LeaveIfError(iSession.Connect());
}
@@ -498,24 +540,21 @@
return 0;
- iSession.SetCng(EFalse);
- iSession.SetVadMode(EFalse);
- iSession.SetPlc(EFalse);
- iSession.SetEncoderMode(EULawOr30ms);
- iSession.SetDecoderMode(EULawOr30ms);
- iSession.ActivateLoudspeaker(act_loudspeaker);
-
- // Not only playback
- if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
- iRecHandler = CQueueHandler::NewL(this, &iReadQ,
- CQueueHandler::ERecordQueue);
- iRecHandler->Start();
- iSession.Read();
- TRACE_((THIS_FILE, "APS recorder started"));
- }
+ iSession.SetCng(setting_.cng);
+ iSession.SetVadMode(setting_.vad);
+ iSession.SetPlc(setting_.plc);
+ iSession.SetEncoderMode(setting_.mode);
+ iSession.SetDecoderMode(setting_.mode);
+ iSession.ActivateLoudspeaker(setting_.loudspk);
// Not only capture
if (parentStrm_->dir != PJMEDIA_DIR_CAPTURE) {
iSession.Write();
- TRACE_((THIS_FILE, "APS player started"));
+ TRACE_((THIS_FILE, "Player streaming started"));
+ }
+
+ // Not only playback
+ if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
+ iSession.Read();
+ TRACE_((THIS_FILE, "Recorder streaming started"));
}
@@ -557,88 +596,4 @@
}
-void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer)
-{
- pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0);
-
- /* Detect the recorder G.711 frame size, player frame size will follow
- * this recorder frame size.
- */
- if (CPjAudioEngine::aps_samples_per_frame == 0) {
- CPjAudioEngine::aps_samples_per_frame = buffer.iBuffer.Length() < 160?
- 80 : 160;
- TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
- CPjAudioEngine::aps_samples_per_frame));
- }
-
- /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
- * Whenever rec_buf is full, call parent stream callback.
- */
- unsigned dec_len = 0;
-
- while (dec_len < CPjAudioEngine::aps_samples_per_frame) {
- unsigned tmp;
-
- tmp = PJ_MIN(parentStrm_->samples_per_frame - rec_buf_len,
- CPjAudioEngine::aps_samples_per_frame - dec_len);
- pjmedia_ulaw_decode(&rec_buf[rec_buf_len],
- buffer.iBuffer.Ptr() + 2 + dec_len,
- tmp);
- rec_buf_len += tmp;
- dec_len += tmp;
-
- pj_assert(rec_buf_len <= parentStrm_->samples_per_frame);
-
- if (rec_buf_len == parentStrm_->samples_per_frame) {
- recCb_(userData_, 0, rec_buf, rec_buf_len << 1);
- rec_buf_len = 0;
- }
- }
-}
-
-void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer)
-{
- buffer.iCommand = CQueueHandler::EAPSPlayData;
- buffer.iStatus = 0;
- buffer.iBuffer.Zero();
- buffer.iBuffer.Append(1);
- buffer.iBuffer.Append(0);
-
- /* Send 10ms silence frame if frame size hasn't been known. */
- if (CPjAudioEngine::aps_samples_per_frame == 0) {
- pjmedia_zero_samples(play_buf, 80);
- pjmedia_ulaw_encode((pj_uint8_t*)play_buf, play_buf, 80);
- buffer.iBuffer.Append((TUint8*)play_buf, 80);
- iWriteQ.Send(buffer);
- return;
- }
-
- unsigned enc_len = 0;
-
- /* Call parent stream callback to get PCM samples to play,
- * encode the PCM samples into G.711 and put it into APS buffer.
- */
- while (enc_len < CPjAudioEngine::aps_samples_per_frame) {
- if (play_buf_len == 0) {
- playCb_(userData_, 0, play_buf, parentStrm_->samples_per_frame<<1);
- play_buf_len = parentStrm_->samples_per_frame;
- play_buf_start = 0;
- }
-
- unsigned tmp;
-
- tmp = PJ_MIN(play_buf_len,
- CPjAudioEngine::aps_samples_per_frame - enc_len);
- pjmedia_ulaw_encode((pj_uint8_t*)&play_buf[play_buf_start],
- &play_buf[play_buf_start],
- tmp);
- buffer.iBuffer.Append((TUint8*)&play_buf[play_buf_start], tmp);
- enc_len += tmp;
- play_buf_len -= tmp;
- play_buf_start += tmp;
- }
-
- iWriteQ.Send(buffer);
-}
-
//
// End of inherited from MQueueHandlerObserver
@@ -650,4 +605,5 @@
if (state_ == STATE_READY || state_ == STATE_STREAMING) {
iSession.ActivateLoudspeaker(active);
+ TRACE_((THIS_FILE, "Loudspeaker on/off: %d", active));
return KErrNone;
}
@@ -658,4 +614,93 @@
+static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
+{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+
+ pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
+
+ /* Detect the recorder G.711 frame size, player frame size will follow
+ * this recorder frame size.
+ */
+ if (aps_g711_frame_len == 0) {
+ aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
+ TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
+ aps_g711_frame_len));
+ }
+
+ /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
+ * Whenever rec_buf is full, call parent stream callback.
+ */
+ unsigned dec_len = 0;
+
+ while (dec_len < aps_g711_frame_len) {
+ unsigned tmp;
+
+ tmp = PJ_MIN(strm->samples_per_frame - strm->rec_buf_len,
+ aps_g711_frame_len - dec_len);
+ pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
+ buf.iBuffer.Ptr() + 2 + dec_len,
+ tmp);
+ strm->rec_buf_len += tmp;
+ dec_len += tmp;
+
+ pj_assert(strm->rec_buf_len <= strm->samples_per_frame);
+
+ if (strm->rec_buf_len == strm->samples_per_frame) {
+ strm->rec_cb(strm->user_data, 0, strm->rec_buf,
+ strm->rec_buf_len << 1);
+ strm->rec_buf_len = 0;
+ }
+ }
+}
+
+static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
+{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+ unsigned g711_frame_len = aps_g711_frame_len;
+
+ buf.iCommand = CQueueHandler::EAPSPlayData;
+ buf.iStatus = 0;
+ buf.iBuffer.Zero();
+ buf.iBuffer.Append(1);
+ buf.iBuffer.Append(0);
+
+ /* Assume frame size is 10ms if frame size hasn't been known. */
+ if (g711_frame_len == 0)
+ g711_frame_len = 80;
+
+ /* Call parent stream callback to get PCM samples to play,
+ * encode the PCM samples into G.711 and put it into APS buffer.
+ */
+ unsigned enc_len = 0;
+ while (enc_len < g711_frame_len) {
+ if (strm->play_buf_len == 0) {
+ strm->play_cb(strm->user_data, 0, strm->play_buf,
+ strm->samples_per_frame<<1);
+ strm->play_buf_len = strm->samples_per_frame;
+ strm->play_buf_start = 0;
+ }
+
+ unsigned tmp;
+
+ tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - enc_len);
+ pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
+ &strm->play_buf[strm->play_buf_start],
+ tmp);
+ buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
+ enc_len += tmp;
+ strm->play_buf_len -= tmp;
+ strm->play_buf_start += tmp;
+ }
+}
+
+static void RecCb(TAPSCommBuffer &buf, void *user_data)
+{
+}
+
+static void PlayCb(TAPSCommBuffer &buf, void *user_data)
+{
+}
+
/*
* Initialize sound subsystem.
@@ -664,4 +709,13 @@
{
snd_pool_factory = factory;
+
+ def_setting.format.u32 = PJMEDIA_FOURCC_L16;
+ def_setting.mode = 0;
+ def_setting.bitrate = 128000;
+ def_setting.plc = PJ_FALSE;
+ def_setting.vad = PJ_FALSE;
+ def_setting.cng = PJ_FALSE;
+ def_setting.loudspk = PJ_FALSE;
+
return PJ_SUCCESS;
}
@@ -701,7 +755,10 @@
pj_pool_t *pool;
pjmedia_snd_stream *strm;
+ CPjAudioSetting setting;
+ PjAudioCallback aps_rec_cb;
+ PjAudioCallback aps_play_cb;
PJ_ASSERT_RETURN(p_snd_strm, PJ_EINVAL);
- PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 &&
+ PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 &&
bits_per_sample == 16, PJ_ENOTSUP);
PJ_ASSERT_RETURN((dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && rec_cb && play_cb)
@@ -710,10 +767,10 @@
PJ_EINVAL);
- pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
+ pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
NULL);
if (!pool)
return PJ_ENOMEM;
- strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
+ strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
sizeof(pjmedia_snd_stream));
strm->dir = dir;
@@ -723,11 +780,65 @@
strm->samples_per_frame = samples_per_frame;
+ /* Set audio engine settings. */
+ if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_L16)
+ {
+ setting.fourcc = TFourCC(KMCPFourCCIdG711);
+ } else {
+ setting.fourcc = TFourCC(def_setting.format.u32);
+ }
+
+ if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR)
+ {
+ setting.mode = (TAPSCodecMode)def_setting.bitrate;
+ } else if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_L16 ||
+ (def_setting.format.u32 == PJMEDIA_FOURCC_ILBC &&
+ def_setting.mode == 30))
+ {
+ setting.mode = EULawOr30ms;
+ } else {
+ setting.mode = EALawOr20ms;
+ }
+
+ setting.vad = def_setting.vad;
+ setting.plc = def_setting.plc;
+ setting.cng = def_setting.cng;
+ setting.loudspk = def_setting.loudspk;
+
+ if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_G711A ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_G729 ||
+ def_setting.format.u32 == PJMEDIA_FOURCC_ILBC)
+ {
+ aps_play_cb = &PlayCb;
+ aps_rec_cb = &RecCb;
+ } else {
+ aps_play_cb = &PlayCbPcm;
+ aps_rec_cb = &RecCbPcm;
+ }
+
// Create the audio engine.
- TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, rec_cb, play_cb,
- user_data));
+ TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
+ aps_rec_cb, aps_play_cb,
+ strm, setting));
if (err != KErrNone) {
- pj_pool_release(pool);
+ pj_pool_release(pool);
return PJ_RETURN_OS_ERROR(err);
}
+
+ strm->rec_cb = rec_cb;
+ strm->play_cb = play_cb;
+ strm->user_data = user_data;
+
+ /* play_buf size is samples per frame. */
+ strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1);
+ strm->play_buf_len = 0;
+ strm->play_buf_start = 0;
+
+ /* rec_buf size is samples per frame. */
+ strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1);
+ strm->rec_buf_len = 0;
// Done.
@@ -753,5 +864,5 @@
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
- return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
+ return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
samples_per_frame, bits_per_sample, rec_cb, NULL,
user_data, p_snd_strm);
@@ -770,5 +881,5 @@
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
- return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
+ return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
samples_per_frame, bits_per_sample, NULL, play_cb,
user_data, p_snd_strm);
@@ -790,5 +901,5 @@
PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
- return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count,
+ return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count,
samples_per_frame, bits_per_sample, rec_cb, play_cb,
user_data, p_snd_strm);
@@ -822,5 +933,5 @@
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
+
if (stream->engine) {
TInt err = stream->engine->StartL();
@@ -828,5 +939,5 @@
return PJ_RETURN_OS_ERROR(err);
}
-
+
return PJ_SUCCESS;
}
@@ -836,9 +947,9 @@
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
+
if (stream->engine) {
stream->engine->Stop();
}
-
+
return PJ_SUCCESS;
}
@@ -848,18 +959,16 @@
{
pj_pool_t *pool;
-
+
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->engine) {
- delete stream->engine;
- stream->engine = NULL;
- }
+
+ delete stream->engine;
+ stream->engine = NULL;
pool = stream->pool;
- if (pool) {
+ if (pool) {
stream->pool = NULL;
pj_pool_release(pool);
}
-
+
return PJ_SUCCESS;
}
@@ -876,5 +985,5 @@
* Set sound latency.
*/
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
+PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
unsigned output_latency)
{
@@ -890,9 +999,9 @@
*/
PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker(
- pjmedia_snd_stream *stream,
+ pjmedia_snd_stream *stream,
pj_bool_t active)
{
if (stream == NULL) {
- act_loudspeaker = active;
+ def_setting.loudspk = active;
} else {
if (stream->engine == NULL)
@@ -906,2 +1015,17 @@
return PJ_SUCCESS;
}
+
+/**
+ * Set a codec and its settings to be used on the next sound device session.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_aps_modify_setting(
+ const pjmedia_snd_aps_setting *setting)
+{
+ PJ_ASSERT_RETURN(setting, PJ_EINVAL);
+
+ def_setting = *setting;
+
+ return PJ_SUCCESS;
+}
+
+#endif // PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
Index: /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/app_main.cpp
===================================================================
--- /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/app_main.cpp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/app_main.cpp (revision 2434)
@@ -50,5 +50,5 @@
PJ_UNUSED_ARG(level);
-
+
pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
@@ -58,7 +58,7 @@
/* perror util */
-static void app_perror(const char *title, pj_status_t status)
-{
- char errmsg[PJ_ERR_MSG_SIZE];
+static void app_perror(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
@@ -66,14 +66,14 @@
/* Application init */
-static pj_status_t app_init()
+static pj_status_t app_init()
{
unsigned i, count;
pj_status_t status;
-
+
/* Redirect log */
pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
pj_log_set_level(3);
-
+
/* Init pjlib */
status = pj_init();
@@ -84,5 +84,5 @@
pj_caching_pool_init(&cp, NULL, 0);
-
+
/* Init sound subsystem */
status = pjmedia_snd_init(&cp.factory);
@@ -93,14 +93,14 @@
return status;
}
-
+
count = pjmedia_snd_get_dev_count();
PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
for (i=0; iname, info->input_count, info->output_count,
- info->default_samples_per_sec));
+ info->default_samples_per_sec));
}
@@ -115,6 +115,6 @@
/* Init delay buffer */
- status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE,
- SAMPLES_PER_FRAME, CHANNEL_COUNT,
+ status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE,
+ SAMPLES_PER_FRAME, CHANNEL_COUNT,
0, 0, &delaybuf);
if (status != PJ_SUCCESS) {
@@ -124,5 +124,5 @@
//return status;
}
-
+
return PJ_SUCCESS;
}
@@ -130,8 +130,8 @@
/* Sound capture callback */
-static pj_status_t rec_cb(void *user_data,
+static pj_status_t rec_cb(void *user_data,
pj_uint32_t timestamp,
void *input,
- unsigned size)
+ unsigned size)
{
PJ_UNUSED_ARG(user_data);
@@ -154,26 +154,26 @@
pj_uint32_t timestamp,
void *output,
- unsigned size)
+ unsigned size)
{
PJ_UNUSED_ARG(user_data);
PJ_UNUSED_ARG(timestamp);
PJ_UNUSED_ARG(size);
-
+
pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)output);
-
+
++play_cnt;
- return PJ_SUCCESS;
+ return PJ_SUCCESS;
}
/* Start sound */
-static pj_status_t snd_start(unsigned flag)
+static pj_status_t snd_start(unsigned flag)
{
pj_status_t status;
-
+
if (strm != NULL) {
app_perror("snd already open", PJ_EINVALIDOP);
return PJ_EINVALIDOP;
}
-
+
if (flag==PJMEDIA_DIR_CAPTURE_PLAYBACK)
status = pjmedia_snd_open(-1, -1, CLOCK_RATE, CHANNEL_COUNT,
@@ -188,5 +188,5 @@
SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
&play_cb, NULL, &strm);
-
+
if (status != PJ_SUCCESS) {
app_perror("snd open", status);
@@ -211,17 +211,21 @@
/* Stop sound */
-static pj_status_t snd_stop()
+static pj_status_t snd_stop()
{
pj_time_val now;
pj_status_t status;
-
+
if (strm == NULL) {
app_perror("snd not open", PJ_EINVALIDOP);
return PJ_EINVALIDOP;
}
-
+
+ status = pjmedia_snd_stream_stop(strm);
+ if (status != PJ_SUCCESS) {
+ app_perror("snd failed to stop", status);
+ }
status = pjmedia_snd_stream_close(strm);
strm = NULL;
-
+
pj_gettimeofday(&now);
PJ_TIME_VAL_SUB(now, t_start);
@@ -235,9 +239,9 @@
/* Shutdown application */
-static void app_fini()
+static void app_fini()
{
if (strm)
snd_stop();
-
+
pjmedia_snd_deinit();
pjmedia_delay_buf_destroy(delaybuf);
@@ -254,8 +258,8 @@
#include
-class ConsoleUI : public CActive
+class ConsoleUI : public CActive
{
public:
- ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
+ ConsoleUI(CConsoleBase *con);
// Run console UI
@@ -264,5 +268,5 @@
// Stop
void Stop();
-
+
protected:
// Cancel asynchronous read.
@@ -271,13 +275,12 @@
// Implementation: called when read has completed.
void RunL();
-
+
private:
- CActiveSchedulerWait *asw_;
CConsoleBase *con_;
};
-ConsoleUI::ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con)
-: CActive(EPriorityHigh), asw_(asw), con_(con)
+ConsoleUI::ConsoleUI(CConsoleBase *con)
+: CActive(EPriorityUserInput), con_(con)
{
CActiveScheduler::Add(this);
@@ -285,5 +288,5 @@
// Run console UI
-void ConsoleUI::Run()
+void ConsoleUI::Run()
{
con_->Read(iStatus);
@@ -292,5 +295,5 @@
// Stop console UI
-void ConsoleUI::Stop()
+void ConsoleUI::Stop()
{
DoCancel();
@@ -298,10 +301,10 @@
// Cancel asynchronous read.
-void ConsoleUI::DoCancel()
+void ConsoleUI::DoCancel()
{
con_->ReadCancel();
}
-static void PrintMenu()
+static void PrintMenu()
{
PJ_LOG(3, (THIS_FILE, "\n\n"
@@ -315,12 +318,13 @@
// Implementation: called when read has completed.
-void ConsoleUI::RunL()
+void ConsoleUI::RunL()
{
TKeyCode kc = con_->KeyCode();
pj_bool_t reschedule = PJ_TRUE;
-
+
switch (kc) {
case 'w':
- asw_->AsyncStop();
+ snd_stop();
+ CActiveScheduler::Stop();
reschedule = PJ_FALSE;
break;
@@ -344,5 +348,5 @@
PrintMenu();
-
+
if (reschedule)
Run();
@@ -351,21 +355,19 @@
////////////////////////////////////////////////////////////////////////////
-int app_main()
+int app_main()
{
if (app_init() != PJ_SUCCESS)
return -1;
-
+
// Run the UI
- CActiveSchedulerWait *asw = new CActiveSchedulerWait;
- ConsoleUI *con = new ConsoleUI(asw, console);
-
+ ConsoleUI *con = new ConsoleUI(console);
+
con->Run();
-
+
PrintMenu();
- asw->Start();
-
+ CActiveScheduler::Start();
+
delete con;
- delete asw;
-
+
app_fini();
return 0;
Index: /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/main_symbian.cpp
===================================================================
--- /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/main_symbian.cpp (revision 2433)
+++ /pjproject/branches/projects/aps-direct/pjsip-apps/src/symsndtest/main_symbian.cpp (revision 2434)
@@ -30,71 +30,8 @@
// Needed by APS
-TPtrC APP_UID = _L("A000000D");
+TPtrC APP_UID = _L("A000000E");
int app_main();
-
-////////////////////////////////////////////////////////////////////////////
-class MyTask : public CActive
-{
-public:
- static MyTask *NewL(CActiveSchedulerWait *asw);
- ~MyTask();
- void Start();
-
-protected:
- MyTask(CActiveSchedulerWait *asw);
- void ConstructL();
- virtual void RunL();
- virtual void DoCancel();
-
-private:
- RTimer timer_;
- CActiveSchedulerWait *asw_;
-};
-
-MyTask::MyTask(CActiveSchedulerWait *asw)
-: CActive(EPriorityNormal), asw_(asw)
-{
-}
-
-MyTask::~MyTask()
-{
- timer_.Close();
-}
-
-void MyTask::ConstructL()
-{
- timer_.CreateLocal();
- CActiveScheduler::Add(this);
-}
-
-MyTask *MyTask::NewL(CActiveSchedulerWait *asw)
-{
- MyTask *self = new (ELeave) MyTask(asw);
- CleanupStack::PushL(self);
-
- self->ConstructL();
-
- CleanupStack::Pop(self);
- return self;
-}
-
-void MyTask::Start()
-{
- timer_.After(iStatus, 0);
- SetActive();
-}
-
-void MyTask::RunL()
-{
- int rc = app_main();
- asw_->AsyncStop();
-}
-
-void MyTask::DoCancel()
-{
-
-}
////////////////////////////////////////////////////////////////////////////
@@ -106,17 +43,6 @@
CActiveScheduler::Install(scheduler);
- CActiveSchedulerWait *asw = new CActiveSchedulerWait;
- CleanupStack::PushL(asw);
-
- MyTask *task = MyTask::NewL(asw);
- task->Start();
+ app_main();
- asw->Start();
-
- delete task;
-
- CleanupStack::Pop(asw);
- delete asw;
-
CActiveScheduler::Install(NULL);
CleanupStack::Pop(scheduler);
@@ -143,11 +69,11 @@
TRAPD(startError, DoStartL());
- console->Printf(_L("[press any key to close]\n"));
- console->Getch();
-
+ //console->Printf(_L("[press any key to close]\n"));
+ //console->Getch();
+
delete console;
delete cleanup;
- CloseSTDLIB();
+ CloseSTDLIB();
// Mark end of heap usage, detect memory leaks