Initial sources of APS-direct.

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2434 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2009-01-28 18:03:12 +00:00
parent 27c98df853
commit 4e50262076
18 changed files with 2031 additions and 574 deletions

View File

@ -1,6 +1,3 @@
#define SND_USE_NULL 0
#define SND_USE_APS 0
prj_platforms
winscw
//armv5
@ -25,13 +22,7 @@ libgsmcodec.mmp
libspeexcodec.mmp
/* 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 */
//pjlib_test.mmp

View File

@ -40,6 +40,7 @@ SOURCE bidirectional.c
SOURCE clock_thread.c
SOURCE codec.c
SOURCE conference.c
SOURCE conf_switch.c
SOURCE echo_common.c
SOURCE echo_port.c
SOURCE echo_suppress.c

View File

@ -24,22 +24,23 @@ TARGETTYPE lib
SOURCEPATH ..\pjmedia\src\pjmedia
OPTION CW -lang c++
//
// GCCE optimization setting
//
OPTION GCCE -O2 -fno-unit-at-a-time
MACRO PJ_M_I386=1
MACRO PJ_SYMBIAN=1
SOURCE nullsound.c
SOURCE symbian_sound.cpp
SOURCE symbian_sound_aps.cpp
SYSTEMINCLUDE ..\pjlib\include
SYSTEMINCLUDE ..\pjmedia\include
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

View File

@ -1,63 +1,61 @@
#define SND_USE_NULL 0
#define SND_USE_APS 0
TARGET symbian_ua.exe
TARGETTYPE exe
UID 0x0 0xA000000D
SOURCEPATH ..\pjsip-apps\src\symbian_ua
MACRO PJ_M_I386=1
MACRO PJ_SYMBIAN=1
// Source files
SOURCE ua.cpp
SOURCE main_symbian.cpp
DOCUMENT ua.h
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 \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
#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
#else
STATICLIBRARY symbian_audio.lib
LIBRARY mediaclientaudiostream.lib
LIBRARY mediaclientaudioinputstream.lib
CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#endif
#ifdef WINSCW
STATICLIBRARY eexe.lib ecrt0.lib
#endif
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
// whistles turned on
EPOCSTACKSIZE 12288
#define SND_USE_APS 1
#define SND_USE_VAS 0
TARGET symbian_ua.exe
TARGETTYPE exe
UID 0x0 0xA000000D
SOURCEPATH ..\pjsip-apps\src\symbian_ua
MACRO PJ_M_I386=1
MACRO PJ_SYMBIAN=1
// Source files
SOURCE ua.cpp
SOURCE main_symbian.cpp
DOCUMENT ua.h
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 \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 symbian_audio.lib
#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
LIBRARY mediaclientaudiostream.lib
LIBRARY mediaclientaudioinputstream.lib
CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#endif
#ifdef WINSCW
STATICLIBRARY eexe.lib ecrt0.lib
#endif
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
// whistles turned on
EPOCSTACKSIZE 12288

View File

@ -1,53 +1,45 @@
#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
#ifdef WINSCW

View File

@ -94,78 +94,6 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="$(OutDir)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
ExecutionBucket="7"
Optimization="2"
FavorSizeOrSpeed="2"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
PreprocessorDefinitions="NDEBUG;_WIN32_WCE=$(CEVER);UNDER_CE;$(PLATFORMDEFINES);WINCE;$(ARCHFAM);$(_ARCHFAM_);SMARTPHONE2003_UI_MODEL;SMARTPHONE2003_UI_MODEL"
ExceptionHandling="0"
RuntimeLibrary="0"
WarningLevel="3"
DebugInformationFormat="0"
CompileAs="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalOptions=""
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCCodeSignTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
<DeploymentTool
ForceDirty="-1"
RemoteDirectory=""
RegisterOutput="0"
AdditionalFiles=""
/>
<DebuggerTool
/>
</Configuration>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\output\pjmedia-i386-win32-vc8-debug"
@ -242,6 +170,78 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="$(OutDir)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
ExecutionBucket="7"
Optimization="2"
FavorSizeOrSpeed="2"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
PreprocessorDefinitions="NDEBUG;_WIN32_WCE=$(CEVER);UNDER_CE;$(PLATFORMDEFINES);WINCE;$(ARCHFAM);$(_ARCHFAM_);SMARTPHONE2003_UI_MODEL;SMARTPHONE2003_UI_MODEL"
ExceptionHandling="0"
RuntimeLibrary="0"
WarningLevel="3"
DebugInformationFormat="0"
CompileAs="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalOptions=""
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCCodeSignTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
<DeploymentTool
ForceDirty="-1"
RemoteDirectory=""
RegisterOutput="0"
AdditionalFiles=""
/>
<DebuggerTool
/>
</Configuration>
<Configuration
Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"
@ -433,6 +433,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\src\pjmedia\conf_switch.c"
>
</File>
<File
RelativePath="..\src\pjmedia\conference.c"
>

View File

@ -194,5 +194,13 @@
# define PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 1
#endif
/**
* Enable Passthrough codecs.
*
* Default: 0
*/
#ifndef PJMEDIA_HAS_PASSTHROUGH_CODECS
# define PJMEDIA_HAS_PASSTHROUGH_CODECS 0
#endif
#endif /* __PJMEDIA_CODEC_CONFIG_H__ */

View File

@ -56,6 +56,7 @@ typedef struct pjmedia_conf_port_info
{
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. */
unsigned listener_cnt; /**< Number of listeners. */

View File

@ -44,6 +44,22 @@
# include <pjmedia/config_auto.h>
#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,6 +76,16 @@
/** 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.
* Application will have to provide its own sound device backend
* and link the application with it.

View File

@ -25,6 +25,7 @@
* @brief Port interface declaration
*/
#include <pjmedia/types.h>
#include <pj/assert.h>
#include <pj/os.h>
@ -211,6 +212,7 @@ typedef struct pjmedia_port_info
pj_bool_t has_info; /**< Has info? */
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. */
unsigned channel_count; /**< Number of channels. */
@ -226,7 +228,8 @@ typedef struct pjmedia_port_info
typedef enum pjmedia_frame_type
{
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;
@ -248,6 +251,88 @@ typedef struct pjmedia_frame
} pjmedia_frame;
/**
* 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.
*/

View File

@ -30,6 +30,24 @@
PJ_BEGIN_DECL
/**
* 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.
@ -47,6 +65,17 @@ PJ_DECL(pj_status_t) pjmedia_snd_aps_activate_loudspeaker(
pj_bool_t active);
/**
* 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

View File

@ -47,8 +47,8 @@
* @{
*/
/**
* Top most media type.
/**
* Top most media type.
*/
typedef enum pjmedia_type
{
@ -61,7 +61,7 @@ typedef enum pjmedia_type
/** The media is video. */
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.
*/
PJMEDIA_TYPE_UNKNOWN = 3,
@ -72,8 +72,8 @@ typedef enum pjmedia_type
} pjmedia_type;
/**
* Media transport protocol.
/**
* Media transport protocol.
*/
typedef enum pjmedia_tp_proto
{
@ -92,8 +92,8 @@ typedef enum pjmedia_tp_proto
} pjmedia_tp_proto;
/**
* Media direction.
/**
* Media direction.
*/
typedef enum pjmedia_dir
{
@ -138,8 +138,8 @@ typedef enum pjmedia_dir
(a<<24 | b<<16 | c<<8 | d)
/**
* Opague declaration of media endpoint.
/**
* Opaque declaration of media endpoint.
*/
typedef struct pjmedia_endpt pjmedia_endpt;
@ -150,7 +150,7 @@ typedef struct pjmedia_endpt pjmedia_endpt;
typedef struct pjmedia_stream pjmedia_stream;
/**
/**
* Media socket info is used to describe the underlying sockets
* to be used as media transport.
*/
@ -178,10 +178,34 @@ typedef struct pjmedia_sock_info
} 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.
*
@ -205,7 +229,7 @@ PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
/**
* 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.
*/
@ -220,7 +244,7 @@ PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
#else
unsigned i;
count >>= 1;
for (i=0; i<count; ++i)
for (i=0; i<count; ++i)
((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
#endif
}
@ -228,7 +252,7 @@ PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
/**
* 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.
*/
@ -243,7 +267,7 @@ PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
#else
unsigned i;
count >>= 1;
for (i=0; i<count; ++i)
for (i=0; i<count; ++i)
((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@
#include <pj/pool.h>
#include <pj/string.h>
#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).
@ -1987,3 +1988,4 @@ static pj_status_t put_frame(pjmedia_port *this_port,
return status;
}
#endif

View File

@ -23,6 +23,7 @@
#include <pj/log.h>
#include <pj/os.h>
#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_MDA_SOUND
/*
* This file provides sound implementation for Symbian Audio Streaming
@ -942,3 +943,5 @@ PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
PJ_UNUSED_ARG(output_latency);
return PJ_SUCCESS;
}
#endif

View File

@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/symbian_sound_aps.h>
#include <pjmedia/sound.h>
#include <pjmedia/alaw_ulaw.h>
#include <pjmedia/errno.h>
@ -25,6 +26,8 @@
#include <pj/math.h>
#include <pj/os.h>
#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
#include <e32msgqueue.h>
#include <sounddevice.h>
#include <APSClientSession.h>
@ -45,7 +48,7 @@
# define TRACE_(st)
#endif
static pjmedia_snd_dev_info symbian_snd_dev_info =
static pjmedia_snd_dev_info symbian_snd_dev_info =
{
"Symbian Sound Device (APS)",
1,
@ -56,56 +59,81 @@ static pjmedia_snd_dev_info symbian_snd_dev_info =
/* App UID to open global APS queues to communicate with the APS server. */
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;
static pj_pool_factory *snd_pool_factory;
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;
};
/*
* 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.
*/
class MQueueHandlerObserver
{
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;
};
/**
@ -132,11 +160,13 @@ public:
EAPSRecorderInitComplete = 6
};
static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
RMsgQueue<TAPSCommBuffer>* aQ,
static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
RMsgQueue<TAPSCommBuffer>* aQ,
RMsgQueue<TAPSCommBuffer>* 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();
CleanupStack::Pop(self);
@ -154,11 +184,12 @@ public:
private:
// Constructor
CQueueHandler(MQueueHandlerObserver* aObserver,
RMsgQueue<TAPSCommBuffer>* aQ,
TQueueHandlerType aType)
CQueueHandler(MQueueHandlerObserver* aObserver,
RMsgQueue<TAPSCommBuffer>* aQ,
RMsgQueue<TAPSCommBuffer>* aWriteQ,
TQueueHandlerType aType)
: CActive(CActive::EPriorityHigh),
iQ(aQ), iObserver(aObserver), iType(aType)
iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
{
CActiveScheduler::Add(this);
@ -177,7 +208,7 @@ private:
if (iStatus != KErrNone) {
iObserver->NotifyError(iStatus.Int());
return;
}
}
TAPSCommBuffer buffer;
TInt ret = iQ->Receive(buffer);
@ -190,7 +221,9 @@ private:
switch (iType) {
case ERecordQueue:
if (buffer.iCommand == EAPSRecordData) {
iObserver->RecCb(buffer);
iObserver->RecCb(buffer, iObserver->UserData);
} else {
iObserver->NotifyError(buffer.iStatus);
}
break;
@ -199,7 +232,8 @@ private:
switch (buffer.iCommand) {
case EAPSPlayData:
if (buffer.iStatus == KErrUnderflow) {
iObserver->PlayCb(buffer);
iObserver->PlayCb(buffer, iObserver->UserData);
iWriteQ->Send(buffer);
}
break;
case EAPSPlayerInitialize:
@ -224,14 +258,9 @@ private:
// through this handler. All other callbacks will be
// sent from the APS main thread through EPlayCommQueue
case EAPSRecorderInitialize:
if (buffer.iStatus == KErrNone) {
iObserver->InputStreamInitialized(buffer.iStatus);
break;
}
case EAPSRecordData:
iObserver->NotifyError(buffer.iStatus);
break;
default:
iObserver->NotifyError(buffer.iStatus);
break;
}
break;
@ -245,12 +274,30 @@ private:
SetActive();
}
TInt RunError(TInt) {
return 0;
}
// Data
RMsgQueue<TAPSCommBuffer> *iQ; // (not owned)
RMsgQueue<TAPSCommBuffer> *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;
};
/*
* Implementation: Symbian Input & Output Stream.
@ -268,9 +315,10 @@ public:
~CPjAudioEngine();
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();
void Stop();
@ -279,33 +327,29 @@ public:
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);
virtual void OutputStreamInitialized(const TInt aStatus);
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<TAPSCommBuffer> iReadQ;
RMsgQueue<TAPSCommBuffer> iReadCommQ;
RMsgQueue<TAPSCommBuffer> iWriteQ;
@ -314,28 +358,19 @@ private:
CQueueHandler *iPlayCommHandler;
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();
CleanupStack::Pop(self);
@ -343,14 +378,14 @@ CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm,
}
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),
iRecHandler(0)
@ -361,10 +396,21 @@ CPjAudioEngine::~CPjAudioEngine()
{
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();
@ -383,16 +429,16 @@ TInt CPjAudioEngine::InitPlayL()
if (state_ == STATE_STREAMING || state_ == STATE_READY)
return 0;
TInt err = iSession.InitializePlayer(iSettings);
TInt err = iSession.InitializePlayer(iPlaySettings);
if (err != KErrNone) {
snd_perror("Failed to initialize player", err);
return err;
}
// 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"));
while (iWriteQ.OpenGlobal(buf2))
@ -401,8 +447,7 @@ TInt CPjAudioEngine::InitPlayL()
User::After(10);
// Construct message queue handler
iPlayCommHandler = CQueueHandler::NewL(this,
&iWriteCommQ,
iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
CQueueHandler::EPlayCommQueue);
// Start observing APS callbacks on output stream message queue
@ -417,15 +462,15 @@ TInt CPjAudioEngine::InitRecL()
return 0;
// 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"));
// Must wait for APS thread to finish creating message queues
@ -436,60 +481,57 @@ TInt CPjAudioEngine::InitRecL()
User::After(10);
// 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;
}
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();
}
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());
}
TInt CPjAudioEngine::StartStreamL()
@ -497,26 +539,23 @@ TInt CPjAudioEngine::StartStreamL()
if (state_ == STATE_STREAMING)
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"));
}
state_ = STATE_STREAMING;
@ -556,90 +595,6 @@ void CPjAudioEngine::NotifyError(const TInt aError)
snd_perror("Error from CQueueHandler", aError);
}
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
/////////////////////////////////////////////////////////////
@ -649,6 +604,7 @@ TInt CPjAudioEngine::ActivateSpeaker(TBool active)
{
if (state_ == STATE_READY || state_ == STATE_STREAMING) {
iSession.ActivateLoudspeaker(active);
TRACE_((THIS_FILE, "Loudspeaker on/off: %d", active));
return KErrNone;
}
return KErrNotReady;
@ -657,12 +613,110 @@ TInt CPjAudioEngine::ActivateSpeaker(TBool active)
//
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.
*/
PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
{
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;
}
@ -700,21 +754,24 @@ static pj_status_t sound_open(pjmedia_dir dir,
{
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)
|| (dir == PJMEDIA_DIR_CAPTURE && rec_cb && !play_cb)
|| (dir == PJMEDIA_DIR_PLAYBACK && !rec_cb && play_cb),
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;
strm->pool = pool;
@ -722,14 +779,68 @@ static pj_status_t sound_open(pjmedia_dir dir,
strm->channel_count = channel_count;
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.
*p_snd_strm = strm;
return PJ_SUCCESS;
@ -752,7 +863,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
if (index < 0) index = 0;
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);
}
@ -769,7 +880,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
if (index < 0) index = 0;
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);
}
@ -789,7 +900,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
if (play_id < 0) play_id = 0;
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);
}
@ -821,13 +932,13 @@ PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
if (stream->engine) {
TInt err = stream->engine->StartL();
if (err != KErrNone)
return PJ_RETURN_OS_ERROR(err);
}
return PJ_SUCCESS;
}
@ -835,11 +946,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
{
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
if (stream->engine) {
stream->engine->Stop();
}
return PJ_SUCCESS;
}
@ -847,20 +958,18 @@ PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
{
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;
}
@ -875,7 +984,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
/*
* 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)
{
/* Nothing to do */
@ -889,11 +998,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
* Activate/deactivate loudspeaker.
*/
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)
return PJ_EINVAL;
@ -905,3 +1014,18 @@ PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker(
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

View File

@ -49,7 +49,7 @@ static void log_writer(int level, const char *buf, unsigned len)
static wchar_t buf16[PJ_LOG_MAX_SIZE];
PJ_UNUSED_ARG(level);
pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
@ -57,24 +57,24 @@ static void log_writer(int level, const char *buf, unsigned len)
}
/* perror util */
static void app_perror(const char *title, pj_status_t status)
static void app_perror(const char *title, pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
}
/* 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();
if (status != PJ_SUCCESS) {
@ -83,7 +83,7 @@ static pj_status_t app_init()
}
pj_caching_pool_init(&cp, NULL, 0);
/* Init sound subsystem */
status = pjmedia_snd_init(&cp.factory);
if (status != PJ_SUCCESS) {
@ -92,16 +92,16 @@ static pj_status_t app_init()
pj_shutdown();
return status;
}
count = pjmedia_snd_get_dev_count();
PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
for (i=0; i<count; ++i) {
const pjmedia_snd_dev_info *info;
info = pjmedia_snd_get_dev_info(i);
PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
i, info->name, info->input_count, info->output_count,
info->default_samples_per_sec));
info->default_samples_per_sec));
}
/* Create pool */
@ -114,8 +114,8 @@ static pj_status_t app_init()
}
/* 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) {
app_perror("pjmedia_delay_buf_create()", status);
@ -123,16 +123,16 @@ static pj_status_t app_init()
//pj_shutdown();
//return status;
}
return PJ_SUCCESS;
}
/* 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);
PJ_UNUSED_ARG(timestamp);
@ -153,28 +153,28 @@ static pj_status_t rec_cb(void *user_data,
static pj_status_t play_cb(void *user_data,
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,
SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
@ -187,7 +187,7 @@ static pj_status_t snd_start(unsigned flag)
status = pjmedia_snd_open_player(-1, CLOCK_RATE, CHANNEL_COUNT,
SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
&play_cb, NULL, &strm);
if (status != PJ_SUCCESS) {
app_perror("snd open", status);
return status;
@ -210,19 +210,23 @@ static pj_status_t snd_start(unsigned flag)
}
/* 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);
@ -234,11 +238,11 @@ static pj_status_t snd_stop()
}
/* Shutdown application */
static void app_fini()
static void app_fini()
{
if (strm)
snd_stop();
pjmedia_snd_deinit();
pjmedia_delay_buf_destroy(delaybuf);
pj_pool_release(pool);
@ -253,56 +257,55 @@ static void app_fini()
*/
#include <e32base.h>
class ConsoleUI : public CActive
class ConsoleUI : public CActive
{
public:
ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
ConsoleUI(CConsoleBase *con);
// Run console UI
void Run();
// Stop
void Stop();
protected:
// Cancel asynchronous read.
void DoCancel();
// 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);
}
// Run console UI
void ConsoleUI::Run()
void ConsoleUI::Run()
{
con_->Read(iStatus);
SetActive();
}
// Stop console UI
void ConsoleUI::Stop()
void ConsoleUI::Stop()
{
DoCancel();
}
// Cancel asynchronous read.
void ConsoleUI::DoCancel()
void ConsoleUI::DoCancel()
{
con_->ReadCancel();
}
static void PrintMenu()
static void PrintMenu()
{
PJ_LOG(3, (THIS_FILE, "\n\n"
"Menu:\n"
@ -314,14 +317,15 @@ static void PrintMenu()
}
// 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;
case 'a':
@ -343,30 +347,28 @@ void ConsoleUI::RunL()
}
PrintMenu();
if (reschedule)
Run();
}
////////////////////////////////////////////////////////////////////////////
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;
}

View File

@ -29,74 +29,11 @@
CConsoleBase* console;
// 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()
{
}
////////////////////////////////////////////////////////////////////////////
LOCAL_C void DoStartL()
@ -105,19 +42,8 @@ LOCAL_C void DoStartL()
CleanupStack::PushL(scheduler);
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);
delete scheduler;
@ -142,13 +68,13 @@ GLDEF_C TInt E32Main()
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
__UHEAP_MARKEND;