summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2009-05-06 15:44:12 +0000
committerNanang Izzuddin <nanang@teluu.com>2009-05-06 15:44:12 +0000
commit9193944dbe454603dbe45c505106a9e483df2203 (patch)
treefd630e40ac8755547de2e592a9c6f20569b09032
parentafdc3f91f455e233cb1944cc593d25c7b47afb79 (diff)
Ticket #819:
- Updated APS to supports for sampling rate 16kHz and stereo (using resample). - Fixed buffer len calculations in Symbian MDA. - Disabled multichannel support on Symbian MDA (since its input device doesn't support for stereo?). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2677 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp247
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp13
2 files changed, 206 insertions, 54 deletions
diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
index bfdec1e5..5a5058eb 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
@@ -20,6 +20,8 @@
#include <pjmedia-audiodev/audiodev_imp.h>
#include <pjmedia-audiodev/errno.h>
#include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/resample.h>
+#include <pjmedia/stereo.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/math.h>
@@ -80,7 +82,7 @@ struct aps_stream
pj_pool_t *pool; /**< Memory pool. */
// Common settings.
- pjmedia_aud_param param; /**< Stream param. */
+ pjmedia_aud_param param; /**< Stream param. */
pjmedia_aud_rec_cb rec_cb; /**< Record callback. */
pjmedia_aud_play_cb play_cb; /**< Playback callback. */
void *user_data; /**< Application data. */
@@ -97,6 +99,20 @@ struct aps_stream
pj_int16_t *rec_buf; /**< Record buffer. */
pj_uint16_t rec_buf_len; /**< Record buffer length. */
void *strm_data; /**< Stream data. */
+
+ /* Resampling is needed, in case audio device is opened with clock rate
+ * other than 8kHz (only for PCM format).
+ */
+ pjmedia_resample *play_resample; /**< Resampler for playback. */
+ pjmedia_resample *rec_resample; /**< Resampler for recording */
+ pj_uint16_t resample_factor; /**< Resample factor, requested
+ clock rate / 8000 */
+
+ /* When stream is working in PCM format, where the samples may need to be
+ * resampled from/to different clock rate and/or channel count, PCM buffer
+ * is needed to perform such resampling operations.
+ */
+ pj_int16_t *pcm_buf; /**< PCM buffer. */
};
@@ -677,7 +693,9 @@ TInt CPjAudioEngine::ActivateSpeaker(TBool active)
return KErrNotReady;
}
-
+/****************************************************************************
+ * Internal APS callbacks for PCM format
+ */
static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
{
@@ -698,28 +716,63 @@ static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
/* 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;
+ unsigned samples_processed = 0;
- while (dec_len < aps_g711_frame_len) {
- unsigned tmp;
+ while (samples_processed < aps_g711_frame_len) {
+ unsigned samples_to_process;
+ unsigned samples_req;
+
+ samples_to_process = aps_g711_frame_len - samples_processed;
+ samples_req = (strm->param.samples_per_frame /
+ strm->param.channel_count /
+ strm->resample_factor) -
+ strm->rec_buf_len;
+ if (samples_to_process > samples_req)
+ samples_to_process = samples_req;
- tmp = PJ_MIN(strm->param.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;
+ buf.iBuffer.Ptr() + 2 + samples_processed,
+ samples_to_process);
- pj_assert(strm->rec_buf_len <= strm->param.samples_per_frame);
+ strm->rec_buf_len += samples_to_process;
+ samples_processed += samples_to_process;
- if (strm->rec_buf_len == strm->param.samples_per_frame) {
+ /* Buffer is full, time to call parent callback */
+ if (strm->rec_buf_len == strm->param.samples_per_frame /
+ strm->param.channel_count /
+ strm->resample_factor)
+ {
pjmedia_frame f;
-
+
+ /* Need to resample clock rate? */
+ if (strm->rec_resample) {
+ unsigned resampled = 0;
+
+ while (resampled < strm->rec_buf_len) {
+ pjmedia_resample_run(strm->rec_resample,
+ &strm->rec_buf[resampled],
+ strm->pcm_buf +
+ resampled * strm->resample_factor);
+ resampled += 80;
+ }
+ f.buf = strm->pcm_buf;
+ } else {
+ f.buf = strm->rec_buf;
+ }
+
+ /* Need to convert channel count? */
+ if (strm->param.channel_count != 1) {
+ pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
+ (pj_int16_t*)f.buf,
+ strm->param.channel_count,
+ strm->param.samples_per_frame /
+ strm->param.channel_count,
+ 0);
+ }
+
+ /* Call parent callback */
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
- f.buf = strm->rec_buf;
- f.size = strm->rec_buf_len << 1;
-
+ f.size = strm->param.samples_per_frame << 1;
strm->rec_cb(strm->user_data, &f);
strm->rec_buf_len = 0;
}
@@ -745,39 +798,74 @@ static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
/* 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) {
+ unsigned samples_processed = 0;
+
+ while (samples_processed < g711_frame_len) {
+ /* Need more samples to play, time to call parent callback */
if (strm->play_buf_len == 0) {
pjmedia_frame f;
+ unsigned samples_got;
- f.buf = strm->play_buf;
f.size = strm->param.samples_per_frame << 1;
-
+ if (strm->play_resample || strm->param.channel_count != 1)
+ f.buf = strm->pcm_buf;
+ else
+ f.buf = strm->play_buf;
+
+ /* Call parent callback */
strm->play_cb(strm->user_data, &f);
if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
- pjmedia_zero_samples(strm->play_buf,
+ pjmedia_zero_samples((pj_int16_t*)f.buf,
strm->param.samples_per_frame);
}
- strm->play_buf_len = strm->param.samples_per_frame;
+ samples_got = strm->param.samples_per_frame /
+ strm->param.channel_count /
+ strm->resample_factor;
+
+ /* Need to convert channel count? */
+ if (strm->param.channel_count != 1) {
+ pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
+ (pj_int16_t*)f.buf,
+ strm->param.channel_count,
+ strm->param.samples_per_frame,
+ PJ_FALSE,
+ 0);
+ }
+
+ /* Need to resample clock rate? */
+ if (strm->play_resample) {
+ unsigned resampled = 0;
+
+ while (resampled < samples_got)
+ {
+ pjmedia_resample_run(strm->play_resample,
+ strm->pcm_buf +
+ resampled * strm->resample_factor,
+ &strm->play_buf[resampled]);
+ resampled += 80;
+ }
+ }
+
+ strm->play_buf_len = samples_got;
strm->play_buf_start = 0;
}
unsigned tmp;
- tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - enc_len);
+ tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
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;
+ samples_processed += tmp;
strm->play_buf_len -= tmp;
strm->play_buf_start += tmp;
}
}
/****************************************************************************
- * Internal APS callbacks
+ * Internal APS callbacks for non-PCM format
*/
static void RecCb(TAPSCommBuffer &buf, void *user_data)
@@ -1262,6 +1350,24 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
/* Can only support 16bits per sample */
PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
+ /* Supported clock rates:
+ * - for non-PCM format: 8kHz
+ * - for PCM format: 8kHz and 16kHz
+ */
+ PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
+ (param->clock_rate == 16000 &&
+ param->ext_fmt.id == PJMEDIA_FORMAT_L16),
+ PJ_EINVAL);
+
+ /* Supported channels number:
+ * - for non-PCM format: mono
+ * - for PCM format: mono and stereo
+ */
+ PJ_ASSERT_RETURN(param->channel_count == 1 ||
+ (param->channel_count == 2 &&
+ param->ext_fmt.id == PJMEDIA_FORMAT_L16),
+ PJ_EINVAL);
+
/* Create and Initialize stream descriptor */
pool = pj_pool_create(af->pf, "aps-dev", 1000, 1000, NULL);
PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
@@ -1341,34 +1447,26 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
aps_rec_cb = &RecCb;
}
- /* Create the audio engine. */
- TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
- aps_rec_cb, aps_play_cb,
- strm, aps_setting));
- if (err != KErrNone) {
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err);
- }
-
- /* Apply output volume setting if specified */
- if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
- stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
- &param->output_vol);
- }
-
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_zalloc(pool,
- strm->param.samples_per_frame << 1);
+ strm->resample_factor = strm->param.clock_rate / 8000;
+
+ /* play_buf size is samples per frame scaled in to 8kHz mono. */
+ strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
+ pool,
+ (strm->param.samples_per_frame /
+ strm->resample_factor /
+ strm->param.channel_count) << 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_zalloc(pool,
- strm->param.samples_per_frame << 1);
+ /* rec_buf size is samples per frame scaled in to 8kHz mono. */
+ strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(
+ pool,
+ (strm->param.samples_per_frame /
+ strm->resample_factor /
+ strm->param.channel_count) << 1);
strm->rec_buf_len = 0;
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
@@ -1378,6 +1476,59 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
strm->strm_data = (void*)g729_bitstream;
}
+ /* Init resampler when format is PCM and clock rate is not 8kHz */
+ if (strm->param.clock_rate != 8000 &&
+ strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
+ {
+ pj_status_t status;
+
+ if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
+ /* Create resample for recorder */
+ status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
+ 8000,
+ strm->param.clock_rate,
+ 80,
+ &strm->rec_resample);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
+ /* Create resample for player */
+ status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
+ strm->param.clock_rate,
+ 8000,
+ 80 * strm->resample_factor,
+ &strm->play_resample);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+ }
+
+ /* Create PCM buffer, when the clock rate is not 8kHz or not mono */
+ if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
+ (strm->resample_factor > 1 || strm->param.channel_count != 1))
+ {
+ strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
+ strm->param.samples_per_frame << 1);
+ }
+
+
+ /* Create the audio engine. */
+ TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
+ aps_rec_cb, aps_play_cb,
+ strm, aps_setting));
+ if (err != KErrNone) {
+ pj_pool_release(pool);
+ return PJ_RETURN_OS_ERROR(err);
+ }
+
+ /* Apply output volume setting if specified */
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &param->output_vol);
+ }
+
/* Done */
strm->base.op = &stream_op;
*p_aud_strm = &strm->base;
diff --git a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
index 5c0e1abf..9a1f2d65 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
@@ -270,7 +270,6 @@ CPjAudioInputEngine::CPjAudioInputEngine(struct mda_stream *parent_strm,
iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
lastError_(KErrNone), timeStamp_(0),
frameLen_(parent_strm->param.samples_per_frame *
- parent_strm->param.channel_count *
BYTES_PER_SAMPLE),
frameRecBuf_(NULL), frameRecBufLen_(0)
{
@@ -425,9 +424,9 @@ void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
return;
}
- if (frameRecBufLen_ || aBuffer.Size() < frameLen_) {
- pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Size());
- frameRecBufLen_ += aBuffer.Size();
+ if (frameRecBufLen_ || aBuffer.Length() < frameLen_) {
+ pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length());
+ frameRecBufLen_ += aBuffer.Length();
}
if (frameRecBufLen_) {
@@ -453,7 +452,7 @@ void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
f.buf = (void*)aBuffer.Ptr();
- f.size = aBuffer.Size();
+ f.size = aBuffer.Length();
f.timestamp.u32.lo = timeStamp_;
f.bit_info = 0;
@@ -571,7 +570,6 @@ CPjAudioOutputEngine::CPjAudioOutputEngine(struct mda_stream *parent_strm,
void CPjAudioOutputEngine::ConstructL()
{
frameBufSize_ = parentStrm_->param.samples_per_frame *
- parentStrm_->param.channel_count *
BYTES_PER_SAMPLE;
frameBuf_ = new TUint8[frameBufSize_];
}
@@ -897,6 +895,9 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
PJ_ASSERT_RETURN((param->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT)==0 ||
param->ext_fmt.id == PJMEDIA_FORMAT_L16,
PJ_ENOTSUP);
+
+ /* It seems that MDA recorder only supports for mono channel. */
+ PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL);
/* Create and Initialize stream descriptor */
pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);