1
/* $Id: symb_aps_dev.cpp 4243 2012-08-31 11:42:17Z 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;
977
strm->param.ext_fmt.det.aud.avg_bps == 15200? 160 : 240;
979
/* Check if we got a normal frame. */
980
if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
981
const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2;
982
unsigned len = buf.iBuffer.Length() - 2;
984
pjmedia_frame_ext_append_subframe(frame, p, len << 3,
986
} else { /* We got null frame. */
987
pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
990
if (frame->samples_cnt == strm->param.samples_per_frame) {
991
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
992
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
993
frame->samples_cnt = 0;
994
frame->subframe_cnt = 0;
999
case PJMEDIA_FORMAT_PCMU:
1000
case PJMEDIA_FORMAT_PCMA:
1002
unsigned samples_processed = 0;
1004
/* Make sure it is normal frame. */
1005
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
1007
/* Detect the recorder G.711 frame size, player frame size will
1008
* follow this recorder frame size.
1010
if (aps_g711_frame_len == 0) {
1011
aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
1012
TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
1013
aps_g711_frame_len));
1016
/* Convert APS buffer format into pjmedia_frame_ext. Whenever
1017
* samples count in the frame is equal to stream's samples per
1018
* frame, call parent stream callback.
1020
while (samples_processed < aps_g711_frame_len) {
1022
const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() +
1023
2 + samples_processed;
1025
tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
1026
aps_g711_frame_len - samples_processed);
1028
pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
1029
samples_processed += tmp;
1031
if (frame->samples_cnt == strm->param.samples_per_frame) {
1032
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1033
strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1034
frame->samples_cnt = 0;
1035
frame->subframe_cnt = 0;
1046
static void PlayCb(TAPSCommBuffer &buf, void *user_data)
1048
struct aps_stream *strm = (struct aps_stream*) user_data;
1049
pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
1051
/* Init buffer attributes and header. */
1052
buf.iCommand = CQueueHandler::EAPSPlayData;
1056
switch(strm->param.ext_fmt.id) {
1057
case PJMEDIA_FORMAT_AMR:
1059
if (frame->samples_cnt == 0) {
1060
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1061
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1062
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1063
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1066
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1067
pjmedia_frame_ext_subframe *sf;
1068
unsigned samples_cnt;
1070
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1071
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1073
if (sf->data && sf->bitlen) {
1074
/* AMR header for APS is one byte, the format (may be!):
1075
* 0xxxxy00, where xxxx:frame type, y:not sure.
1077
unsigned len = (sf->bitlen+7)>>3;
1079
pj_uint8_t amr_header = 4, ft = SID_FT;
1081
if (len >= pjmedia_codec_amrnb_framelen[0])
1082
ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
1084
amr_header |= ft << 3;
1085
buf.iBuffer.Append(amr_header);
1087
buf.iBuffer.Append((TUint8*)sf->data, len);
1089
enum {NO_DATA_FT = 15 };
1090
pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1092
buf.iBuffer.Append(amr_header);
1095
pjmedia_frame_ext_pop_subframes(frame, 1);
1097
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1098
enum {NO_DATA_FT = 15 };
1099
pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1101
buf.iBuffer.Append(amr_header);
1103
frame->samples_cnt = 0;
1104
frame->subframe_cnt = 0;
1109
case PJMEDIA_FORMAT_G729:
1111
if (frame->samples_cnt == 0) {
1112
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1113
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1114
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1115
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1118
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1119
pjmedia_frame_ext_subframe *sf;
1120
unsigned samples_cnt;
1122
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1123
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1125
if (sf->data && sf->bitlen) {
1126
enum { NORMAL_LEN = 10, SID_LEN = 2 };
1127
pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
1128
TBitStream *bitstream = (TBitStream*)strm->strm_data;
1129
const TPtrC8 src(sf->data, sf->bitlen>>3);
1130
const TDesC8 &dst = bitstream->ExpandG729Frame(src,
1133
buf.iBuffer.Append(2);
1134
buf.iBuffer.Append(0);
1136
buf.iBuffer.Append(1);
1137
buf.iBuffer.Append(0);
1139
buf.iBuffer.Append(dst);
1141
buf.iBuffer.Append(2);
1142
buf.iBuffer.Append(0);
1143
buf.iBuffer.AppendFill(0, 22);
1146
pjmedia_frame_ext_pop_subframes(frame, 1);
1148
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1149
buf.iBuffer.Append(2);
1150
buf.iBuffer.Append(0);
1151
buf.iBuffer.AppendFill(0, 22);
1153
frame->samples_cnt = 0;
1154
frame->subframe_cnt = 0;
1159
case PJMEDIA_FORMAT_ILBC:
1161
if (frame->samples_cnt == 0) {
1162
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1163
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1164
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1165
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1168
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1169
pjmedia_frame_ext_subframe *sf;
1170
unsigned samples_cnt;
1172
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1173
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1175
pj_assert((strm->param.ext_fmt.det.aud.avg_bps == 15200 &&
1176
samples_cnt == 160) ||
1177
(strm->param.ext_fmt.det.aud.avg_bps != 15200 &&
1178
samples_cnt == 240));
1180
if (sf->data && sf->bitlen) {
1181
buf.iBuffer.Append(1);
1182
buf.iBuffer.Append(0);
1183
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1185
buf.iBuffer.Append(0);
1186
buf.iBuffer.Append(0);
1189
pjmedia_frame_ext_pop_subframes(frame, 1);
1191
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1192
buf.iBuffer.Append(0);
1193
buf.iBuffer.Append(0);
1195
frame->samples_cnt = 0;
1196
frame->subframe_cnt = 0;
1201
case PJMEDIA_FORMAT_PCMU:
1202
case PJMEDIA_FORMAT_PCMA:
1204
unsigned samples_ready = 0;
1205
unsigned samples_req = aps_g711_frame_len;
1207
/* Assume frame size is 10ms if frame size hasn't been known. */
1208
if (samples_req == 0)
1211
buf.iBuffer.Append(1);
1212
buf.iBuffer.Append(0);
1214
/* Call parent stream callback to get samples to play. */
1215
while (samples_ready < samples_req) {
1216
if (frame->samples_cnt == 0) {
1217
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1218
strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1219
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1220
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1223
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1224
pjmedia_frame_ext_subframe *sf;
1225
unsigned samples_cnt;
1227
sf = pjmedia_frame_ext_get_subframe(frame, 0);
1228
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1229
if (sf->data && sf->bitlen) {
1230
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1233
silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1234
pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1235
buf.iBuffer.AppendFill(silc, samples_cnt);
1237
samples_ready += samples_cnt;
1239
pjmedia_frame_ext_pop_subframes(frame, 1);
1241
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1244
silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1245
pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1246
buf.iBuffer.AppendFill(silc, samples_req - samples_ready);
1248
samples_ready = samples_req;
1249
frame->samples_cnt = 0;
1250
frame->subframe_cnt = 0;
1262
/****************************************************************************
1263
* Factory operations
1267
* C compatible declaration of APS factory.
1270
PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf);
1274
* Init APS audio driver.
1276
PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf)
1278
struct aps_factory *f;
1281
pool = pj_pool_create(pf, "APS", 1000, 1000, NULL);
1282
f = PJ_POOL_ZALLOC_T(pool, struct aps_factory);
1285
f->base.op = &factory_op;
1290
/* API: init factory */
1291
static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
1293
struct aps_factory *af = (struct aps_factory*)f;
1295
pj_ansi_strcpy(af->dev_info.name, "S60 APS");
1296
af->dev_info.default_samples_per_sec = 8000;
1297
af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
1298
//PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
1299
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
1300
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
1301
PJMEDIA_AUD_DEV_CAP_VAD |
1302
PJMEDIA_AUD_DEV_CAP_CNG;
1303
af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
1304
PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1305
af->dev_info.input_count = 1;
1306
af->dev_info.output_count = 1;
1308
/* Enumerate codecs by trying to initialize each codec and examining
1309
* the error code. Consider the following:
1310
* - not possible to reinitialize the same APS session with
1311
* different settings,
1312
* - closing APS session and trying to immediately reconnect may fail,
1313
* clients should wait ~5s before attempting to reconnect.
1316
unsigned i, fmt_cnt = 0;
1317
pj_bool_t g711_supported = PJ_FALSE;
1319
/* Do not change the order! */
1320
TFourCC fourcc[] = {
1321
TFourCC(KMCPFourCCIdAMRNB),
1322
TFourCC(KMCPFourCCIdG711),
1323
TFourCC(KMCPFourCCIdG729),
1324
TFourCC(KMCPFourCCIdILBC)
1327
for (i = 0; i < PJ_ARRAY_SIZE(fourcc); ++i) {
1328
pj_bool_t supported = PJ_FALSE;
1329
unsigned retry_cnt = 0;
1330
enum { MAX_RETRY = 3 };
1332
#if (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 0)
1333
/* Codec detection is disabled */
1334
supported = PJ_TRUE;
1335
#elif (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 1)
1336
/* Minimal codec detection, AMR-NB and G.711 only */
1338
/* If G.711 has been checked, skip G.729 and iLBC checks */
1339
retry_cnt = MAX_RETRY;
1340
supported = g711_supported;
1344
while (!supported && ++retry_cnt <= MAX_RETRY) {
1345
RAPSSession iSession;
1346
TAPSInitSettings iPlaySettings;
1347
TAPSInitSettings iRecSettings;
1350
// Recorder settings
1351
iRecSettings.iGlobal = APP_UID;
1352
iRecSettings.iPriority = TMdaPriority(100);
1353
iRecSettings.iPreference = TMdaPriorityPreference(0x05210001);
1354
iRecSettings.iSettings.iChannels = EMMFMono;
1355
iRecSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
1358
iPlaySettings.iGlobal = APP_UID;
1359
iPlaySettings.iPriority = TMdaPriority(100);
1360
iPlaySettings.iPreference = TMdaPriorityPreference(0x05220001);
1361
iPlaySettings.iSettings.iChannels = EMMFMono;
1362
iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
1364
iRecSettings.iFourCC = iPlaySettings.iFourCC = fourcc[i];
1366
err = iSession.Connect();
1367
if (err == KErrNone)
1368
err = iSession.InitializePlayer(iPlaySettings);
1369
if (err == KErrNone)
1370
err = iSession.InitializeRecorder(iRecSettings);
1372
// On some devices, immediate closing causes APS Server panic,
1373
// e.g: N95, so let's just wait for some time before closing.
1374
enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
1375
snd_wait(APS_CLOSE_WAIT_TIME);
1379
if (err == KErrNone) {
1380
/* All fine, stop retyring */
1381
supported = PJ_TRUE;
1382
} else if (err == KErrAlreadyExists && retry_cnt < MAX_RETRY) {
1383
/* Seems that the previous session is still arround,
1384
* let's wait before retrying.
1386
enum { RETRY_WAIT_TIME = 3000 }; /* in msecs */
1387
snd_wait(RETRY_WAIT_TIME);
1389
/* Seems that this format is not supported */
1390
retry_cnt = MAX_RETRY;
1395
pjmedia_format ext_fmt;
1399
pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_AMR,
1400
8000, 1, 16, 20, 7400, 12200);
1401
af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
1402
//af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1406
pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMU,
1407
8000, 1, 16, 20, 64000, 64000);
1408
af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
1409
//af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1411
pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMA,
1412
8000, 1, 16, 20, 64000, 64000);
1413
af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
1414
//af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1416
g711_supported = PJ_TRUE;
1419
pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_G729,
1420
8000, 1, 16, 20, 8000, 8000);
1421
af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
1422
//af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1426
pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_ILBC,
1427
8000, 1, 16, 30, 13333, 15200);
1428
af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
1429
//af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1436
af->dev_info.ext_fmt_cnt = fmt_cnt;
1438
PJ_LOG(4, (THIS_FILE, "APS initialized"));
1443
/* API: destroy factory */
1444
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
1446
struct aps_factory *af = (struct aps_factory*)f;
1447
pj_pool_t *pool = af->pool;
1450
pj_pool_release(pool);
1452
PJ_LOG(4, (THIS_FILE, "APS destroyed"));
1457
/* API: refresh the device list */
1458
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
1464
/* API: get number of devices */
1465
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
1471
/* API: get device info */
1472
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
1474
pjmedia_aud_dev_info *info)
1476
struct aps_factory *af = (struct aps_factory*)f;
1478
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1480
pj_memcpy(info, &af->dev_info, sizeof(*info));
1485
/* API: create default device parameter */
1486
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
1488
pjmedia_aud_param *param)
1490
struct aps_factory *af = (struct aps_factory*)f;
1492
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1494
pj_bzero(param, sizeof(*param));
1495
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1496
param->rec_id = index;
1497
param->play_id = index;
1498
param->clock_rate = af->dev_info.default_samples_per_sec;
1499
param->channel_count = 1;
1500
param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
1501
param->bits_per_sample = BITS_PER_SAMPLE;
1502
param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1503
param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1509
/* API: create stream */
1510
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1511
const pjmedia_aud_param *param,
1512
pjmedia_aud_rec_cb rec_cb,
1513
pjmedia_aud_play_cb play_cb,
1515
pjmedia_aud_stream **p_aud_strm)
1517
struct aps_factory *af = (struct aps_factory*)f;
1519
struct aps_stream *strm;
1521
CPjAudioSetting aps_setting;
1522
PjAudioCallback aps_rec_cb;
1523
PjAudioCallback aps_play_cb;
1525
/* Can only support 16bits per sample */
1526
PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
1528
/* Supported clock rates:
1529
* - for non-PCM format: 8kHz
1530
* - for PCM format: 8kHz and 16kHz
1532
PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
1533
(param->clock_rate == 16000 &&
1534
param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1537
/* Supported channels number:
1538
* - for non-PCM format: mono
1539
* - for PCM format: mono and stereo
1541
PJ_ASSERT_RETURN(param->channel_count == 1 ||
1542
(param->channel_count == 2 &&
1543
param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1546
/* Create and Initialize stream descriptor */
1547
pool = pj_pool_create(af->pf, "aps-dev", 1000, 1000, NULL);
1548
PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
1550
strm = PJ_POOL_ZALLOC_T(pool, struct aps_stream);
1552
strm->param = *param;
1554
if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
1555
strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
1557
/* Set audio engine fourcc. */
1558
switch(strm->param.ext_fmt.id) {
1559
case PJMEDIA_FORMAT_L16:
1560
case PJMEDIA_FORMAT_PCMU:
1561
case PJMEDIA_FORMAT_PCMA:
1562
aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
1564
case PJMEDIA_FORMAT_AMR:
1565
aps_setting.fourcc = TFourCC(KMCPFourCCIdAMRNB);
1567
case PJMEDIA_FORMAT_G729:
1568
aps_setting.fourcc = TFourCC(KMCPFourCCIdG729);
1570
case PJMEDIA_FORMAT_ILBC:
1571
aps_setting.fourcc = TFourCC(KMCPFourCCIdILBC);
1574
aps_setting.fourcc = 0;
1578
/* Set audio engine mode. */
1579
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
1581
aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.det.aud.avg_bps;
1583
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1584
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1585
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
1586
strm->param.ext_fmt.det.aud.avg_bps != 15200))
1588
aps_setting.mode = EULawOr30ms;
1590
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1591
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
1592
strm->param.ext_fmt.det.aud.avg_bps == 15200))
1594
aps_setting.mode = EALawOr20ms;
1597
/* Disable VAD on L16, G711, and also G729 (G729's VAD potentially
1600
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1601
strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1602
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1603
strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
1605
aps_setting.vad = EFalse;
1607
aps_setting.vad = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_VAD) &&
1608
strm->param.vad_enabled;
1611
/* Set other audio engine attributes. */
1612
aps_setting.plc = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_PLC) &&
1613
strm->param.plc_enabled;
1614
aps_setting.cng = aps_setting.vad;
1615
aps_setting.loudspk =
1616
strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1618
/* Set audio engine callbacks. */
1619
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
1620
aps_play_cb = &PlayCbPcm;
1621
aps_rec_cb = &RecCbPcm;
1623
aps_play_cb = &PlayCb;
1624
aps_rec_cb = &RecCb;
1627
strm->rec_cb = rec_cb;
1628
strm->play_cb = play_cb;
1629
strm->user_data = user_data;
1630
strm->resample_factor = strm->param.clock_rate / 8000;
1632
/* play_buf size is samples per frame scaled in to 8kHz mono. */
1633
strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
1635
(strm->param.samples_per_frame /
1636
strm->resample_factor /
1637
strm->param.channel_count) << 1);
1638
strm->play_buf_len = 0;
1639
strm->play_buf_start = 0;
1641
/* rec_buf size is samples per frame scaled in to 8kHz mono. */
1642
strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(
1644
(strm->param.samples_per_frame /
1645
strm->resample_factor /
1646
strm->param.channel_count) << 1);
1647
strm->rec_buf_len = 0;
1649
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1650
TBitStream *g729_bitstream = new TBitStream;
1652
PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
1653
strm->strm_data = (void*)g729_bitstream;
1656
/* Init resampler when format is PCM and clock rate is not 8kHz */
1657
if (strm->param.clock_rate != 8000 &&
1658
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1662
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1663
/* Create resample for recorder */
1664
status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1666
strm->param.clock_rate,
1668
&strm->rec_resample);
1669
if (status != PJ_SUCCESS)
1673
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1674
/* Create resample for player */
1675
status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1676
strm->param.clock_rate,
1678
80 * strm->resample_factor,
1679
&strm->play_resample);
1680
if (status != PJ_SUCCESS)
1685
/* Create PCM buffer, when the clock rate is not 8kHz or not mono */
1686
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
1687
(strm->resample_factor > 1 || strm->param.channel_count != 1))
1689
strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
1690
strm->param.samples_per_frame << 1);
1694
/* Create the audio engine. */
1695
TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
1696
aps_rec_cb, aps_play_cb,
1697
strm, aps_setting));
1698
if (err != KErrNone) {
1699
pj_pool_release(pool);
1700
return PJ_RETURN_OS_ERROR(err);
1703
/* Apply output volume setting if specified */
1704
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1705
stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1706
¶m->output_vol);
1710
strm->base.op = &stream_op;
1711
*p_aud_strm = &strm->base;
1716
/* API: Get stream info. */
1717
static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1718
pjmedia_aud_param *pi)
1720
struct aps_stream *strm = (struct aps_stream*)s;
1722
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1724
pj_memcpy(pi, &strm->param, sizeof(*pi));
1726
/* Update the output volume setting */
1727
if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1728
&pi->output_vol) == PJ_SUCCESS)
1730
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1736
/* API: get capability */
1737
static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1738
pjmedia_aud_dev_cap cap,
1741
struct aps_stream *strm = (struct aps_stream*)s;
1742
pj_status_t status = PJ_ENOTSUP;
1744
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1747
case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1748
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1749
*(pjmedia_aud_dev_route*)pval = strm->param.output_route;
1750
status = PJ_SUCCESS;
1754
/* There is a case that GetMaxGain() stucks, e.g: in N95. */
1756
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1757
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1758
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1760
TInt max_gain = strm->engine->GetMaxGain();
1761
TInt gain = strm->engine->GetGain();
1763
if (max_gain > 0 && gain >= 0) {
1764
*(unsigned*)pval = gain * 100 / max_gain;
1765
status = PJ_SUCCESS;
1767
status = PJMEDIA_EAUD_NOTREADY;
1773
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1774
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1775
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1777
TInt max_vol = strm->engine->GetMaxVolume();
1778
TInt vol = strm->engine->GetVolume();
1780
if (max_vol > 0 && vol >= 0) {
1781
*(unsigned*)pval = vol * 100 / max_vol;
1782
status = PJ_SUCCESS;
1784
status = PJMEDIA_EAUD_NOTREADY;
1795
/* API: set capability */
1796
static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1797
pjmedia_aud_dev_cap cap,
1800
struct aps_stream *strm = (struct aps_stream*)s;
1801
pj_status_t status = PJ_ENOTSUP;
1803
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1806
case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1807
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1808
pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
1811
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1814
case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
1815
case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
1816
err = strm->engine->ActivateSpeaker(EFalse);
1817
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1819
case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
1820
err = strm->engine->ActivateSpeaker(ETrue);
1821
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1827
if (status == PJ_SUCCESS)
1828
strm->param.output_route = r;
1832
/* There is a case that GetMaxGain() stucks, e.g: in N95. */
1834
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1835
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1836
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1838
TInt max_gain = strm->engine->GetMaxGain();
1842
gain = *(unsigned*)pval * max_gain / 100;
1843
err = strm->engine->SetGain(gain);
1844
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1846
status = PJMEDIA_EAUD_NOTREADY;
1848
if (status == PJ_SUCCESS)
1849
strm->param.input_vol = *(unsigned*)pval;
1854
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1855
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1856
PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1858
TInt max_vol = strm->engine->GetMaxVolume();
1862
vol = *(unsigned*)pval * max_vol / 100;
1863
err = strm->engine->SetVolume(vol);
1864
status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1866
status = PJMEDIA_EAUD_NOTREADY;
1868
if (status == PJ_SUCCESS)
1869
strm->param.output_vol = *(unsigned*)pval;
1879
/* API: Start stream. */
1880
static pj_status_t stream_start(pjmedia_aud_stream *strm)
1882
struct aps_stream *stream = (struct aps_stream*)strm;
1884
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1886
if (stream->engine) {
1887
TInt err = stream->engine->StartL();
1888
if (err != KErrNone)
1889
return PJ_RETURN_OS_ERROR(err);
1895
/* API: Stop stream. */
1896
static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1898
struct aps_stream *stream = (struct aps_stream*)strm;
1900
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1902
if (stream->engine) {
1903
stream->engine->Stop();
1910
/* API: Destroy stream. */
1911
static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1913
struct aps_stream *stream = (struct aps_stream*)strm;
1915
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1919
delete stream->engine;
1920
stream->engine = NULL;
1922
if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1923
TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
1924
stream->strm_data = NULL;
1925
delete g729_bitstream;
1929
pool = stream->pool;
1931
stream->pool = NULL;
1932
pj_pool_release(pool);
1938
#endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_APS