1
/* $Id: symb_aps_dev.cpp 3809 2011-10-11 03:05:34Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia-audiodev/audiodev_imp.h>
21
#include <pjmedia-audiodev/errno.h>
22
#include <pjmedia/alaw_ulaw.h>
23
#include <pjmedia/resample.h>
24
#include <pjmedia/stereo.h>
25
#include <pj/assert.h>
29
#include <pj/string.h>
31
#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
33
#include <e32msgqueue.h>
34
#include <sounddevice.h>
35
#include <APSClientSession.h>
36
#include <pjmedia-codec/amr_helper.h>
38
/* Pack/unpack G.729 frame of S60 DSP codec, taken from:
39
* http://wiki.forum.nokia.com/index.php/TSS000776_-_Payload_conversion_for_G.729_audio_format
41
#include "s60_g729_bitstream.h"
44
#define THIS_FILE "symb_aps_dev.c"
45
#define BITS_PER_SAMPLE 16
49
# define TRACE_(st) PJ_LOG(3, st)
55
/* App UID to open global APS queues to communicate with the APS server. */
58
/* APS G.711 frame length */
59
static pj_uint8_t aps_g711_frame_len;
65
pjmedia_aud_dev_factory base;
68
pjmedia_aud_dev_info dev_info;
72
/* Forward declaration of CPjAudioEngine */
80
pjmedia_aud_stream base; /**< Base class. */
83
pj_pool_t *pool; /**< Memory pool. */
86
pjmedia_aud_param param; /**< Stream param. */
87
pjmedia_aud_rec_cb rec_cb; /**< Record callback. */
88
pjmedia_aud_play_cb play_cb; /**< Playback callback. */
89
void *user_data; /**< Application data. */
92
CPjAudioEngine *engine; /**< Internal engine. */
94
pj_timestamp ts_play; /**< Playback timestamp.*/
95
pj_timestamp ts_rec; /**< Record timestamp. */
97
pj_int16_t *play_buf; /**< Playback buffer. */
98
pj_uint16_t play_buf_len; /**< Playback buffer length. */
99
pj_uint16_t play_buf_start; /**< Playback buffer start index. */
100
pj_int16_t *rec_buf; /**< Record buffer. */
101
pj_uint16_t rec_buf_len; /**< Record buffer length. */
102
void *strm_data; /**< Stream data. */
104
/* Resampling is needed, in case audio device is opened with clock rate
105
* other than 8kHz (only for PCM format).
107
pjmedia_resample *play_resample; /**< Resampler for playback. */
108
pjmedia_resample *rec_resample; /**< Resampler for recording */
109
pj_uint16_t resample_factor; /**< Resample factor, requested
112
/* When stream is working in PCM format, where the samples may need to be
113
* resampled from/to different clock rate and/or channel count, PCM buffer
114
* is needed to perform such resampling operations.
116
pj_int16_t *pcm_buf; /**< PCM buffer. */
121
static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
122
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
123
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
124
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
125
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
127
pjmedia_aud_dev_info *info);
128
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
130
pjmedia_aud_param *param);
131
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
132
const pjmedia_aud_param *param,
133
pjmedia_aud_rec_cb rec_cb,
134
pjmedia_aud_play_cb play_cb,
136
pjmedia_aud_stream **p_aud_strm);
138
static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
139
pjmedia_aud_param *param);
140
static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
141
pjmedia_aud_dev_cap cap,
143
static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
144
pjmedia_aud_dev_cap cap,
146
static pj_status_t stream_start(pjmedia_aud_stream *strm);
147
static pj_status_t stream_stop(pjmedia_aud_stream *strm);
148
static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
152
static pjmedia_aud_dev_factory_op factory_op =
156
&factory_get_dev_count,
157
&factory_get_dev_info,
158
&factory_default_param,
159
&factory_create_stream,
163
static pjmedia_aud_stream_op stream_op =
174
/****************************************************************************
175
* Internal APS Engine
179
* Utility: print sound device error
181
static void snd_perror(const char *title, TInt rc)
183
PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
187
* Utility: wait for specified time.
189
static void snd_wait(unsigned ms)
193
start.UniversalTime();
195
pj_symbianos_poll(-1, ms);
197
} while (now.MicroSecondsFrom(start) < ms * 1000);
200
typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data);
203
* Abstract class for handler of callbacks from APS client.
205
class MQueueHandlerObserver
208
MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_,
210
: RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_)
213
virtual void InputStreamInitialized(const TInt aStatus) = 0;
214
virtual void OutputStreamInitialized(const TInt aStatus) = 0;
215
virtual void NotifyError(const TInt aError) = 0;
218
PjAudioCallback RecCb;
219
PjAudioCallback PlayCb;
224
* Handler for communication and data queue.
226
class CQueueHandler : public CActive
229
// Types of queue handler
230
enum TQueueHandlerType {
237
// The order corresponds to the APS Server state, do not change!
239
EAPSPlayerInitialize = 1,
240
EAPSRecorderInitialize = 2,
243
EAPSPlayerInitComplete = 5,
244
EAPSRecorderInitComplete = 6
247
static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
248
RMsgQueue<TAPSCommBuffer>* aQ,
249
RMsgQueue<TAPSCommBuffer>* aWriteQ,
250
TQueueHandlerType aType)
252
CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ,
254
CleanupStack::PushL(self);
256
CleanupStack::Pop(self);
261
~CQueueHandler() { Cancel(); }
263
// Start listening queue event
265
iQ->NotifyDataAvailable(iStatus);
271
CQueueHandler(MQueueHandlerObserver* aObserver,
272
RMsgQueue<TAPSCommBuffer>* aQ,
273
RMsgQueue<TAPSCommBuffer>* aWriteQ,
274
TQueueHandlerType aType)
275
: CActive(CActive::EPriorityHigh),
276
iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
278
CActiveScheduler::Add(this);
280
// use lower priority for comm queues
281
if ((iType == ERecordCommQueue) || (iType == EPlayCommQueue))
282
SetPriority(CActive::EPriorityStandard);
285
// Second phase constructor
288
// Inherited from CActive
289
void DoCancel() { iQ->CancelDataAvailable(); }
292
if (iStatus != KErrNone) {
293
iObserver->NotifyError(iStatus.Int());
297
TAPSCommBuffer buffer;
298
TInt ret = iQ->Receive(buffer);
300
if (ret != KErrNone) {
301
iObserver->NotifyError(ret);
307
if (buffer.iCommand == EAPSRecordData) {
308
iObserver->RecCb(buffer, iObserver->UserData);
310
iObserver->NotifyError(buffer.iStatus);
314
// Callbacks from the APS main thread
316
switch (buffer.iCommand) {
318
if (buffer.iStatus == KErrUnderflow) {
319
iObserver->PlayCb(buffer, iObserver->UserData);
320
iWriteQ->Send(buffer);
323
case EAPSPlayerInitialize:
324
iObserver->NotifyError(buffer.iStatus);
326
case EAPSPlayerInitComplete:
327
iObserver->OutputStreamInitialized(buffer.iStatus);
329
case EAPSRecorderInitComplete:
330
iObserver->InputStreamInitialized(buffer.iStatus);
333
iObserver->NotifyError(buffer.iStatus);
338
// Callbacks from the APS recorder thread
339
case ERecordCommQueue:
340
switch (buffer.iCommand) {
341
// The APS recorder thread will only report errors
342
// through this handler. All other callbacks will be
343
// sent from the APS main thread through EPlayCommQueue
344
case EAPSRecorderInitialize:
347
iObserver->NotifyError(buffer.iStatus);
356
// issue next request
357
iQ->NotifyDataAvailable(iStatus);
361
TInt RunError(TInt) {
366
RMsgQueue<TAPSCommBuffer> *iQ; // (not owned)
367
RMsgQueue<TAPSCommBuffer> *iWriteQ; // (not owned)
368
MQueueHandlerObserver *iObserver; // (not owned)
369
TQueueHandlerType iType;
373
* Audio setting for CPjAudioEngine.
375
class CPjAudioSetting
387
* Implementation: Symbian Input & Output Stream.
389
class CPjAudioEngine : public CBase, MQueueHandlerObserver
403
static CPjAudioEngine *NewL(struct aps_stream *parent_strm,
404
PjAudioCallback rec_cb,
405
PjAudioCallback play_cb,
407
const CPjAudioSetting &setting);
412
TInt ActivateSpeaker(TBool active);
414
TInt SetVolume(TInt vol) { return iSession.SetVolume(vol); }
415
TInt GetVolume() { return iSession.Volume(); }
416
TInt GetMaxVolume() { return iSession.MaxVolume(); }
418
TInt SetGain(TInt gain) { return iSession.SetGain(gain); }
419
TInt GetGain() { return iSession.Gain(); }
420
TInt GetMaxGain() { return iSession.MaxGain(); }
423
CPjAudioEngine(struct aps_stream *parent_strm,
424
PjAudioCallback rec_cb,
425
PjAudioCallback play_cb,
427
const CPjAudioSetting &setting);
435
// Inherited from MQueueHandlerObserver
436
virtual void InputStreamInitialized(const TInt aStatus);
437
virtual void OutputStreamInitialized(const TInt aStatus);
438
virtual void NotifyError(const TInt aError);
440
TBool session_opened;
442
struct aps_stream *parentStrm_;
443
CPjAudioSetting setting_;
445
RAPSSession iSession;
446
TAPSInitSettings iPlaySettings;
447
TAPSInitSettings iRecSettings;
449
RMsgQueue<TAPSCommBuffer> iReadQ;
450
RMsgQueue<TAPSCommBuffer> iReadCommQ;
452
RMsgQueue<TAPSCommBuffer> iWriteQ;
453
RMsgQueue<TAPSCommBuffer> iWriteCommQ;
456
CQueueHandler *iPlayCommHandler;
457
CQueueHandler *iRecCommHandler;
458
CQueueHandler *iRecHandler;
462
CPjAudioEngine* CPjAudioEngine::NewL(struct aps_stream *parent_strm,
463
PjAudioCallback rec_cb,
464
PjAudioCallback play_cb,
466
const CPjAudioSetting &setting)
468
CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
472
CleanupStack::PushL(self);
474
CleanupStack::Pop(self);
478
CPjAudioEngine::CPjAudioEngine(struct aps_stream *parent_strm,
479
PjAudioCallback rec_cb,
480
PjAudioCallback play_cb,
482
const CPjAudioSetting &setting)
483
: MQueueHandlerObserver(rec_cb, play_cb, user_data),
484
session_opened(EFalse),
486
parentStrm_(parent_strm),
488
readq_opened(EFalse),
489
writeq_opened(EFalse),
496
CPjAudioEngine::~CPjAudioEngine()
500
TRACE_((THIS_FILE, "Sound device destroyed"));
503
TInt CPjAudioEngine::InitPlayL()
505
TInt err = iSession.InitializePlayer(iPlaySettings);
506
if (err != KErrNone) {
508
snd_perror("Failed to initialize player", err);
512
// Open message queues for the output stream
513
TBuf<128> buf2 = iPlaySettings.iGlobal;
514
buf2.Append(_L("PlayQueue"));
515
TBuf<128> buf3 = iPlaySettings.iGlobal;
516
buf3.Append(_L("PlayCommQueue"));
518
while (iWriteQ.OpenGlobal(buf2))
520
while (iWriteCommQ.OpenGlobal(buf3))
523
writeq_opened = ETrue;
525
// Construct message queue handler
526
iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
527
CQueueHandler::EPlayCommQueue);
529
// Start observing APS callbacks on output stream message queue
530
iPlayCommHandler->Start();
535
TInt CPjAudioEngine::InitRecL()
537
// Initialize input stream device
538
TInt err = iSession.InitializeRecorder(iRecSettings);
539
if (err != KErrNone && err != KErrAlreadyExists) {
541
snd_perror("Failed to initialize recorder", err);
545
TBuf<128> buf1 = iRecSettings.iGlobal;
546
buf1.Append(_L("RecordQueue"));
547
TBuf<128> buf4 = iRecSettings.iGlobal;
548
buf4.Append(_L("RecordCommQueue"));
550
// Must wait for APS thread to finish creating message queues
551
// before we can open and use them.
552
while (iReadQ.OpenGlobal(buf1))
554
while (iReadCommQ.OpenGlobal(buf4))
557
readq_opened = ETrue;
559
// Construct message queue handlers
560
iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL,
561
CQueueHandler::ERecordQueue);
562
iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL,
563
CQueueHandler::ERecordCommQueue);
565
// Start observing APS callbacks from on input stream message queue
566
iRecHandler->Start();
567
iRecCommHandler->Start();
572
TInt CPjAudioEngine::StartL()
574
if (state_ == STATE_READY)
575
return StartStreamL();
577
PJ_ASSERT_RETURN(state_ == STATE_NULL, PJMEDIA_EAUD_INVOP);
579
if (!session_opened) {
580
TInt err = iSession.Connect();
583
session_opened = ETrue;
586
// Even if only capturer are opened, playback thread of APS Server need
587
// to be run(?). Since some messages will be delivered via play comm queue.
588
state_ = STATE_INITIALIZING;
593
void CPjAudioEngine::Stop()
595
if (state_ == STATE_STREAMING) {
597
state_ = STATE_READY;
598
TRACE_((THIS_FILE, "Sound device stopped"));
599
} else if (state_ == STATE_INITIALIZING) {
600
// Initialization is on progress, so let's set the state to
601
// STATE_PENDING_STOP to prevent it starting the stream.
602
state_ = STATE_PENDING_STOP;
604
// Then wait until initialization done.
605
while (state_ != STATE_READY && state_ != STATE_NULL)
606
pj_symbianos_poll(-1, 100);
610
void CPjAudioEngine::ConstructL()
613
iRecSettings.iFourCC = setting_.fourcc;
614
iRecSettings.iGlobal = APP_UID;
615
iRecSettings.iPriority = TMdaPriority(100);
616
iRecSettings.iPreference = TMdaPriorityPreference(0x05210001);
617
iRecSettings.iSettings.iChannels = EMMFMono;
618
iRecSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
621
iPlaySettings.iFourCC = setting_.fourcc;
622
iPlaySettings.iGlobal = APP_UID;
623
iPlaySettings.iPriority = TMdaPriority(100);
624
iPlaySettings.iPreference = TMdaPriorityPreference(0x05220001);
625
iPlaySettings.iSettings.iChannels = EMMFMono;
626
iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
627
iPlaySettings.iSettings.iVolume = 0;
629
User::LeaveIfError(iSession.Connect());
630
session_opened = ETrue;
633
TInt CPjAudioEngine::StartStreamL()
635
pj_assert(state_==STATE_READY || state_==STATE_INITIALIZING);
637
iSession.SetCng(setting_.cng);
638
iSession.SetVadMode(setting_.vad);
639
iSession.SetPlc(setting_.plc);
640
iSession.SetEncoderMode(setting_.mode);
641
iSession.SetDecoderMode(setting_.mode);
642
iSession.ActivateLoudspeaker(setting_.loudspk);
645
if (parentStrm_->param.dir != PJMEDIA_DIR_CAPTURE) {
647
TRACE_((THIS_FILE, "Player started"));
651
if (parentStrm_->param.dir != PJMEDIA_DIR_PLAYBACK) {
653
TRACE_((THIS_FILE, "Recorder started"));
656
state_ = STATE_STREAMING;
661
void CPjAudioEngine::Deinit()
666
delete iPlayCommHandler;
667
delete iRecCommHandler;
669
if (session_opened) {
670
enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
672
// On some devices, immediate closing after stopping may cause
673
// APS server panic KERN-EXEC 0, so let's wait for sometime before
674
// closing the client session.
675
snd_wait(APS_CLOSE_WAIT_TIME);
678
session_opened = EFalse;
684
readq_opened = EFalse;
690
writeq_opened = EFalse;
696
void CPjAudioEngine::InputStreamInitialized(const TInt aStatus)
698
TRACE_((THIS_FILE, "Recorder initialized, err=%d", aStatus));
700
if (aStatus == KErrNone) {
701
// Don't start the stream since Stop() has been requested.
702
if (state_ != STATE_PENDING_STOP) {
705
state_ = STATE_READY;
712
void CPjAudioEngine::OutputStreamInitialized(const TInt aStatus)
714
TRACE_((THIS_FILE, "Player initialized, err=%d", aStatus));
716
if (aStatus == KErrNone) {
717
if (parentStrm_->param.dir == PJMEDIA_DIR_PLAYBACK) {
718
// Don't start the stream since Stop() has been requested.
719
if (state_ != STATE_PENDING_STOP) {
722
state_ = STATE_READY;
731
void CPjAudioEngine::NotifyError(const TInt aError)
734
snd_perror("Error from CQueueHandler", aError);
737
TInt CPjAudioEngine::ActivateSpeaker(TBool active)
739
if (state_ == STATE_READY || state_ == STATE_STREAMING) {
740
iSession.ActivateLoudspeaker(active);
741
TRACE_((THIS_FILE, "Loudspeaker turned %s", (active? "on":"off")));
747
/****************************************************************************
748
* Internal APS callbacks for PCM format
751
static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
753
struct aps_stream *strm = (struct aps_stream*) user_data;
755
/* Buffer has to contain normal speech. */
756
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
758
/* Detect the recorder G.711 frame size, player frame size will follow
759
* this recorder frame size.
761
if (aps_g711_frame_len == 0) {
762
aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
763
TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
764
aps_g711_frame_len));
767
/* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
768
* Whenever rec_buf is full, call parent stream callback.
770
unsigned samples_processed = 0;
772
while (samples_processed < aps_g711_frame_len) {
773
unsigned samples_to_process;
774
unsigned samples_req;
776
samples_to_process = aps_g711_frame_len - samples_processed;
777
samples_req = (strm->param.samples_per_frame /
778
strm->param.channel_count /
779
strm->resample_factor) -
781
if (samples_to_process > samples_req)
782
samples_to_process = samples_req;
784
pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
785
buf.iBuffer.Ptr() + 2 + samples_processed,
788
strm->rec_buf_len += samples_to_process;
789
samples_processed += samples_to_process;
791
/* Buffer is full, time to call parent callback */
792
if (strm->rec_buf_len == strm->param.samples_per_frame /
793
strm->param.channel_count /
794
strm->resample_factor)
798
/* Need to resample clock rate? */
799
if (strm->rec_resample) {
800
unsigned resampled = 0;
802
while (resampled < strm->rec_buf_len) {
803
pjmedia_resample_run(strm->rec_resample,
804
&strm->rec_buf[resampled],
806
resampled * strm->resample_factor);
809
f.buf = strm->pcm_buf;
811
f.buf = strm->rec_buf;
814
/* Need to convert channel count? */
815
if (strm->param.channel_count != 1) {
816
pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
818
strm->param.channel_count,
819
strm->param.samples_per_frame /
820
strm->param.channel_count,
824
/* Call parent callback */
825
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
826
f.size = strm->param.samples_per_frame << 1;
827
strm->rec_cb(strm->user_data, &f);
828
strm->rec_buf_len = 0;
833
static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
835
struct aps_stream *strm = (struct aps_stream*) user_data;
836
unsigned g711_frame_len = aps_g711_frame_len;
838
/* Init buffer attributes and header. */
839
buf.iCommand = CQueueHandler::EAPSPlayData;
842
buf.iBuffer.Append(1);
843
buf.iBuffer.Append(0);
845
/* Assume frame size is 10ms if frame size hasn't been known. */
846
if (g711_frame_len == 0)
849
/* Call parent stream callback to get PCM samples to play,
850
* encode the PCM samples into G.711 and put it into APS buffer.
852
unsigned samples_processed = 0;
854
while (samples_processed < g711_frame_len) {
855
/* Need more samples to play, time to call parent callback */
856
if (strm->play_buf_len == 0) {
858
unsigned samples_got;
860
f.size = strm->param.samples_per_frame << 1;
861
if (strm->play_resample || strm->param.channel_count != 1)
862
f.buf = strm->pcm_buf;
864
f.buf = strm->play_buf;
866
/* Call parent callback */
867
strm->play_cb(strm->user_data, &f);
868
if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
869
pjmedia_zero_samples((pj_int16_t*)f.buf,
870
strm->param.samples_per_frame);
873
samples_got = strm->param.samples_per_frame /
874
strm->param.channel_count /
875
strm->resample_factor;
877
/* Need to convert channel count? */
878
if (strm->param.channel_count != 1) {
879
pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
881
strm->param.channel_count,
882
strm->param.samples_per_frame,
887
/* Need to resample clock rate? */
888
if (strm->play_resample) {
889
unsigned resampled = 0;
891
while (resampled < samples_got)
893
pjmedia_resample_run(strm->play_resample,
895
resampled * strm->resample_factor,
896
&strm->play_buf[resampled]);
901
strm->play_buf_len = samples_got;
902
strm->play_buf_start = 0;
907
tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
908
pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
909
&strm->play_buf[strm->play_buf_start],
911
buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
912
samples_processed += tmp;
913
strm->play_buf_len -= tmp;
914
strm->play_buf_start += tmp;
918
/****************************************************************************
919
* Internal APS callbacks for non-PCM format
922
static void RecCb(TAPSCommBuffer &buf, void *user_data)
924
struct aps_stream *strm = (struct aps_stream*) user_data;
925
pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
927
switch(strm->param.ext_fmt.id) {
928
case PJMEDIA_FORMAT_AMR:
930
const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 1;
931
unsigned len = buf.iBuffer.Length() - 1;
933
pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160);
934
if (frame->samples_cnt == strm->param.samples_per_frame) {
935
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
936
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
937
frame->samples_cnt = 0;
938
frame->subframe_cnt = 0;
943
case PJMEDIA_FORMAT_G729:
945
/* Check if we got a normal or SID frame. */
946
if (buf.iBuffer[0] != 0 || buf.iBuffer[1] != 0) {
947
enum { NORMAL_LEN = 22, SID_LEN = 8 };
948
TBitStream *bitstream = (TBitStream*)strm->strm_data;
949
unsigned src_len = buf.iBuffer.Length()- 2;
951
pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
953
const TDesC8& p = bitstream->CompressG729Frame(
954
buf.iBuffer.Right(src_len),
957
pjmedia_frame_ext_append_subframe(frame, p.Ptr(),
958
p.Length() << 3, 80);
959
} else { /* We got null frame. */
960
pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80);
963
if (frame->samples_cnt == strm->param.samples_per_frame) {
964
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
965
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
966
frame->samples_cnt = 0;
967
frame->subframe_cnt = 0;
972
case PJMEDIA_FORMAT_ILBC:
974
unsigned samples_got;
976
samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
978
/* Check if we got a normal frame. */
979
if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
980
const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2;
981
unsigned len = buf.iBuffer.Length() - 2;
983
pjmedia_frame_ext_append_subframe(frame, p, len << 3,
985
} else { /* We got null frame. */
986
pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
989
if (frame->samples_cnt == strm->param.samples_per_frame) {
990
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
991
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
992
frame->samples_cnt = 0;
993
frame->subframe_cnt = 0;
998
case PJMEDIA_FORMAT_PCMU:
999
case PJMEDIA_FORMAT_PCMA:
1001
unsigned samples_processed = 0;
1003
/* Make sure it is normal frame. */
1004
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
1006
/* Detect the recorder G.711 frame size, player frame size will
1007
* follow this recorder frame size.
1009
if (aps_g711_frame_len == 0) {
1010
aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
1011
TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
1012
aps_g711_frame_len));
1015
/* Convert APS buffer format into pjmedia_frame_ext. Whenever
1016
* samples count in the frame is equal to stream's samples per
1017
* frame, call parent stream callback.
1019
while (samples_processed < aps_g711_frame_len) {
1021
const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() +
1022
2 + samples_processed;
1024
tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
1025
aps_g711_frame_len - samples_processed);
1027
pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
1028
samples_processed += tmp;
1030
if (frame->samples_cnt == strm->param.samples_per_frame) {
1031
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1032
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1033
frame->samples_cnt = 0;
1034
frame->subframe_cnt = 0;
1045
static void PlayCb(TAPSCommBuffer &buf, void *user_data)
1047
struct aps_stream *strm = (struct aps_stream*) user_data;
1048
pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
1050
/* Init buffer attributes and header. */
1051
buf.iCommand = CQueueHandler::EAPSPlayData;
1055
switch(strm->param.ext_fmt.id) {
1056
case PJMEDIA_FORMAT_AMR:
1058
if (frame->samples_cnt == 0) {
1059
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1060
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1061
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1062
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1065
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1066
pjmedia_frame_ext_subframe *sf;
1067
unsigned samples_cnt;
1069
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1070
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1072
if (sf->data && sf->bitlen) {
1073
/* AMR header for APS is one byte, the format (may be!):
1074
* 0xxxxy00, where xxxx:frame type, y:not sure.
1076
unsigned len = (sf->bitlen+7)>>3;
1078
pj_uint8_t amr_header = 4, ft = SID_FT;
1080
if (len >= pjmedia_codec_amrnb_framelen[0])
1081
ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
1083
amr_header |= ft << 3;
1084
buf.iBuffer.Append(amr_header);
1086
buf.iBuffer.Append((TUint8*)sf->data, len);
1088
enum {NO_DATA_FT = 15 };
1089
pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1091
buf.iBuffer.Append(amr_header);
1094
pjmedia_frame_ext_pop_subframes(frame, 1);
1096
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1097
enum {NO_DATA_FT = 15 };
1098
pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1100
buf.iBuffer.Append(amr_header);
1102
frame->samples_cnt = 0;
1103
frame->subframe_cnt = 0;
1108
case PJMEDIA_FORMAT_G729:
1110
if (frame->samples_cnt == 0) {
1111
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1112
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1113
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1114
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1117
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1118
pjmedia_frame_ext_subframe *sf;
1119
unsigned samples_cnt;
1121
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1122
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1124
if (sf->data && sf->bitlen) {
1125
enum { NORMAL_LEN = 10, SID_LEN = 2 };
1126
pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
1127
TBitStream *bitstream = (TBitStream*)strm->strm_data;
1128
const TPtrC8 src(sf->data, sf->bitlen>>3);
1129
const TDesC8 &dst = bitstream->ExpandG729Frame(src,
1132
buf.iBuffer.Append(2);
1133
buf.iBuffer.Append(0);
1135
buf.iBuffer.Append(1);
1136
buf.iBuffer.Append(0);
1138
buf.iBuffer.Append(dst);
1140
buf.iBuffer.Append(2);
1141
buf.iBuffer.Append(0);
1142
buf.iBuffer.AppendFill(0, 22);
1145
pjmedia_frame_ext_pop_subframes(frame, 1);
1147
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1148
buf.iBuffer.Append(2);
1149
buf.iBuffer.Append(0);
1150
buf.iBuffer.AppendFill(0, 22);
1152
frame->samples_cnt = 0;
1153
frame->subframe_cnt = 0;
1158
case PJMEDIA_FORMAT_ILBC:
1160
if (frame->samples_cnt == 0) {
1161
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1162
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1163
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1164
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1167
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1168
pjmedia_frame_ext_subframe *sf;
1169
unsigned samples_cnt;
1171
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1172
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1174
pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
1175
samples_cnt == 160) ||
1176
(strm->param.ext_fmt.bitrate != 15200 &&
1177
samples_cnt == 240));
1179
if (sf->data && sf->bitlen) {
1180
buf.iBuffer.Append(1);
1181
buf.iBuffer.Append(0);
1182
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1184
buf.iBuffer.Append(0);
1185
buf.iBuffer.Append(0);
1188
pjmedia_frame_ext_pop_subframes(frame, 1);
1190
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1191
buf.iBuffer.Append(0);
1192
buf.iBuffer.Append(0);
1194
frame->samples_cnt = 0;
1195
frame->subframe_cnt = 0;
1200
case PJMEDIA_FORMAT_PCMU:
1201
case PJMEDIA_FORMAT_PCMA:
1203
unsigned samples_ready = 0;
1204
unsigned samples_req = aps_g711_frame_len;
1206
/* Assume frame size is 10ms if frame size hasn't been known. */
1207
if (samples_req == 0)
1210
buf.iBuffer.Append(1);
1211
buf.iBuffer.Append(0);
1213
/* Call parent stream callback to get samples to play. */
1214
while (samples_ready < samples_req) {
1215
if (frame->samples_cnt == 0) {
1216
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1217
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1218
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1219
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1222
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1223
pjmedia_frame_ext_subframe *sf;
1224
unsigned samples_cnt;
1226
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1227
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1228
if (sf->data && sf->bitlen) {
1229
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1232
silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1233
pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1234
buf.iBuffer.AppendFill(silc, samples_cnt);
1236
samples_ready += samples_cnt;
1238
pjmedia_frame_ext_pop_subframes(frame, 1);
1240
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1243
silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1244
pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1245
buf.iBuffer.AppendFill(silc, samples_req - samples_ready);
1247
samples_ready = samples_req;
1248
frame->samples_cnt = 0;
1249
frame->subframe_cnt = 0;
1261
/****************************************************************************
1262
* Factory operations
1266
* C compatible declaration of APS factory.
1269
PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf);
1273
* Init APS audio driver.
1275
PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf)
1277
struct aps_factory *f;
1280
pool = pj_pool_create(pf, "APS", 1000, 1000, NULL);
1281
f = PJ_POOL_ZALLOC_T(pool, struct aps_factory);
1284
f->base.op = &factory_op;
1289
/* API: init factory */
1290
static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
1292
struct aps_factory *af = (struct aps_factory*)f;
1294
pj_ansi_strcpy(af->dev_info.name, "S60 APS");
1295
af->dev_info.default_samples_per_sec = 8000;
1296
af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
1297
//PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
1298
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
1299
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
1300
PJMEDIA_AUD_DEV_CAP_VAD |
1301
PJMEDIA_AUD_DEV_CAP_CNG;
1302
af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
1303
PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1304
af->dev_info.input_count = 1;
1305
af->dev_info.output_count = 1;
1307
/* Enumerate codecs by trying to initialize each codec and examining
1308
* the error code. Consider the following:
1309
* - not possible to reinitialize the same APS session with
1310
* different settings,
1311
* - closing APS session and trying to immediately reconnect may fail,
1312
* clients should wait ~5s before attempting to reconnect.
1315
unsigned i, fmt_cnt = 0;
1316
pj_bool_t g711_supported = PJ_FALSE;
1318
/* Do not change the order! */
1319
TFourCC fourcc[] = {
1320
TFourCC(KMCPFourCCIdAMRNB),
1321
TFourCC(KMCPFourCCIdG711),
1322
TFourCC(KMCPFourCCIdG729),
1323
TFourCC(KMCPFourCCIdILBC)
1326
for (i = 0; i < PJ_ARRAY_SIZE(fourcc); ++i) {
1327
pj_bool_t supported = PJ_FALSE;
1328
unsigned retry_cnt = 0;
1329
enum { MAX_RETRY = 3 };
1331
#if (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 0)
1332
/* Codec detection is disabled */
1333
supported = PJ_TRUE;
1334
#elif (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 1)
1335
/* Minimal codec detection, AMR-NB and G.711 only */
1337
/* If G.711 has been checked, skip G.729 and iLBC checks */
1338
retry_cnt = MAX_RETRY;
1339
supported = g711_supported;
1343
while (!supported && ++retry_cnt <= MAX_RETRY) {
1344
RAPSSession iSession;
1345
TAPSInitSettings iPlaySettings;
1346
TAPSInitSettings iRecSettings;
1349
// Recorder settings
1350
iRecSettings.iGlobal = APP_UID;
1351
iRecSettings.iPriority = TMdaPriority(100);
1352
iRecSettings.iPreference = TMdaPriorityPreference(0x05210001);
1353
iRecSettings.iSettings.iChannels = EMMFMono;
1354
iRecSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
1357
iPlaySettings.iGlobal = APP_UID;
1358
iPlaySettings.iPriority = TMdaPriority(100);
1359
iPlaySettings.iPreference = TMdaPriorityPreference(0x05220001);
1360
iPlaySettings.iSettings.iChannels = EMMFMono;
1361
iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
1363
iRecSettings.iFourCC = iPlaySettings.iFourCC = fourcc[i];
1365
err = iSession.Connect();
1366
if (err == KErrNone)
1367
err = iSession.InitializePlayer(iPlaySettings);
1368
if (err == KErrNone)
1369
err = iSession.InitializeRecorder(iRecSettings);
1371
// On some devices, immediate closing causes APS Server panic,
1372
// e.g: N95, so let's just wait for some time before closing.
1373
enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
1374
snd_wait(APS_CLOSE_WAIT_TIME);
1378
if (err == KErrNone) {
1379
/* All fine, stop retyring */
1380
supported = PJ_TRUE;
1381
} else if (err == KErrAlreadyExists && retry_cnt < MAX_RETRY) {
1382
/* Seems that the previous session is still arround,
1383
* let's wait before retrying.
1385
enum { RETRY_WAIT_TIME = 3000 }; /* in msecs */
1386
snd_wait(RETRY_WAIT_TIME);
1388
/* Seems that this format is not supported */
1389
retry_cnt = MAX_RETRY;
1396
af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_AMR;
1397
af->dev_info.ext_fmt[fmt_cnt].bitrate = 7400;
1398
af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1402
af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
1403
af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
1404
af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1406
af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
1407
af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
1408
af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1410
g711_supported = PJ_TRUE;
1413
af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_G729;
1414
af->dev_info.ext_fmt[fmt_cnt].bitrate = 8000;
1415
af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1419
af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
1420
af->dev_info.ext_fmt[fmt_cnt].bitrate = 13333;
1421
af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1428
af->dev_info.ext_fmt_cnt = fmt_cnt;
1430
PJ_LOG(4, (THIS_FILE, "APS initialized"));
1435
/* API: destroy factory */
1436
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
1438
struct aps_factory *af = (struct aps_factory*)f;
1439
pj_pool_t *pool = af->pool;
1442
pj_pool_release(pool);
1444
PJ_LOG(4, (THIS_FILE, "APS destroyed"));
1449
/* API: refresh the device list */
1450
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
1456
/* API: get number of devices */
1457
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
1463
/* API: get device info */
1464
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
1466
pjmedia_aud_dev_info *info)
1468
struct aps_factory *af = (struct aps_factory*)f;
1470
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1472
pj_memcpy(info, &af->dev_info, sizeof(*info));
1477
/* API: create default device parameter */
1478
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
1480
pjmedia_aud_param *param)
1482
struct aps_factory *af = (struct aps_factory*)f;
1484
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1486
pj_bzero(param, sizeof(*param));
1487
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1488
param->rec_id = index;
1489
param->play_id = index;
1490
param->clock_rate = af->dev_info.default_samples_per_sec;
1491
param->channel_count = 1;
1492
param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
1493
param->bits_per_sample = BITS_PER_SAMPLE;
1494
param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1495
param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1501
/* API: create stream */
1502
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1503
const pjmedia_aud_param *param,
1504
pjmedia_aud_rec_cb rec_cb,
1505
pjmedia_aud_play_cb play_cb,
1507
pjmedia_aud_stream **p_aud_strm)
1509
struct aps_factory *af = (struct aps_factory*)f;
1511
struct aps_stream *strm;
1513
CPjAudioSetting aps_setting;
1514
PjAudioCallback aps_rec_cb;
1515
PjAudioCallback aps_play_cb;
1517
/* Can only support 16bits per sample */
1518
PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
1520
/* Supported clock rates:
1521
* - for non-PCM format: 8kHz
1522
* - for PCM format: 8kHz and 16kHz
1524
PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
1525
(param->clock_rate == 16000 &&
1526
param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1529
/* Supported channels number:
1530
* - for non-PCM format: mono
1531
* - for PCM format: mono and stereo
1533
PJ_ASSERT_RETURN(param->channel_count == 1 ||
1534
(param->channel_count == 2 &&
1535
param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1538
/* Create and Initialize stream descriptor */
1539
pool = pj_pool_create(af->pf, "aps-dev", 1000, 1000, NULL);
1540
PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
1542
strm = PJ_POOL_ZALLOC_T(pool, struct aps_stream);
1544
strm->param = *param;
1546
if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
1547
strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
1549
/* Set audio engine fourcc. */
1550
switch(strm->param.ext_fmt.id) {
1551
case PJMEDIA_FORMAT_L16:
1552
case PJMEDIA_FORMAT_PCMU:
1553
case PJMEDIA_FORMAT_PCMA:
1554
aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
1556
case PJMEDIA_FORMAT_AMR:
1557
aps_setting.fourcc = TFourCC(KMCPFourCCIdAMRNB);
1559
case PJMEDIA_FORMAT_G729:
1560
aps_setting.fourcc = TFourCC(KMCPFourCCIdG729);
1562
case PJMEDIA_FORMAT_ILBC:
1563
aps_setting.fourcc = TFourCC(KMCPFourCCIdILBC);
1566
aps_setting.fourcc = 0;
1570
/* Set audio engine mode. */
1571
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
1573
aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.bitrate;
1575
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1576
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1577
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
1578
strm->param.ext_fmt.bitrate != 15200))
1580
aps_setting.mode = EULawOr30ms;
1582
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1583
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
1584
strm->param.ext_fmt.bitrate == 15200))
1586
aps_setting.mode = EALawOr20ms;
1589
/* Disable VAD on L16, G711, and also G729 (G729's VAD potentially
1592
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1593
strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1594
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1595
strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
1597
aps_setting.vad = EFalse;
1599
aps_setting.vad = strm->param.ext_fmt.vad;
1602
/* Set other audio engine attributes. */
1603
aps_setting.plc = strm->param.plc_enabled;
1604
aps_setting.cng = aps_setting.vad;
1605
aps_setting.loudspk =
1606
strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1608
/* Set audio engine callbacks. */
1609
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
1610
aps_play_cb = &PlayCbPcm;
1611
aps_rec_cb = &RecCbPcm;
1613
aps_play_cb = &PlayCb;
1614
aps_rec_cb = &RecCb;
1617
strm->rec_cb = rec_cb;
1618
strm->play_cb = play_cb;
1619
strm->user_data = user_data;
1620
strm->resample_factor = strm->param.clock_rate / 8000;
1622
/* play_buf size is samples per frame scaled in to 8kHz mono. */
1623
strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
1625
(strm->param.samples_per_frame /
1626
strm->resample_factor /
1627
strm->param.channel_count) << 1);
1628
strm->play_buf_len = 0;
1629
strm->play_buf_start = 0;
1631
/* rec_buf size is samples per frame scaled in to 8kHz mono. */
1632
strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(
1634
(strm->param.samples_per_frame /
1635
strm->resample_factor /
1636
strm->param.channel_count) << 1);
1637
strm->rec_buf_len = 0;
1639
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1640
TBitStream *g729_bitstream = new TBitStream;
1642
PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
1643
strm->strm_data = (void*)g729_bitstream;
1646
/* Init resampler when format is PCM and clock rate is not 8kHz */
1647
if (strm->param.clock_rate != 8000 &&
1648
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1652
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1653
/* Create resample for recorder */
1654
status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1656
strm->param.clock_rate,
1658
&strm->rec_resample);
1659
if (status != PJ_SUCCESS)
1663
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1664
/* Create resample for player */
1665
status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1666
strm->param.clock_rate,
1668
80 * strm->resample_factor,
1669
&strm->play_resample);
1670
if (status != PJ_SUCCESS)
1675
/* Create PCM buffer, when the clock rate is not 8kHz or not mono */
1676
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
1677
(strm->resample_factor > 1 || strm->param.channel_count != 1))
1679
strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
1680
strm->param.samples_per_frame << 1);
1684
/* Create the audio engine. */
1685
TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
1686
aps_rec_cb, aps_play_cb,
1687
strm, aps_setting));
1688
if (err != KErrNone) {
1689
pj_pool_release(pool);
1690
return PJ_RETURN_OS_ERROR(err);
1693
/* Apply output volume setting if specified */
1694
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1695
stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1696
¶m->output_vol);
1700
strm->base.op = &stream_op;
1701
*p_aud_strm = &strm->base;
1706
/* API: Get stream info. */
1707
static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1708
pjmedia_aud_param *pi)
1710
struct aps_stream *strm = (struct aps_stream*)s;
1712
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1714
pj_memcpy(pi, &strm->param, sizeof(*pi));
1716
/* Update the output volume setting */
1717
if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1718
&pi->output_vol) == PJ_SUCCESS)
1720
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1726
/* API: get capability */
1727
static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1728
pjmedia_aud_dev_cap cap,
1731
struct aps_stream *strm = (struct aps_stream*)s;
1732
pj_status_t status = PJ_ENOTSUP;
1734
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1737
case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1738
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1739
*(pjmedia_aud_dev_route*)pval = strm->param.output_route;
1740
status = PJ_SUCCESS;
1744
/* There is a case that GetMaxGain() stucks, e.g: in N95. */
1746
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1747
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1748
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1750
TInt max_gain = strm->engine->GetMaxGain();
1751
TInt gain = strm->engine->GetGain();
1753
if (max_gain > 0 && gain >= 0) {
1754
*(unsigned*)pval = gain * 100 / max_gain;
1755
status = PJ_SUCCESS;
1757
status = PJMEDIA_EAUD_NOTREADY;
1763
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1764
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1765
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1767
TInt max_vol = strm->engine->GetMaxVolume();
1768
TInt vol = strm->engine->GetVolume();
1770
if (max_vol > 0 && vol >= 0) {
1771
*(unsigned*)pval = vol * 100 / max_vol;
1772
status = PJ_SUCCESS;
1774
status = PJMEDIA_EAUD_NOTREADY;
1785
/* API: set capability */
1786
static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1787
pjmedia_aud_dev_cap cap,
1790
struct aps_stream *strm = (struct aps_stream*)s;
1791
pj_status_t status = PJ_ENOTSUP;
1793
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1796
case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1797
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1798
pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
1801
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1804
case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
1805
case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
1806
err = strm->engine->ActivateSpeaker(EFalse);
1807
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1809
case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
1810
err = strm->engine->ActivateSpeaker(ETrue);
1811
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1817
if (status == PJ_SUCCESS)
1818
strm->param.output_route = r;
1822
/* There is a case that GetMaxGain() stucks, e.g: in N95. */
1824
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1825
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1826
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1828
TInt max_gain = strm->engine->GetMaxGain();
1832
gain = *(unsigned*)pval * max_gain / 100;
1833
err = strm->engine->SetGain(gain);
1834
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1836
status = PJMEDIA_EAUD_NOTREADY;
1838
if (status == PJ_SUCCESS)
1839
strm->param.input_vol = *(unsigned*)pval;
1844
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1845
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1846
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1848
TInt max_vol = strm->engine->GetMaxVolume();
1852
vol = *(unsigned*)pval * max_vol / 100;
1853
err = strm->engine->SetVolume(vol);
1854
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1856
status = PJMEDIA_EAUD_NOTREADY;
1858
if (status == PJ_SUCCESS)
1859
strm->param.output_vol = *(unsigned*)pval;
1869
/* API: Start stream. */
1870
static pj_status_t stream_start(pjmedia_aud_stream *strm)
1872
struct aps_stream *stream = (struct aps_stream*)strm;
1874
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1876
if (stream->engine) {
1877
TInt err = stream->engine->StartL();
1878
if (err != KErrNone)
1879
return PJ_RETURN_OS_ERROR(err);
1885
/* API: Stop stream. */
1886
static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1888
struct aps_stream *stream = (struct aps_stream*)strm;
1890
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1892
if (stream->engine) {
1893
stream->engine->Stop();
1900
/* API: Destroy stream. */
1901
static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1903
struct aps_stream *stream = (struct aps_stream*)strm;
1905
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1909
delete stream->engine;
1910
stream->engine = NULL;
1912
if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1913
TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
1914
stream->strm_data = NULL;
1915
delete g729_bitstream;
1919
pool = stream->pool;
1921
stream->pool = NULL;
1922
pj_pool_release(pool);
1928
#endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_APS