- Added new API for sound & sound port to create/open sound device with extended setting, to allow opening sound device with non-PCM format and other settings.
- Updated symbian_ua/ua.cpp to be able to reopen sound device when audio stream session is using non-PCM data/passthrough codec.
- Updated stream.c to allow it works with non-PCM data.
- Added PCMU/A frames processing into non-PCM play/record callbacks in symbian_audio_aps.cpp.
- Added passthrough codec init/deinitialization in pjsua-lib.
- Added a new pjmedia_frame_ext helper function, pjmedia_frame_ext_pop_subframes, to pop-out/remove some subframes.
- Other minor updates/fixes.
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2438 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/build.symbian/bld.inf b/build.symbian/bld.inf
index 4c9c13a..a07df70 100644
--- a/build.symbian/bld.inf
+++ b/build.symbian/bld.inf
@@ -20,6 +20,7 @@
/* Codecs */
libgsmcodec.mmp
libspeexcodec.mmp
+libpassthroughcodec.mmp
/* Sound device impl */
symbian_audio.mmp
diff --git a/build.symbian/libpassthroughcodec.mmp b/build.symbian/libpassthroughcodec.mmp
new file mode 100644
index 0000000..c4fb277
--- /dev/null
+++ b/build.symbian/libpassthroughcodec.mmp
@@ -0,0 +1,26 @@
+TARGET libpassthroughcodec.lib
+TARGETTYPE lib
+
+MACRO HAVE_CONFIG_H
+MACRO PJ_M_I386=1
+MACRO PJ_SYMBIAN=1
+
+//
+// GCCE optimization setting
+//
+OPTION GCCE -O2 -fno-unit-at-a-time
+
+//
+// Passthrough codecs wrapper for pjmedia-codec
+//
+SOURCEPATH ..\pjmedia\src\pjmedia-codec
+SOURCE passthrough.c
+
+//
+// Header files
+//
+SYSTEMINCLUDE ..\pjmedia\include
+SYSTEMINCLUDE ..\pjlib\include
+
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\libc
diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp
index 5f7f1f8..e21a899 100644
--- a/build.symbian/symbian_ua.mmp
+++ b/build.symbian/symbian_ua.mmp
@@ -34,7 +34,7 @@
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 libgsmcodec.lib libspeexcodec.lib libpassthroughcodec.lib
STATICLIBRARY symbian_audio.lib
#if SND_USE_APS
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index 3db581a..8b7c3f6 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -31,6 +31,7 @@
#include <pjmedia-codec/ilbc.h>
#include <pjmedia-codec/g722.h>
#include <pjmedia-codec/ipp_codecs.h>
+#include <pjmedia-codec/passthrough.h>
#endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 3fa8189..49ac321 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -217,10 +217,12 @@
#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU
# define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1
+# undef PJMEDIA_HAS_G711_CODEC
#endif
-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
+#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA
# define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1
+# undef PJMEDIA_HAS_G711_CODEC
#endif
#endif /* __PJMEDIA_CODEC_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h
index 3c08895..374b372 100644
--- a/pjmedia/include/pjmedia/port.h
+++ b/pjmedia/include/pjmedia/port.h
@@ -359,6 +359,42 @@
}
/**
+ * Pop out first n subframes from #pjmedia_frame_ext.
+ *
+ * @param frm The #pjmedia_frame_ext.
+ * @param n Number of first subframes to be popped out.
+ *
+ * @return PJ_SUCCESS, or PJ_ENOTFOUND if frame is empty.
+ */
+PJ_INLINE(pj_status_t)
+ pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
+{
+ pjmedia_frame_ext_subframe *sf;
+ pj_uint8_t *move_src;
+ unsigned move_len;
+
+ if (frm->subframe_cnt <= n) {
+ frm->subframe_cnt = 0;
+ frm->samples_cnt = 0;
+ return PJ_SUCCESS;
+ }
+
+ move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
+ sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
+ move_len = (pj_uint8_t*)sf - move_src + sf->bitlen/8;
+ if (sf->bitlen % 8 != 0)
+ ++move_len;
+ pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext),
+ move_src, move_len);
+
+ frm->samples_cnt = (pj_uint16_t)
+ (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
+ frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
+
+ return PJ_SUCCESS;
+}
+
+/**
* Port interface.
*/
typedef struct pjmedia_port
diff --git a/pjmedia/include/pjmedia/sound.h b/pjmedia/include/pjmedia/sound.h
index 962bfe9..d984ec5 100644
--- a/pjmedia/include/pjmedia/sound.h
+++ b/pjmedia/include/pjmedia/sound.h
@@ -94,6 +94,22 @@
/**
+ * Stream setting for opening sound device with non-PCM data.
+ */
+typedef struct pjmedia_snd_setting
+{
+ pjmedia_fourcc format; /**< Format (FourCC ID). */
+ pj_uint32_t bitrate; /**< Bitrate (bps). */
+ pj_uint32_t mode; /**< Mode, e.g: iLBC format has
+ 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_setting;
+
+
+/**
* This callback is called by player stream when it needs additional data
* to be played by the device. Application must fill in the whole of output
* buffer with sound samples.
@@ -273,6 +289,46 @@
/**
+ * Create sound stream for capturing audio and/or audio playback, from the
+ * same device. This also allows opening sound stream with extended settings,
+ * e.g: stream format, see #pjmedia_snd_setting.
+ *
+ * @param dir Sound stream direction.
+ * @param rec_id Device index for recorder/capture stream, or
+ * -1 to use the first capable device.
+ * @param play_id Device index for playback stream, or -1 to use
+ * the first capable device.
+ * @param clock_rate Sound device's clock rate to set.
+ * @param channel_count Set number of channels, 1 for mono, or 2 for
+ * stereo. The channel count determines the format
+ * of the frame.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample Set the number of bits per sample. The normal
+ * value for this parameter is 16 bits per sample.
+ * @param rec_cb Callback to handle captured audio samples.
+ * @param play_cb Callback to be called when the sound player needs
+ * more audio samples to play.
+ * @param user_data User data to be associated with the stream.
+ * @param setting Sound device extended setting.
+ * @param p_snd_strm Pointer to receive the stream instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_open2( pjmedia_dir dir,
+ int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_rec_cb rec_cb,
+ pjmedia_snd_play_cb play_cb,
+ void *user_data,
+ const pjmedia_snd_setting *setting,
+ pjmedia_snd_stream **p_snd_strm);
+
+
+/**
* Get information about live stream.
*
* @param strm The stream to be queried.
diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h
index a41357c..4c943a2 100644
--- a/pjmedia/include/pjmedia/sound_port.h
+++ b/pjmedia/include/pjmedia/sound_port.h
@@ -159,7 +159,44 @@
unsigned bits_per_sample,
unsigned options,
pjmedia_snd_port **p_port);
-
+
+
+/**
+ * Create unidirectional or bidirectional sound port. This also allows
+ * creating sound port with extended settings, e.g: audio format, see
+ * #pjmedia_snd_setting.
+ *
+ * @param pool Pool to allocate sound port structure.
+ * @param dir Sound device direction.
+ * @param rec_id Device index for recorder/capture stream, or
+ * -1 to use the first capable device.
+ * @param play_id Device index for playback stream, or -1 to use
+ * the first capable device.
+ * @param clock_rate Sound device's clock rate to set.
+ * @param channel_count Set number of channels, 1 for mono, or 2 for
+ * stereo. The channel count determines the format
+ * of the frame.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample Set the number of bits per sample. The normal
+ * value for this parameter is 16 bits per sample.
+ * @param setting Sound device extended settings, see
+ * #pjmedia_snd_setting.
+ * @param p_port Pointer to receive the sound device port instance.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
+ pjmedia_dir dir,
+ int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ const pjmedia_snd_setting *setting,
+ pjmedia_snd_port **p_port);
+
/**
* Destroy sound device port.
diff --git a/pjmedia/include/pjmedia/symbian_sound_aps.h b/pjmedia/include/pjmedia/symbian_sound_aps.h
index 4d7c8ee..2679702 100644
--- a/pjmedia/include/pjmedia/symbian_sound_aps.h
+++ b/pjmedia/include/pjmedia/symbian_sound_aps.h
@@ -53,8 +53,7 @@
* will be routed to earpiece.
*
* @param stream The sound device stream, the stream should be started
- * before calling this function. This param can be NULL
- * to set the behaviour of next opened stream.
+ * before calling this function.
* @param active Specify PJ_TRUE to activate loudspeaker, and PJ_FALSE
* otherwise.
*
@@ -64,18 +63,6 @@
pjmedia_snd_stream *stream,
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
diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h
index 14786ca..eed07e6 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -190,7 +190,7 @@
/**
* FourCC packing macro.
*/
-#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C1<<24 | C2<<16 | C3<<8 | C4 )
+#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
/**
* FourCC identifier definitions.
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
index 400f22d..a590291 100644
--- a/pjmedia/src/pjmedia-codec/passthrough.c
+++ b/pjmedia/src/pjmedia-codec/passthrough.c
@@ -459,13 +459,13 @@
(codec_desc[i].samples_per_frame * 1000 /
codec_desc[i].channel_count /
codec_desc[i].clock_rate);
- attr->info.format.u32 = codec_desc[i].format.u32;
+ attr->info.format = codec_desc[i].format;
/* Default flags. */
attr->setting.frm_per_pkt = codec_desc[i].frm_per_pkt;
- attr->setting.plc = 1;
+ attr->setting.plc = 0;
attr->setting.penh= 0;
- attr->setting.vad = 1;
+ attr->setting.vad = 0;
attr->setting.cng = attr->setting.vad;
attr->setting.dec_fmtp = codec_desc[i].dec_fmtp;
@@ -719,7 +719,7 @@
frames[count].size = codec_data->avg_frame_size;
frames[count].timestamp.u64 = ts->u64 + count*desc->samples_per_frame;
- pkt = ((char*)pkt) + codec_data->avg_frame_size;
+ pkt = (pj_uint8_t*)pkt + codec_data->avg_frame_size;
pkt_size -= codec_data->avg_frame_size;
++count;
@@ -799,8 +799,7 @@
struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
- /* Check if input is formatted in pjmedia_frame */
- pj_assert(input && input->type == PJMEDIA_FRAME_TYPE_AUDIO);
+ pj_assert(input && input->size > 0);
#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
/* Need to rearrange the AMR bitstream, since the bitstream may not be
@@ -817,20 +816,16 @@
pjmedia_codec_amr_predecode(input, setting, &frame);
}
#endif
+ /*
+ PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
+ sizeof(pjmedia_frame_ext_subframe) +
+ input->size,
+ PJMEDIA_CODEC_EFRMTOOSHORT);
+ */
- pj_bzero(output_, sizeof(pjmedia_frame_ext));
- output_->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
-
- if (input && input->size > 0) {
- PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
- sizeof(pjmedia_frame_ext_subframe) +
- input->size,
- PJMEDIA_CODEC_EFRMTOOSHORT);
-
- pjmedia_frame_ext_append_subframe(output_, input->buf,
- (pj_uint16_t)(input->size << 3),
- (pj_uint16_t)desc->samples_per_frame);
- }
+ pjmedia_frame_ext_append_subframe(output_, input->buf,
+ (pj_uint16_t)(input->size << 3),
+ (pj_uint16_t)desc->samples_per_frame);
return PJ_SUCCESS;
}
@@ -842,7 +837,16 @@
unsigned output_buf_len,
struct pjmedia_frame *output)
{
- return codec_decode(codec, NULL, output_buf_len, output);
+ codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+ struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+ pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
+
+ PJ_UNUSED_ARG(output_buf_len);
+
+ pjmedia_frame_ext_append_subframe(output_, NULL, 0,
+ (pj_uint16_t)desc->samples_per_frame);
+
+ return PJ_SUCCESS;
}
#endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index 9639948..ca34fab 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -996,14 +996,15 @@
pjmedia_port_put_frame(cport_dst->port,
(pjmedia_frame*)f_dst);
- /* Update TX timestamp. */
- pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->samples_per_frame);
-
/* Reset TX buffer. */
f_dst->subframe_cnt = 0;
f_dst->samples_cnt = 0;
}
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->samples_per_frame);
+
}
}
@@ -1036,13 +1037,13 @@
if (cport_dst->port) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
- /* Update TX timestamp. */
- pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->samples_per_frame);
-
/* Reset TX buffer. */
frm_dst->size = 0;
}
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->samples_per_frame);
}
}
@@ -1062,15 +1063,16 @@
frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
frm_dst->size = cport_dst->samples_per_frame << 1;
- if (cport_dst->port)
+ if (cport_dst->port) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
+ /* Reset TX buffer. */
+ frm_dst->size = 0;
+ }
+
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
-
- /* Reset TX buffer. */
- frm_dst->size = 0;
}
} else {
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
@@ -1079,29 +1081,31 @@
frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
(cport_dst->samples_per_frame-
- f_dst->samples_cnt));
- if (cport_dst->port)
+ f_dst->samples_cnt));
+ if (cport_dst->port) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
+ /* Reset TX buffer. */
+ f_dst->subframe_cnt = 0;
+ f_dst->samples_cnt = 0;
+ }
+
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
-
- /* Reset TX buffer. */
- f_dst->subframe_cnt = 0;
- f_dst->samples_cnt = 0;
}
}
/* Synchronize clock. */
while (pj_cmp_timestamp(&cport_dst->ts_clock,
- &cport_dst->ts_tx) >= 0)
+ &cport_dst->ts_tx) > 0)
{
- if (cport_dst->port) {
- frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
- frm_dst->timestamp = cport_dst->ts_tx;
+ frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
+ frm_dst->timestamp = cport_dst->ts_tx;
+ if (cport_dst->port)
pjmedia_port_put_frame(cport_dst->port, frm_dst);
- }
+
+ /* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
}
}
@@ -1234,7 +1238,7 @@
cport->tx_level = 0;
- while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) >= 0)
+ while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
{
if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
pjmedia_frame tmp_f;
@@ -1264,36 +1268,35 @@
pjmedia_frame_ext_subframe *sf;
pj_uint16_t samples_per_subframe;
+ if (f_src_->samples_cnt < this_cport->samples_per_frame) {
+ pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ break;
+ }
+
f_dst->samples_cnt = 0;
f_dst->subframe_cnt = 0;
i = 0;
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
+
while (f_dst->samples_cnt < this_cport->samples_per_frame) {
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
+ pj_assert(sf);
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
samples_per_subframe);
}
/* Shift left TX buffer. */
- sf = pjmedia_frame_ext_get_subframe(f_src_, i);
- if (sf) {
- pjmedia_frame_ext_subframe *sf_end;
- unsigned len;
-
- sf_end = pjmedia_frame_ext_get_subframe(f_src_,
- f_src_->subframe_cnt -1);
- len = (pj_uint8_t*)sf_end - (pj_uint8_t*)sf + sf_end->bitlen/8;
- if (sf_end->bitlen % 8 != 0)
- ++len;
- pj_memmove(this_cport->tx_buf + sizeof(pjmedia_frame_ext), sf,
- len);
- }
- f_src_->samples_cnt = f_src_->samples_cnt -
- (pj_uint16_t)(i * samples_per_subframe);
- f_src_->subframe_cnt = f_src_->subframe_cnt - (pj_uint16_t)i;
+ pjmedia_frame_ext_pop_subframes(f_src_, i);
} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if ((f_src->size>>1) < this_cport->samples_per_frame) {
+ pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ break;
+ }
+
pjmedia_copy_samples((pj_int16_t*)frame->buf,
(pj_int16_t*)f_src->buf,
this_cport->samples_per_frame);
@@ -1306,6 +1309,10 @@
(pj_int16_t*)f_src->buf +
this_cport->samples_per_frame,
f_src->size >> 1);
+ } else { /* PJMEDIA_FRAME_TYPE_NONE */
+ /* Reset TX buffer */
+ pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
}
} while (0);
@@ -1326,11 +1333,6 @@
unsigned j;
pj_int32_t level;
- /* Check for correct size. */
- PJ_ASSERT_RETURN( f->size == conf->samples_per_frame *
- conf->bits_per_sample / 8,
- PJMEDIA_ENCSAMPLESPFRAME);
-
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
/* Skip if this port is muted/disabled. */
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 3a21ee5..c3f4968 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -35,14 +35,6 @@
//#define TEST_OVERFLOW_UNDERFLOW
-enum
-{
- PJMEDIA_PLC_ENABLED = 1,
-};
-
-//#define DEFAULT_OPTIONS PJMEDIA_PLC_ENABLED
-#define DEFAULT_OPTIONS 0
-
struct pjmedia_snd_port
{
@@ -51,7 +43,6 @@
pjmedia_snd_stream *snd_stream;
pjmedia_dir dir;
pjmedia_port *port;
- unsigned options;
pjmedia_echo_state *ec_state;
unsigned aec_tail_len;
@@ -66,6 +57,7 @@
unsigned channel_count;
unsigned samples_per_frame;
unsigned bits_per_sample;
+ pjmedia_snd_setting setting;
#if PJMEDIA_SOUND_USE_DELAYBUF
pjmedia_delay_buf *delay_buf;
@@ -244,61 +236,120 @@
}
/*
+ * The callback called by sound player when it needs more samples to be
+ * played. This version is for non-PCM data.
+ */
+static pj_status_t play_cb_ext(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *output,
+ /* out */ unsigned size)
+{
+ pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+ pjmedia_port *port;
+ pjmedia_frame *frame = (pjmedia_frame*) output;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(timestamp);
+
+ /* We're risking accessing the port without holding any mutex.
+ * It's possible that port is disconnected then destroyed while
+ * we're trying to access it.
+ * But in the name of performance, we'll try this approach until
+ * someone complains when it crashes.
+ */
+ port = snd_port->port;
+ if (port == NULL) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
+ }
+
+ status = pjmedia_port_get_frame(port, frame);
+
+ return status;
+}
+
+
+/*
+ * The callback called by sound recorder when it has finished capturing a
+ * frame. This version is for non-PCM data.
+ */
+static pj_status_t rec_cb_ext(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ void *input,
+ /* in*/ unsigned size)
+{
+ pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+ pjmedia_port *port;
+ pjmedia_frame *frame = (pjmedia_frame*)input;
+
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(timestamp);
+
+ /* We're risking accessing the port without holding any mutex.
+ * It's possible that port is disconnected then destroyed while
+ * we're trying to access it.
+ * But in the name of performance, we'll try this approach until
+ * someone complains when it crashes.
+ */
+ port = snd_port->port;
+ if (port == NULL)
+ return PJ_SUCCESS;
+
+ pjmedia_port_put_frame(port, frame);
+
+ return PJ_SUCCESS;
+}
+
+/*
* Start the sound stream.
* This may be called even when the sound stream has already been started.
*/
static pj_status_t start_sound_device( pj_pool_t *pool,
pjmedia_snd_port *snd_port )
{
+ pjmedia_snd_rec_cb snd_rec_cb;
+ pjmedia_snd_play_cb snd_play_cb;
pj_status_t status;
/* Check if sound has been started. */
if (snd_port->snd_stream != NULL)
return PJ_SUCCESS;
- /* Open sound stream. */
- if (snd_port->dir == PJMEDIA_DIR_CAPTURE) {
- status = pjmedia_snd_open_rec( snd_port->rec_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &rec_cb,
- snd_port,
- &snd_port->snd_stream);
+ PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
+ snd_port->dir == PJMEDIA_DIR_PLAYBACK ||
+ snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
+ PJ_EBUG);
- } else if (snd_port->dir == PJMEDIA_DIR_PLAYBACK) {
- status = pjmedia_snd_open_player( snd_port->play_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &play_cb,
- snd_port,
- &snd_port->snd_stream);
-
- } else if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
- status = pjmedia_snd_open( snd_port->rec_id,
- snd_port->play_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &rec_cb,
- &play_cb,
- snd_port,
- &snd_port->snd_stream);
+ if (snd_port->setting.format.u32 == 0 ||
+ snd_port->setting.format.u32 == PJMEDIA_FOURCC_L16)
+ {
+ snd_rec_cb = &rec_cb;
+ snd_play_cb = &play_cb;
} else {
- pj_assert(!"Invalid dir");
- status = PJ_EBUG;
+ snd_rec_cb = &rec_cb_ext;
+ snd_play_cb = &play_cb_ext;
}
+ status = pjmedia_snd_open2( snd_port->dir,
+ snd_port->rec_id,
+ snd_port->play_id,
+ snd_port->clock_rate,
+ snd_port->channel_count,
+ snd_port->samples_per_frame,
+ snd_port->bits_per_sample,
+ snd_rec_cb,
+ snd_play_cb,
+ snd_port,
+ &snd_port->setting,
+ &snd_port->snd_stream);
+
if (status != PJ_SUCCESS)
return status;
#ifdef SIMULATE_LOST_PCT
- snd_port->options |= PJMEDIA_PLC_ENABLED;
+ snd_port->setting.plc = PJ_TRUE;
#endif
/* If we have player components, allocate buffer to save the last
@@ -306,7 +357,7 @@
* lost concealment (PLC) algorithm.
*/
if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) &&
- (snd_port->options & PJMEDIA_PLC_ENABLED))
+ (snd_port->setting.plc))
{
status = pjmedia_plc_create(pool, snd_port->clock_rate,
snd_port->samples_per_frame *
@@ -373,6 +424,8 @@
{
pjmedia_snd_port *snd_port;
+ PJ_UNUSED_ARG(options);
+
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -381,7 +434,6 @@
snd_port->rec_id = rec_id;
snd_port->play_id = play_id;
snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
- snd_port->options = options | DEFAULT_OPTIONS;
snd_port->clock_rate = clock_rate;
snd_port->channel_count = channel_count;
snd_port->samples_per_frame = samples_per_frame;
@@ -428,6 +480,8 @@
{
pjmedia_snd_port *snd_port;
+ PJ_UNUSED_ARG(options);
+
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -435,7 +489,6 @@
snd_port->rec_id = dev_id;
snd_port->dir = PJMEDIA_DIR_CAPTURE;
- snd_port->options = options | DEFAULT_OPTIONS;
snd_port->clock_rate = clock_rate;
snd_port->channel_count = channel_count;
snd_port->samples_per_frame = samples_per_frame;
@@ -465,6 +518,8 @@
{
pjmedia_snd_port *snd_port;
+ PJ_UNUSED_ARG(options);
+
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -472,7 +527,6 @@
snd_port->play_id = dev_id;
snd_port->dir = PJMEDIA_DIR_PLAYBACK;
- snd_port->options = options | DEFAULT_OPTIONS;
snd_port->clock_rate = clock_rate;
snd_port->channel_count = channel_count;
snd_port->samples_per_frame = samples_per_frame;
@@ -489,6 +543,67 @@
/*
+ * Create bidirectional port.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
+ pjmedia_dir dir,
+ int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ const pjmedia_snd_setting *setting,
+ pjmedia_snd_port **p_port)
+{
+ pjmedia_snd_port *snd_port;
+
+ PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+
+ snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
+ PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+
+ snd_port->dir = dir;
+ snd_port->rec_id = rec_id;
+ snd_port->play_id = play_id;
+ snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ snd_port->clock_rate = clock_rate;
+ snd_port->channel_count = channel_count;
+ snd_port->samples_per_frame = samples_per_frame;
+ snd_port->bits_per_sample = bits_per_sample;
+ snd_port->setting = *setting;
+
+#if PJMEDIA_SOUND_USE_DELAYBUF
+ if (snd_port->setting.format.u32 == 0 ||
+ snd_port->setting.format.u32 == PJMEDIA_FOURCC_L16)
+ {
+ pj_status_t status;
+ unsigned ptime;
+
+ ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
+
+ status = pjmedia_delay_buf_create(pool, "snd_buff",
+ clock_rate, samples_per_frame,
+ channel_count,
+ PJMEDIA_SOUND_BUFFER_COUNT * ptime,
+ 0, &snd_port->delay_buf);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ }
+#endif
+
+ *p_port = snd_port;
+
+
+ /* Start sound device immediately.
+ * If there's no port connected, the sound callback will return
+ * empty signal.
+ */
+ return start_sound_device( pool, snd_port );
+
+}
+
+
+/*
* Destroy port (also destroys the sound device).
*/
PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 2e9d119..d765fb1 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -410,6 +410,116 @@
}
+/* The other version of get_frame callback used when stream port format
+ * is non linear PCM.
+ */
+static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
+{
+ pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
+ pjmedia_channel *channel = stream->dec;
+ pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame;
+ unsigned samples_per_frame, samples_required;
+ pj_status_t status;
+
+ /* Return no frame if channel is paused */
+ if (channel->paused) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
+ }
+
+ /* Repeat get frame from the jitter buffer and decode the frame
+ * until we have enough frames according to codec's ptime.
+ */
+
+ samples_required = stream->port.info.samples_per_frame;
+ samples_per_frame = stream->codec_param.info.frm_ptime *
+ stream->codec_param.info.clock_rate *
+ stream->codec_param.info.channel_cnt /
+ 1000;
+
+ pj_bzero(f, sizeof(pjmedia_frame_ext));
+ f->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+
+ while (f->samples_cnt < samples_required) {
+ char frame_type;
+ pj_size_t frame_size;
+ pj_uint32_t bit_info;
+
+ /* Lock jitter buffer mutex first */
+ pj_mutex_lock( stream->jb_mutex );
+
+ /* Get frame from jitter buffer. */
+ pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size,
+ &frame_type, &bit_info);
+
+ /* Unlock jitter buffer mutex. */
+ pj_mutex_unlock( stream->jb_mutex );
+
+ if (frame_type == PJMEDIA_JB_NORMAL_FRAME) {
+ /* Got "NORMAL" frame from jitter buffer */
+ pjmedia_frame frame_in;
+
+ /* Decode */
+ frame_in.buf = channel->out_pkt;
+ frame_in.size = frame_size;
+ frame_in.bit_info = bit_info;
+ frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ status = stream->codec->op->decode( stream->codec, &frame_in,
+ 0, frame);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((port->info.name.ptr, "codec decode() error",
+ status));
+ pjmedia_frame_ext_append_subframe(f, NULL, 0,
+ (pj_uint16_t)samples_per_frame);
+ }
+ } else {
+ status = (*stream->codec->op->recover)(stream->codec,
+ 0, frame);
+ if (status != PJ_SUCCESS) {
+ pjmedia_frame_ext_append_subframe(f, NULL, 0,
+ (pj_uint16_t)samples_per_frame);
+ }
+
+ if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
+ PJ_LOG(1,(stream->port.info.name.ptr, "Frame lost!"));
+ } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+ /* Jitter buffer is empty. Check if this is the first "empty"
+ * state.
+ */
+ if (frame_type != stream->jb_last_frm) {
+ pjmedia_jb_state jb_state;
+
+ /* Report the state of jitter buffer */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+ PJ_LOG(1,(stream->port.info.name.ptr,
+ "Jitter buffer empty (prefetch=%d)",
+ jb_state.prefetch));
+ }
+ } else {
+ pjmedia_jb_state jb_state;
+
+ /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
+ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
+
+ /* Get the state of jitter buffer */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+
+ if (stream->jb_last_frm != frame_type) {
+ PJ_LOG(1,(stream->port.info.name.ptr,
+ "Jitter buffer is bufferring (prefetch=%d)",
+ jb_state.prefetch));
+ }
+ }
+ }
+
+ stream->jb_last_frm = frame_type;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
/*
* Transmit DTMF
*/
@@ -688,6 +798,9 @@
/* Number of samples in the frame */
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
+ else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
+ ts_len = stream->port.info.samples_per_frame /
+ stream->port.info.channel_count;
else
ts_len = 0;
@@ -754,6 +867,8 @@
*/
} else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
frame->buf == NULL &&
+ (stream->port.info.format.u32 == 0 ||
+ stream->port.info.format.u32 == PJMEDIA_FOURCC_L16) &&
(stream->dir & PJMEDIA_DIR_ENCODING) &&
stream->codec_param.info.frm_ptime *
stream->codec_param.info.channel_cnt *
@@ -1491,9 +1606,17 @@
pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
stream->port.info.clock_rate = info->fmt.clock_rate;
stream->port.info.channel_count = info->fmt.channel_cnt;
+ stream->port.info.format = info->param->info.format;
stream->port.port_data.pdata = stream;
- stream->port.put_frame = &put_frame;
- stream->port.get_frame = &get_frame;
+ if (stream->port.info.format.u32 == 0 ||
+ stream->port.info.format.u32 == PJMEDIA_FOURCC_L16)
+ {
+ stream->port.put_frame = &put_frame;
+ stream->port.get_frame = &get_frame;
+ } else {
+ stream->port.put_frame = &put_frame;
+ stream->port.get_frame = &get_frame_ext;
+ }
/* Init stream: */
diff --git a/pjmedia/src/pjmedia/symbian_sound_aps.cpp b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
index 687815c..bb17e39 100644
--- a/pjmedia/src/pjmedia/symbian_sound_aps.cpp
+++ b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
@@ -17,10 +17,11 @@
* 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/symbian_sound_aps.h>
#include <pjmedia/alaw_ulaw.h>
#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/math.h>
@@ -59,9 +60,6 @@
/* App UID to open global APS queues to communicate with the APS server. */
extern TPtrC APP_UID;
-/* Default setting */
-static pjmedia_snd_aps_setting def_setting;
-
/* APS G.711 frame length */
static pj_uint8_t aps_g711_frame_len;
@@ -87,6 +85,7 @@
pjmedia_snd_rec_cb rec_cb;
pjmedia_snd_play_cb play_cb;
void *user_data;
+ pjmedia_snd_setting setting;
// Audio engine
CPjAudioEngine *engine;
@@ -404,13 +403,13 @@
// 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 };
+ enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
+
start.UniversalTime();
- now.UniversalTime();
- while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000) {
+ do {
pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME);
now.UniversalTime();
- }
+ } while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000);
iSession.Close();
@@ -617,6 +616,7 @@
{
pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+ /* Buffer has to contain normal speech. */
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
/* Detect the recorder G.711 frame size, player frame size will follow
@@ -659,6 +659,7 @@
pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
unsigned g711_frame_len = aps_g711_frame_len;
+ /* Init buffer attributes and header. */
buf.iCommand = CQueueHandler::EAPSPlayData;
buf.iStatus = 0;
buf.iBuffer.Zero();
@@ -696,10 +697,117 @@
static void RecCb(TAPSCommBuffer &buf, void *user_data)
{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+ pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
+ unsigned samples_processed = 0;
+
+ switch(strm->setting.format.u32) {
+
+ case PJMEDIA_FOURCC_G711U:
+ case PJMEDIA_FOURCC_G711A:
+ 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));
+ }
+
+ /* Convert APS buffer format into pjmedia_frame_ext. Whenever
+ * samples count in the frame is equal to stream's samples per frame,
+ * call parent stream callback.
+ */
+ while (samples_processed < aps_g711_frame_len) {
+ unsigned tmp;
+ const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2 +
+ samples_processed;
+
+ tmp = PJ_MIN(strm->samples_per_frame - frame->samples_cnt,
+ aps_g711_frame_len - samples_processed);
+
+ pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
+ samples_processed += tmp;
+
+ if (frame->samples_cnt == strm->samples_per_frame) {
+ frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+ strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
+ frame->samples_cnt = 0;
+ frame->subframe_cnt = 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
}
static void PlayCb(TAPSCommBuffer &buf, void *user_data)
{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+ pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
+ unsigned g711_frame_len = aps_g711_frame_len;
+ unsigned samples_ready = 0;
+
+ /* Init buffer attributes and header. */
+ buf.iCommand = CQueueHandler::EAPSPlayData;
+ buf.iStatus = 0;
+ buf.iBuffer.Zero();
+
+ switch(strm->setting.format.u32) {
+
+ case PJMEDIA_FOURCC_G711U:
+ case PJMEDIA_FOURCC_G711A:
+ /* Add header. */
+ 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 samples to play. */
+ while (samples_ready < g711_frame_len) {
+ if (frame->samples_cnt == 0) {
+ frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+ strm->play_cb(strm->user_data, 0, strm->play_buf,
+ strm->samples_per_frame<<1);
+
+ pj_assert(frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED ||
+ frame->base.type == PJMEDIA_FRAME_TYPE_NONE);
+ }
+
+ if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+ pjmedia_frame_ext_subframe *sf;
+ unsigned samples_cnt;
+
+ sf = pjmedia_frame_ext_get_subframe(frame, 0);
+ samples_cnt = frame->samples_cnt / frame->subframe_cnt;
+ if (sf->data && sf->bitlen)
+ buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
+ else
+ buf.iBuffer.AppendFill(0, samples_cnt);
+ samples_ready += samples_cnt;
+
+ pjmedia_frame_ext_pop_subframes(frame, 1);
+
+ } else { /* PJMEDIA_FRAME_TYPE_NONE */
+ buf.iBuffer.AppendFill(0, g711_frame_len - samples_ready);
+ samples_ready = g711_frame_len;
+ frame->samples_cnt = 0;
+ frame->subframe_cnt = 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ unsigned tes = buf.iBuffer.Length();
}
/*
@@ -709,14 +817,6 @@
{
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;
}
@@ -750,11 +850,12 @@
pjmedia_snd_rec_cb rec_cb,
pjmedia_snd_play_cb play_cb,
void *user_data,
+ const pjmedia_snd_setting *setting,
pjmedia_snd_stream **p_snd_strm)
{
pj_pool_t *pool;
pjmedia_snd_stream *strm;
- CPjAudioSetting setting;
+ CPjAudioSetting aps_setting;
PjAudioCallback aps_rec_cb;
PjAudioCallback aps_play_cb;
@@ -778,51 +879,51 @@
strm->clock_rate = clock_rate;
strm->channel_count = channel_count;
strm->samples_per_frame = samples_per_frame;
+ strm->setting = *setting;
+
+ if (strm->setting.format.u32 == 0)
+ strm->setting.format.u32 = PJMEDIA_FOURCC_L16;
/* Set audio engine settings. */
- if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
- def_setting.format.u32 == PJMEDIA_FOURCC_L16)
+ if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+ strm->setting.format.u32 == PJMEDIA_FOURCC_L16)
{
- setting.fourcc = TFourCC(KMCPFourCCIdG711);
+ aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
} else {
- setting.fourcc = TFourCC(def_setting.format.u32);
+ aps_setting.fourcc = TFourCC(strm->setting.format.u32);
}
- if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR)
+ if (strm->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))
+ aps_setting.mode = (TAPSCodecMode)strm->setting.bitrate;
+ } else if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+ strm->setting.format.u32 == PJMEDIA_FOURCC_L16 ||
+ (strm->setting.format.u32 == PJMEDIA_FOURCC_ILBC &&
+ strm->setting.mode == 30))
{
- setting.mode = EULawOr30ms;
+ aps_setting.mode = EULawOr30ms;
} else {
- setting.mode = EALawOr20ms;
+ aps_setting.mode = EALawOr20ms;
}
- setting.vad = def_setting.vad;
- setting.plc = def_setting.plc;
- setting.cng = def_setting.cng;
- setting.loudspk = def_setting.loudspk;
+ aps_setting.vad = strm->setting.format.u32==PJMEDIA_FOURCC_L16?
+ EFalse : strm->setting.vad;
+ aps_setting.plc = strm->setting.plc;
+ aps_setting.cng = strm->setting.cng;
+ aps_setting.loudspk = strm->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 {
+ if (strm->setting.format.u32 == PJMEDIA_FOURCC_L16) {
aps_play_cb = &PlayCbPcm;
aps_rec_cb = &RecCbPcm;
+ } else {
+ aps_play_cb = &PlayCb;
+ aps_rec_cb = &RecCb;
}
// Create the audio engine.
TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
aps_rec_cb, aps_play_cb,
- strm, setting));
+ strm, aps_setting));
if (err != KErrNone) {
pj_pool_release(pool);
return PJ_RETURN_OS_ERROR(err);
@@ -833,12 +934,12 @@
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 = (pj_int16_t*)pj_pool_zalloc(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 = (pj_int16_t*)pj_pool_zalloc(pool, samples_per_frame << 1);
strm->rec_buf_len = 0;
// Done.
@@ -860,12 +961,18 @@
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
+ pjmedia_snd_setting setting;
+
if (index < 0) index = 0;
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
+ pj_bzero(&setting, sizeof(setting));
+ setting.format.u32 = PJMEDIA_FOURCC_L16;
+ setting.bitrate = 128000;
+
return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
samples_per_frame, bits_per_sample, rec_cb, NULL,
- user_data, p_snd_strm);
+ user_data, &setting, p_snd_strm);
}
PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
@@ -877,12 +984,18 @@
void *user_data,
pjmedia_snd_stream **p_snd_strm )
{
+ pjmedia_snd_setting setting;
+
if (index < 0) index = 0;
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
+ pj_bzero(&setting, sizeof(setting));
+ setting.format.u32 = PJMEDIA_FOURCC_L16;
+ setting.bitrate = 128000;
+
return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
samples_per_frame, bits_per_sample, NULL, play_cb,
- user_data, p_snd_strm);
+ user_data, &setting, p_snd_strm);
}
PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
@@ -896,13 +1009,41 @@
void *user_data,
pjmedia_snd_stream **p_snd_strm)
{
+ pjmedia_snd_setting setting;
+
if (rec_id < 0) rec_id = 0;
if (play_id < 0) play_id = 0;
PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
+ pj_bzero(&setting, sizeof(setting));
+ setting.format.u32 = PJMEDIA_FOURCC_L16;
+ setting.bitrate = 128000;
+
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);
+ user_data, &setting, p_snd_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_open2( pjmedia_dir dir,
+ int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_rec_cb rec_cb,
+ pjmedia_snd_play_cb play_cb,
+ void *user_data,
+ const pjmedia_snd_setting *setting,
+ pjmedia_snd_stream **p_snd_strm)
+{
+ if (rec_id < 0) rec_id = 0;
+ if (play_id < 0) play_id = 0;
+ PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
+
+ return sound_open(dir, clock_rate, channel_count,
+ samples_per_frame, bits_per_sample, rec_cb, play_cb,
+ user_data, setting, p_snd_strm);
}
/*
@@ -993,7 +1134,6 @@
return PJ_SUCCESS;
}
-
/*
* Activate/deactivate loudspeaker.
*/
@@ -1001,29 +1141,11 @@
pjmedia_snd_stream *stream,
pj_bool_t active)
{
- if (stream == NULL) {
- def_setting.loudspk = active;
- } else {
- if (stream->engine == NULL)
- return PJ_EINVAL;
-
- TInt err = stream->engine->ActivateSpeaker(active);
- if (err != KErrNone)
- return PJ_RETURN_OS_ERROR(err);
- }
-
- 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;
+ PJ_ASSERT_RETURN(stream && stream->engine, PJ_EINVAL);
+
+ TInt err = stream->engine->ActivateSpeaker(active);
+ if (err != KErrNone)
+ return PJ_RETURN_OS_ERROR(err);
return PJ_SUCCESS;
}
diff --git a/pjsip-apps/src/symbian_ua/ua.cpp b/pjsip-apps/src/symbian_ua/ua.cpp
index 442b052..531e552 100644
--- a/pjsip-apps/src/symbian_ua/ua.cpp
+++ b/pjsip-apps/src/symbian_ua/ua.cpp
@@ -90,6 +90,9 @@
static pjsua_call_id g_call_id = PJSUA_INVALID_ID;
static pjsua_buddy_id g_buddy_id = PJSUA_INVALID_ID;
+static pj_pool_t *app_pool;
+static pjmedia_snd_port *snd_port;
+
/* Callback called by the library upon receiving incoming call */
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
@@ -129,6 +132,10 @@
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
if (call_id == g_call_id)
g_call_id = PJSUA_INVALID_ID;
+ if (snd_port) {
+ pjmedia_snd_port_destroy(snd_port);
+ snd_port = NULL;
+ }
} else if (ci.state != PJSIP_INV_STATE_INCOMING) {
if (g_call_id == PJSUA_INVALID_ID)
g_call_id = call_id;
@@ -249,6 +256,74 @@
(int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
}
+/* Notification that stream is created. */
+static void on_stream_created(pjsua_call_id call_id,
+ pjmedia_session *sess,
+ unsigned stream_idx,
+ pjmedia_port **p_port)
+{
+ pjmedia_port *conf;
+ pjmedia_session_info sess_info;
+ pjmedia_stream_info *strm_info;
+ pjmedia_snd_setting setting;
+ unsigned samples_per_frame;
+ pj_status_t status;
+
+ status = pjmedia_session_get_info(sess, &sess_info);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to get session info, "
+ "status=%d", status));
+ return;
+ }
+
+ strm_info = &sess_info.stream_info[stream_idx];
+ if (strm_info->type != PJMEDIA_TYPE_AUDIO)
+ return;
+
+ /* Don't need to reopen sound device when the session doesn't use
+ * PCM format.
+ */
+ if (strm_info->param->info.format.u32 == 0 ||
+ strm_info->param->info.format.u32 == PJMEDIA_FOURCC_L16)
+ {
+ return;
+ }
+
+ pj_bzero(&setting, sizeof(setting));
+ setting.format = strm_info->param->info.format;
+ setting.bitrate = strm_info->param->info.avg_bps;
+ setting.cng = strm_info->param->setting.cng;
+ setting.vad = strm_info->param->setting.vad;
+ setting.plc = strm_info->param->setting.plc;
+
+ /* Reopen sound device. */
+ conf = pjsua_set_no_snd_dev();
+
+ samples_per_frame = conf->info.samples_per_frame;
+
+ status = pjmedia_snd_port_create2(app_pool,
+ PJMEDIA_DIR_CAPTURE_PLAYBACK,
+ 0,
+ 0,
+ 8000,
+ 1,
+ samples_per_frame,
+ 16,
+ &setting,
+ &snd_port);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to reopen sound "
+ "device, status=%d", status));
+ return;
+ }
+
+ status = pjmedia_snd_port_connect(snd_port, conf);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to connect sound "
+ "device to conference, status=%d", status));
+ return;
+ }
+}
//#include<e32debug.h>
@@ -289,6 +364,9 @@
return status;
}
+ /* Create pool for application */
+ app_pool = pjsua_pool_create("pjsua-app", 1000, 1000);
+
/* Init pjsua */
pjsua_config cfg;
pjsua_logging_config log_cfg;
@@ -309,6 +387,7 @@
cfg.cb.on_call_transfer_status = &on_call_transfer_status;
cfg.cb.on_call_replaced = &on_call_replaced;
cfg.cb.on_nat_detect = &on_nat_detect;
+ cfg.cb.on_stream_created = &on_stream_created;
if (SIP_PROXY) {
cfg.outbound_proxy_cnt = 1;
@@ -337,14 +416,10 @@
med_cfg.thread_cnt = 0; // Disable threading on Symbian
med_cfg.has_ioqueue = PJ_FALSE;
med_cfg.clock_rate = 8000;
-#if defined(PJMEDIA_SYM_SND_USE_APS) && (PJMEDIA_SYM_SND_USE_APS==1)
- med_cfg.audio_frame_ptime = 20;
-#else
med_cfg.audio_frame_ptime = 40;
-#endif
med_cfg.ec_tail_len = 0;
med_cfg.enable_ice = USE_ICE;
- med_cfg.snd_auto_close_time = 5; // wait for 5 seconds idle before sound dev get auto-closed
+ med_cfg.snd_auto_close_time = 0; // wait for 0 seconds idle before sound dev get auto-closed
status = pjsua_init(&cfg, &log_cfg, &med_cfg);
if (status != PJ_SUCCESS) {
@@ -802,6 +877,18 @@
max_stack_file, max_stack_line));
#endif
+ // Let pjsua destroys app pool, since the pool may still be used by app
+ // until pjsua_destroy() finished.
+ // e.g: quitting app when there is an active call may cause sound port
+ // memory destroyed before sound port itself gets closed/destroyed.
+ /*
+ // Release application pool
+ if (app_pool) {
+ pj_pool_release(app_pool);
+ app_pool = NULL;
+ }
+ */
+
// Shutdown pjsua
pjsua_destroy();
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 8d2d8a8..5d5d56a 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -172,6 +172,16 @@
#endif /* PJMEDIA_HAS_INTEL_IPP */
+#if PJMEDIA_HAS_PASSTHROUGH_CODECS
+ /* Register passthrough codecs */
+ status = pjmedia_codec_passthrough_init(pjsua_var.med_endpt);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error initializing passthrough codecs",
+ status);
+ return status;
+ }
+#endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
+
#if PJMEDIA_HAS_L16_CODEC
/* Register L16 family codecs, but disable all */
status = pjmedia_codec_l16_init(pjsua_var.med_endpt, 0);
@@ -574,6 +584,10 @@
pjmedia_codec_ipp_deinit();
# endif /* PJMEDIA_HAS_INTEL_IPP */
+# if PJMEDIA_HAS_PASSTHROUGH_CODECS
+ pjmedia_codec_passthrough_deinit();
+# endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
+
# if PJMEDIA_HAS_L16_CODEC
pjmedia_codec_l16_deinit();
# endif /* PJMEDIA_HAS_L16_CODEC */