blob: 2f35a660fc8c022551bc8aa92a0ef71d0def9337 [file] [log] [blame]
Nanang Izzuddind687a502009-06-30 13:37:26 +00001/* $Id$ */
2/*
3 * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-audiodev/audiodev_imp.h>
20#include <pjmedia-audiodev/errno.h>
21#include <pjmedia/alaw_ulaw.h>
22#include <pjmedia/resample.h>
23#include <pjmedia/stereo.h>
24#include <pj/assert.h>
25#include <pj/log.h>
26#include <pj/math.h>
27#include <pj/os.h>
28#include <pj/string.h>
29
30#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
31
32/* VAS headers */
33#include <VoIPUtilityFactory.h>
34#include <VoIPDownlinkStream.h>
35#include <VoIPUplinkStream.h>
36#include <VoIPFormatIntfc.h>
37#include <VoIPG711DecoderIntfc.h>
38#include <VoIPG711EncoderIntfc.h>
39#include <VoIPG729DecoderIntfc.h>
40#include <VoIPILBCDecoderIntfc.h>
41#include <VoIPILBCEncoderIntfc.h>
42
43/* AMR helper */
44#include <pjmedia-codec/amr_helper.h>
45
46/* Pack/unpack G.729 frame of S60 DSP codec, taken from:
47 * http://wiki.forum.nokia.com/index.php/TSS000776_-_Payload_conversion_for_G.729_audio_format
48 */
49#include "s60_g729_bitstream.h"
50
51
52#define THIS_FILE "symb_vas_dev.c"
53#define BITS_PER_SAMPLE 16
54
55
56/* When this macro is set, VAS will use EPCM16 format for PCM input/output,
57 * otherwise VAS will use EG711 then transcode it to PCM.
58 * Note that using native EPCM16 format may introduce (much) delay.
59 */
60//#define USE_NATIVE_PCM
61
62#if 1
63# define TRACE_(st) PJ_LOG(3, st)
64#else
65# define TRACE_(st)
66#endif
67
68/* VAS G.711 frame length */
69static pj_uint8_t vas_g711_frame_len;
70
71
72/* VAS factory */
73struct vas_factory
74{
75 pjmedia_aud_dev_factory base;
76 pj_pool_t *pool;
77 pj_pool_factory *pf;
78 pjmedia_aud_dev_info dev_info;
79};
80
81
82/* Forward declaration of CPjAudioEngine */
83class CPjAudioEngine;
84
85
86/* VAS stream. */
87struct vas_stream
88{
89 // Base
90 pjmedia_aud_stream base; /**< Base class. */
91
92 // Pool
93 pj_pool_t *pool; /**< Memory pool. */
94
95 // Common settings.
96 pjmedia_aud_param param; /**< Stream param. */
97 pjmedia_aud_rec_cb rec_cb; /**< Record callback. */
98 pjmedia_aud_play_cb play_cb; /**< Playback callback. */
99 void *user_data; /**< Application data. */
100
101 // Audio engine
102 CPjAudioEngine *engine; /**< Internal engine. */
103
104 pj_timestamp ts_play; /**< Playback timestamp.*/
105 pj_timestamp ts_rec; /**< Record timestamp. */
106
107 pj_int16_t *play_buf; /**< Playback buffer. */
108 pj_uint16_t play_buf_len; /**< Playback buffer length. */
109 pj_uint16_t play_buf_start; /**< Playback buffer start index. */
110 pj_int16_t *rec_buf; /**< Record buffer. */
111 pj_uint16_t rec_buf_len; /**< Record buffer length. */
112 void *strm_data; /**< Stream data. */
113
114 /* Resampling is needed, in case audio device is opened with clock rate
115 * other than 8kHz (only for PCM format).
116 */
117 pjmedia_resample *play_resample; /**< Resampler for playback. */
118 pjmedia_resample *rec_resample; /**< Resampler for recording */
119 pj_uint16_t resample_factor; /**< Resample factor, requested
120 clock rate / 8000 */
121
122 /* When stream is working in PCM format, where the samples may need to be
123 * resampled from/to different clock rate and/or channel count, PCM buffer
124 * is needed to perform such resampling operations.
125 */
126 pj_int16_t *pcm_buf; /**< PCM buffer. */
127};
128
129
130/* Prototypes */
131static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
132static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
133static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
134static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
135 unsigned index,
136 pjmedia_aud_dev_info *info);
137static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
138 unsigned index,
139 pjmedia_aud_param *param);
140static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
141 const pjmedia_aud_param *param,
142 pjmedia_aud_rec_cb rec_cb,
143 pjmedia_aud_play_cb play_cb,
144 void *user_data,
145 pjmedia_aud_stream **p_aud_strm);
146
147static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
148 pjmedia_aud_param *param);
149static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
150 pjmedia_aud_dev_cap cap,
151 void *value);
152static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
153 pjmedia_aud_dev_cap cap,
154 const void *value);
155static pj_status_t stream_start(pjmedia_aud_stream *strm);
156static pj_status_t stream_stop(pjmedia_aud_stream *strm);
157static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
158
159
160/* Operations */
161static pjmedia_aud_dev_factory_op factory_op =
162{
163 &factory_init,
164 &factory_destroy,
165 &factory_get_dev_count,
166 &factory_get_dev_info,
167 &factory_default_param,
168 &factory_create_stream
169};
170
171static pjmedia_aud_stream_op stream_op =
172{
173 &stream_get_param,
174 &stream_get_cap,
175 &stream_set_cap,
176 &stream_start,
177 &stream_stop,
178 &stream_destroy
179};
180
181
182/****************************************************************************
183 * Internal VAS Engine
184 */
185
186/*
187 * Utility: print sound device error
188 */
189static void snd_perror(const char *title, TInt rc)
190{
191 PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
192}
193
194typedef void(*PjAudioCallback)(CVoIPDataBuffer *buf, void *user_data);
195
196/*
197 * Audio setting for CPjAudioEngine.
198 */
199class CPjAudioSetting
200{
201public:
202 TVoIPCodecFormat format;
203 TInt mode;
204 TBool plc;
205 TBool vad;
206 TBool cng;
207 TBool loudspk;
208};
209
210/*
211 * Implementation: Symbian Input & Output Stream.
212 */
213class CPjAudioEngine : public CBase,
214 public MVoIPDownlinkObserver,
215 public MVoIPUplinkObserver,
216 public MVoIPFormatObserver
217{
218public:
219 enum State
220 {
221 STATE_NULL,
222 STATE_STARTING,
223 STATE_READY,
224 STATE_STREAMING
225 };
226
227 ~CPjAudioEngine();
228
229 static CPjAudioEngine *NewL(struct vas_stream *parent_strm,
230 PjAudioCallback rec_cb,
231 PjAudioCallback play_cb,
232 void *user_data,
233 const CPjAudioSetting &setting);
234
235 TInt Start();
236 void Stop();
237
238 TInt ActivateSpeaker(TBool active);
239
240 TInt SetVolume(TInt vol) { return iVoIPDnlink->SetVolume(vol); }
241 TInt GetVolume() { TInt vol;iVoIPDnlink->GetVolume(vol);return vol; }
242 TInt GetMaxVolume() { TInt vol;iVoIPDnlink->GetMaxVolume(vol);return vol; }
243
244 TInt SetGain(TInt gain) { return iVoIPUplink->SetGain(gain); }
245 TInt GetGain() { TInt gain;iVoIPUplink->GetGain(gain);return gain; }
246 TInt GetMaxGain() { TInt gain;iVoIPUplink->GetMaxGain(gain);return gain; }
247
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000248 TBool IsStarted();
249
Nanang Izzuddind687a502009-06-30 13:37:26 +0000250private:
251 CPjAudioEngine(struct vas_stream *parent_strm,
252 PjAudioCallback rec_cb,
253 PjAudioCallback play_cb,
254 void *user_data,
255 const CPjAudioSetting &setting);
256 void ConstructL();
257
258 TInt InitPlay();
259 TInt InitRec();
260
261 TInt StartPlay();
262 TInt StartRec();
263
264 // From MVoIPDownlinkObserver
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000265 void FillBuffer(const CVoIPAudioDownlinkStream& aSrc,
Nanang Izzuddind687a502009-06-30 13:37:26 +0000266 CVoIPDataBuffer* aBuffer);
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000267 void Event(const CVoIPAudioDownlinkStream& aSrc,
Nanang Izzuddind687a502009-06-30 13:37:26 +0000268 TInt aEventType,
269 TInt aError);
270
271 // From MVoIPUplinkObserver
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000272 void EmptyBuffer(const CVoIPAudioUplinkStream& aSrc,
Nanang Izzuddind687a502009-06-30 13:37:26 +0000273 CVoIPDataBuffer* aBuffer);
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000274 void Event(const CVoIPAudioUplinkStream& aSrc,
Nanang Izzuddind687a502009-06-30 13:37:26 +0000275 TInt aEventType,
276 TInt aError);
277
278 // From MVoIPFormatObserver
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000279 void Event(const CVoIPFormatIntfc& aSrc, TInt aEventType);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000280
281 State dn_state_;
282 State up_state_;
283 struct vas_stream *parentStrm_;
284 CPjAudioSetting setting_;
285 PjAudioCallback rec_cb_;
286 PjAudioCallback play_cb_;
287 void *user_data_;
288
289 // VAS objects
290 CVoIPUtilityFactory *iFactory;
291 CVoIPAudioDownlinkStream *iVoIPDnlink;
292 CVoIPAudioUplinkStream *iVoIPUplink;
293 CVoIPFormatIntfc *enc_fmt_if;
294 CVoIPFormatIntfc *dec_fmt_if;
295};
296
297
298CPjAudioEngine* CPjAudioEngine::NewL(struct vas_stream *parent_strm,
299 PjAudioCallback rec_cb,
300 PjAudioCallback play_cb,
301 void *user_data,
302 const CPjAudioSetting &setting)
303{
304 CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
305 rec_cb, play_cb,
306 user_data,
307 setting);
308 CleanupStack::PushL(self);
309 self->ConstructL();
310 CleanupStack::Pop(self);
311 return self;
312}
313
314void CPjAudioEngine::ConstructL()
315{
316 TInt err;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000317 const TVersion ver(1, 0, 0); /* Not really used at this time */
Nanang Izzuddind687a502009-06-30 13:37:26 +0000318
319 err = CVoIPUtilityFactory::CreateFactory(iFactory);
320 User::LeaveIfError(err);
321
322 if (parentStrm_->param.dir != PJMEDIA_DIR_CAPTURE) {
323 err = iFactory->CreateDownlinkStream(ver,
324 CVoIPUtilityFactory::EVoIPCall,
325 iVoIPDnlink);
326 User::LeaveIfError(err);
327 }
328
329 if (parentStrm_->param.dir != PJMEDIA_DIR_PLAYBACK) {
330 err = iFactory->CreateUplinkStream(ver,
331 CVoIPUtilityFactory::EVoIPCall,
332 iVoIPUplink);
333 User::LeaveIfError(err);
334 }
335}
336
337CPjAudioEngine::CPjAudioEngine(struct vas_stream *parent_strm,
338 PjAudioCallback rec_cb,
339 PjAudioCallback play_cb,
340 void *user_data,
341 const CPjAudioSetting &setting)
342 : dn_state_(STATE_NULL),
343 up_state_(STATE_NULL),
344 parentStrm_(parent_strm),
345 setting_(setting),
346 rec_cb_(rec_cb),
347 play_cb_(play_cb),
348 user_data_(user_data),
349 iFactory(NULL),
350 iVoIPDnlink(NULL),
351 iVoIPUplink(NULL),
352 enc_fmt_if(NULL),
353 dec_fmt_if(NULL)
354{
355}
356
357CPjAudioEngine::~CPjAudioEngine()
358{
359 Stop();
360
361 if (iVoIPUplink)
362 iVoIPUplink->Close();
363
364 if (iVoIPDnlink)
365 iVoIPDnlink->Close();
366
Nanang Izzuddin5de1abb2010-06-11 09:44:18 +0000367 delete enc_fmt_if;
368 delete dec_fmt_if;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000369 delete iVoIPDnlink;
370 delete iVoIPUplink;
371 delete iFactory;
372
373 TRACE_((THIS_FILE, "Sound device destroyed"));
374}
375
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000376TBool CPjAudioEngine::IsStarted()
377{
378 return ((((parentStrm_->param.dir & PJMEDIA_DIR_CAPTURE) == 0) ||
379 up_state_ == STATE_STREAMING) &&
380 (((parentStrm_->param.dir & PJMEDIA_DIR_PLAYBACK) == 0) ||
381 dn_state_ == STATE_STREAMING));
382}
383
Nanang Izzuddind687a502009-06-30 13:37:26 +0000384TInt CPjAudioEngine::InitPlay()
385{
386 TInt err;
387
388 pj_assert(iVoIPDnlink);
389
Nanang Izzuddin5de1abb2010-06-11 09:44:18 +0000390 delete dec_fmt_if;
391 dec_fmt_if = NULL;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000392 err = iVoIPDnlink->SetFormat(setting_.format, dec_fmt_if);
393 if (err != KErrNone)
394 return err;
395
396 err = dec_fmt_if->SetObserver(*this);
397 if (err != KErrNone)
398 return err;
399
400 return iVoIPDnlink->Open(*this);
401}
402
403TInt CPjAudioEngine::InitRec()
404{
405 TInt err;
406
407 pj_assert(iVoIPUplink);
408
Nanang Izzuddin5de1abb2010-06-11 09:44:18 +0000409 delete enc_fmt_if;
410 enc_fmt_if = NULL;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000411 err = iVoIPUplink->SetFormat(setting_.format, enc_fmt_if);
412 if (err != KErrNone)
413 return err;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000414
415 err = enc_fmt_if->SetObserver(*this);
416 if (err != KErrNone)
417 return err;
418
Nanang Izzuddind687a502009-06-30 13:37:26 +0000419 return iVoIPUplink->Open(*this);
420}
421
422TInt CPjAudioEngine::StartPlay()
423{
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000424 TInt err = KErrNone;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000425
Nanang Izzuddind687a502009-06-30 13:37:26 +0000426 pj_assert(iVoIPDnlink);
427 pj_assert(dn_state_ == STATE_READY);
428
429 /* Configure specific codec setting */
430 switch (setting_.format) {
431 case EG711:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000432 {
433 CVoIPG711DecoderIntfc *g711dec_if = (CVoIPG711DecoderIntfc*)
434 dec_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000435 err = g711dec_if->SetMode((CVoIPFormatIntfc::TG711CodecMode)
436 setting_.mode);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000437 }
438 break;
439
440 case EILBC:
441 {
442 CVoIPILBCDecoderIntfc *ilbcdec_if = (CVoIPILBCDecoderIntfc*)
443 dec_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000444 err = ilbcdec_if->SetMode((CVoIPFormatIntfc::TILBCCodecMode)
445 setting_.mode);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000446 }
447 break;
448
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000449 case EAMR_NB:
450 /* Ticket #1008: AMR playback issue on few devices, e.g: E72, E52 */
451 err = dec_fmt_if->SetFrameMode(ETrue);
452 break;
453
Nanang Izzuddind687a502009-06-30 13:37:26 +0000454 default:
455 break;
456 }
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000457
458 if (err != KErrNone)
459 goto on_return;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000460
461 /* Configure audio routing */
462 ActivateSpeaker(setting_.loudspk);
463
464 /* Start player */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000465 err = iVoIPDnlink->Start();
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000466
467on_return:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000468 if (err == KErrNone) {
469 dn_state_ = STATE_STREAMING;
470 TRACE_((THIS_FILE, "Downlink started"));
471 } else {
472 snd_perror("Failed starting downlink", err);
473 }
474
475 return err;
476}
477
478TInt CPjAudioEngine::StartRec()
479{
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000480 TInt err = KErrNone;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000481
Nanang Izzuddind687a502009-06-30 13:37:26 +0000482 pj_assert(iVoIPUplink);
483 pj_assert(up_state_ == STATE_READY);
484
Nanang Izzuddind687a502009-06-30 13:37:26 +0000485 /* Configure specific codec setting */
486 switch (setting_.format) {
487 case EG711:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000488 {
489 CVoIPG711EncoderIntfc *g711enc_if = (CVoIPG711EncoderIntfc*)
490 enc_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000491 err = g711enc_if->SetMode((CVoIPFormatIntfc::TG711CodecMode)
492 setting_.mode);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000493 }
494 break;
495
496 case EILBC:
497 {
498 CVoIPILBCEncoderIntfc *ilbcenc_if = (CVoIPILBCEncoderIntfc*)
499 enc_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000500 err = ilbcenc_if->SetMode((CVoIPFormatIntfc::TILBCCodecMode)
501 setting_.mode);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000502 }
503 break;
504
Nanang Izzuddin06839e72010-01-27 11:48:31 +0000505 case EAMR_NB:
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000506 err = enc_fmt_if->SetBitRate(setting_.mode);
Nanang Izzuddin06839e72010-01-27 11:48:31 +0000507 break;
508
Nanang Izzuddind687a502009-06-30 13:37:26 +0000509 default:
510 break;
511 }
512
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000513 if (err != KErrNone)
514 goto on_return;
515
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000516 /* Configure general codec setting */
517 enc_fmt_if->SetVAD(setting_.vad);
518
Nanang Izzuddind687a502009-06-30 13:37:26 +0000519 /* Start recorder */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000520 err = iVoIPUplink->Start();
Nanang Izzuddin308d5332010-03-04 15:47:25 +0000521
522on_return:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000523 if (err == KErrNone) {
524 up_state_ = STATE_STREAMING;
525 TRACE_((THIS_FILE, "Uplink started"));
526 } else {
527 snd_perror("Failed starting uplink", err);
528 }
529
530 return err;
531}
532
533TInt CPjAudioEngine::Start()
534{
535 TInt err = KErrNone;
536
537 if (iVoIPDnlink) {
538 switch(dn_state_) {
539 case STATE_READY:
540 err = StartPlay();
541 break;
542 case STATE_NULL:
543 err = InitPlay();
544 if (err != KErrNone)
545 return err;
546 dn_state_ = STATE_STARTING;
547 break;
548 default:
549 break;
550 }
551 }
552
553 if (iVoIPUplink) {
554 switch(up_state_) {
555 case STATE_READY:
556 err = StartRec();
557 break;
558 case STATE_NULL:
559 err = InitRec();
560 if (err != KErrNone)
561 return err;
562 up_state_ = STATE_STARTING;
563 break;
564 default:
565 break;
566 }
567 }
568
569 return err;
570}
571
572void CPjAudioEngine::Stop()
573{
574 if (iVoIPDnlink) {
575 switch(dn_state_) {
576 case STATE_STREAMING:
577 iVoIPDnlink->Stop();
578 dn_state_ = STATE_READY;
579 break;
580 case STATE_STARTING:
581 dn_state_ = STATE_NULL;
582 break;
583 default:
584 break;
585 }
586 }
587
588 if (iVoIPUplink) {
589 switch(up_state_) {
590 case STATE_STREAMING:
591 iVoIPUplink->Stop();
592 up_state_ = STATE_READY;
593 break;
594 case STATE_STARTING:
595 up_state_ = STATE_NULL;
596 break;
597 default:
598 break;
599 }
600 }
601}
602
603
604TInt CPjAudioEngine::ActivateSpeaker(TBool active)
605{
606 TInt err = KErrNotSupported;
607
608 if (iVoIPDnlink) {
609 err = iVoIPDnlink->SetAudioDevice(active?
610 CVoIPAudioDownlinkStream::ELoudSpeaker :
611 CVoIPAudioDownlinkStream::EHandset);
612 TRACE_((THIS_FILE, "Loudspeaker turned %s", (active? "on":"off")));
613 }
614
615 return err;
616}
617
618// Callback from MVoIPDownlinkObserver
619void CPjAudioEngine::FillBuffer(const CVoIPAudioDownlinkStream& aSrc,
620 CVoIPDataBuffer* aBuffer)
621{
622 play_cb_(aBuffer, user_data_);
623 iVoIPDnlink->BufferFilled(aBuffer);
624}
625
626// Callback from MVoIPUplinkObserver
627void CPjAudioEngine::EmptyBuffer(const CVoIPAudioUplinkStream& aSrc,
628 CVoIPDataBuffer* aBuffer)
629{
630 rec_cb_(aBuffer, user_data_);
631 iVoIPUplink->BufferEmptied(aBuffer);
632}
633
634// Callback from MVoIPDownlinkObserver
635void CPjAudioEngine::Event(const CVoIPAudioDownlinkStream& /*aSrc*/,
636 TInt aEventType,
637 TInt aError)
638{
639 switch (aEventType) {
640 case MVoIPDownlinkObserver::KOpenComplete:
641 if (aError == KErrNone) {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000642 State last_state = dn_state_;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000643
644 dn_state_ = STATE_READY;
645 TRACE_((THIS_FILE, "Downlink opened"));
646
647 if (last_state == STATE_STARTING)
648 StartPlay();
649 }
650 break;
651
652 case MVoIPDownlinkObserver::KDownlinkClosed:
653 dn_state_ = STATE_NULL;
654 TRACE_((THIS_FILE, "Downlink closed"));
655 break;
656
657 case MVoIPDownlinkObserver::KDownlinkError:
658 dn_state_ = STATE_READY;
659 snd_perror("Downlink problem", aError);
660 break;
661 default:
662 break;
663 }
664}
665
666// Callback from MVoIPUplinkObserver
667void CPjAudioEngine::Event(const CVoIPAudioUplinkStream& /*aSrc*/,
668 TInt aEventType,
669 TInt aError)
670{
671 switch (aEventType) {
672 case MVoIPUplinkObserver::KOpenComplete:
673 if (aError == KErrNone) {
674 State last_state = up_state_;
675
676 up_state_ = STATE_READY;
677 TRACE_((THIS_FILE, "Uplink opened"));
678
679 if (last_state == STATE_STARTING)
680 StartRec();
681 }
682 break;
683
684 case MVoIPUplinkObserver::KUplinkClosed:
685 up_state_ = STATE_NULL;
686 TRACE_((THIS_FILE, "Uplink closed"));
687 break;
688
689 case MVoIPUplinkObserver::KUplinkError:
690 up_state_ = STATE_READY;
691 snd_perror("Uplink problem", aError);
692 break;
693 default:
694 break;
695 }
696}
697
698// Callback from MVoIPFormatObserver
699void CPjAudioEngine::Event(const CVoIPFormatIntfc& /*aSrc*/,
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000700 TInt aEventType)
Nanang Izzuddind687a502009-06-30 13:37:26 +0000701{
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000702 snd_perror("Format event", aEventType);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000703}
704
705/****************************************************************************
706 * Internal VAS callbacks for PCM format
707 */
708
709#ifdef USE_NATIVE_PCM
710
711static void RecCbPcm2(CVoIPDataBuffer *buf, void *user_data)
712{
713 struct vas_stream *strm = (struct vas_stream*) user_data;
714 TPtr8 buffer(0, 0, 0);
715 pj_int16_t *p_buf;
716 unsigned buf_len;
717
718 /* Get the buffer */
719 buf->GetPayloadPtr(buffer);
720
721 /* Call parent callback */
722 p_buf = (pj_int16_t*) buffer.Ptr();
723 buf_len = buffer.Length() >> 1;
724 while (buf_len) {
725 unsigned req;
726
727 req = strm->param.samples_per_frame - strm->rec_buf_len;
728 if (req > buf_len)
729 req = buf_len;
730 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_len, p_buf, req);
731 p_buf += req;
732 buf_len -= req;
733 strm->rec_buf_len += req;
734
735 if (strm->rec_buf_len >= strm->param.samples_per_frame) {
736 pjmedia_frame f;
737
738 f.buf = strm->rec_buf;
739 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
740 f.size = strm->param.samples_per_frame << 1;
741 strm->rec_cb(strm->user_data, &f);
742 strm->rec_buf_len = 0;
743 }
744 }
745}
746
747static void PlayCbPcm2(CVoIPDataBuffer *buf, void *user_data)
748{
749 struct vas_stream *strm = (struct vas_stream*) user_data;
750 TPtr8 buffer(0, 0, 0);
751 pjmedia_frame f;
752
753 /* Get the buffer */
754 buf->GetPayloadPtr(buffer);
755
756 /* Call parent callback */
757 f.buf = strm->play_buf;
758 f.size = strm->param.samples_per_frame << 1;
759 strm->play_cb(strm->user_data, &f);
760 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
761 pjmedia_zero_samples((pj_int16_t*)f.buf,
762 strm->param.samples_per_frame);
763 }
764 f.size = strm->param.samples_per_frame << 1;
765
766 /* Init buffer attributes and header. */
767 buffer.Zero();
768 buffer.Append((TUint8*)f.buf, f.size);
769
770 /* Set the buffer */
771 buf->SetPayloadPtr(buffer);
772}
773
774#else // not USE_NATIVE_PCM
775
776static void RecCbPcm(CVoIPDataBuffer *buf, void *user_data)
777{
778 struct vas_stream *strm = (struct vas_stream*) user_data;
779 TPtr8 buffer(0, 0, 0);
780
781 /* Get the buffer */
782 buf->GetPayloadPtr(buffer);
783
784 /* Buffer has to contain normal speech. */
785 pj_assert(buffer[0] == 1 && buffer[1] == 0);
786
787 /* Detect the recorder G.711 frame size, player frame size will follow
788 * this recorder frame size.
789 */
790 if (vas_g711_frame_len == 0) {
791 vas_g711_frame_len = buffer.Length() < 160? 80 : 160;
792 TRACE_((THIS_FILE, "Detected VAS G.711 frame size = %u samples",
793 vas_g711_frame_len));
794 }
795
796 /* Decode VAS buffer (coded in G.711) and put the PCM result into rec_buf.
797 * Whenever rec_buf is full, call parent stream callback.
798 */
799 unsigned samples_processed = 0;
800
801 while (samples_processed < vas_g711_frame_len) {
802 unsigned samples_to_process;
803 unsigned samples_req;
804
805 samples_to_process = vas_g711_frame_len - samples_processed;
806 samples_req = (strm->param.samples_per_frame /
807 strm->param.channel_count /
808 strm->resample_factor) -
809 strm->rec_buf_len;
810 if (samples_to_process > samples_req)
811 samples_to_process = samples_req;
812
813 pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
814 buffer.Ptr() + 2 + samples_processed,
815 samples_to_process);
816
817 strm->rec_buf_len += samples_to_process;
818 samples_processed += samples_to_process;
819
820 /* Buffer is full, time to call parent callback */
821 if (strm->rec_buf_len == strm->param.samples_per_frame /
822 strm->param.channel_count /
823 strm->resample_factor)
824 {
825 pjmedia_frame f;
826
827 /* Need to resample clock rate? */
828 if (strm->rec_resample) {
829 unsigned resampled = 0;
830
831 while (resampled < strm->rec_buf_len) {
832 pjmedia_resample_run(strm->rec_resample,
833 &strm->rec_buf[resampled],
834 strm->pcm_buf +
835 resampled * strm->resample_factor);
836 resampled += 80;
837 }
838 f.buf = strm->pcm_buf;
839 } else {
840 f.buf = strm->rec_buf;
841 }
842
843 /* Need to convert channel count? */
844 if (strm->param.channel_count != 1) {
845 pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
846 (pj_int16_t*)f.buf,
847 strm->param.channel_count,
848 strm->param.samples_per_frame /
849 strm->param.channel_count,
850 0);
851 }
852
853 /* Call parent callback */
854 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
855 f.size = strm->param.samples_per_frame << 1;
856 strm->rec_cb(strm->user_data, &f);
857 strm->rec_buf_len = 0;
858 }
859 }
860}
861
862#endif // USE_NATIVE_PCM
863
864static void PlayCbPcm(CVoIPDataBuffer *buf, void *user_data)
865{
866 struct vas_stream *strm = (struct vas_stream*) user_data;
867 unsigned g711_frame_len = vas_g711_frame_len;
868 TPtr8 buffer(0, 0, 0);
869
870 /* Get the buffer */
871 buf->GetPayloadPtr(buffer);
872
873 /* Init buffer attributes and header. */
874 buffer.Zero();
875 buffer.Append(1);
876 buffer.Append(0);
877
878 /* Assume frame size is 10ms if frame size hasn't been known. */
879 if (g711_frame_len == 0)
880 g711_frame_len = 80;
881
882 /* Call parent stream callback to get PCM samples to play,
883 * encode the PCM samples into G.711 and put it into VAS buffer.
884 */
885 unsigned samples_processed = 0;
886
887 while (samples_processed < g711_frame_len) {
888 /* Need more samples to play, time to call parent callback */
889 if (strm->play_buf_len == 0) {
890 pjmedia_frame f;
891 unsigned samples_got;
892
893 f.size = strm->param.samples_per_frame << 1;
894 if (strm->play_resample || strm->param.channel_count != 1)
895 f.buf = strm->pcm_buf;
896 else
897 f.buf = strm->play_buf;
898
899 /* Call parent callback */
900 strm->play_cb(strm->user_data, &f);
901 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
902 pjmedia_zero_samples((pj_int16_t*)f.buf,
903 strm->param.samples_per_frame);
904 }
905
906 samples_got = strm->param.samples_per_frame /
907 strm->param.channel_count /
908 strm->resample_factor;
909
910 /* Need to convert channel count? */
911 if (strm->param.channel_count != 1) {
912 pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
913 (pj_int16_t*)f.buf,
914 strm->param.channel_count,
915 strm->param.samples_per_frame,
916 PJ_FALSE,
917 0);
918 }
919
920 /* Need to resample clock rate? */
921 if (strm->play_resample) {
922 unsigned resampled = 0;
923
924 while (resampled < samples_got)
925 {
926 pjmedia_resample_run(strm->play_resample,
927 strm->pcm_buf +
928 resampled * strm->resample_factor,
929 &strm->play_buf[resampled]);
930 resampled += 80;
931 }
932 }
933
934 strm->play_buf_len = samples_got;
935 strm->play_buf_start = 0;
936 }
937
938 unsigned tmp;
939
940 tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
941 pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
942 &strm->play_buf[strm->play_buf_start],
943 tmp);
944 buffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
945 samples_processed += tmp;
946 strm->play_buf_len -= tmp;
947 strm->play_buf_start += tmp;
948 }
949
950 /* Set the buffer */
951 buf->SetPayloadPtr(buffer);
952}
953
954/****************************************************************************
955 * Internal VAS callbacks for non-PCM format
956 */
957
958static void RecCb(CVoIPDataBuffer *buf, void *user_data)
959{
960 struct vas_stream *strm = (struct vas_stream*) user_data;
961 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
962 TPtr8 buffer(0, 0, 0);
963
964 /* Get the buffer */
965 buf->GetPayloadPtr(buffer);
966
967 switch(strm->param.ext_fmt.id) {
968 case PJMEDIA_FORMAT_AMR:
969 {
970 const pj_uint8_t *p = (const pj_uint8_t*)buffer.Ptr() + 1;
971 unsigned len = buffer.Length() - 1;
972
973 pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160);
974 if (frame->samples_cnt == strm->param.samples_per_frame) {
975 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
976 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
977 frame->samples_cnt = 0;
978 frame->subframe_cnt = 0;
979 }
980 }
981 break;
982
983 case PJMEDIA_FORMAT_G729:
984 {
985 /* Check if we got a normal or SID frame. */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000986 if (buffer[0] != 0) {
Nanang Izzuddind687a502009-06-30 13:37:26 +0000987 enum { NORMAL_LEN = 22, SID_LEN = 8 };
988 TBitStream *bitstream = (TBitStream*)strm->strm_data;
989 unsigned src_len = buffer.Length()- 2;
990
991 pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000992
Nanang Izzuddind687a502009-06-30 13:37:26 +0000993 const TDesC8& p = bitstream->CompressG729Frame(
994 buffer.Right(src_len),
995 src_len == SID_LEN);
996
997 pjmedia_frame_ext_append_subframe(frame, p.Ptr(),
998 p.Length() << 3, 80);
999 } else { /* We got null frame. */
1000 pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80);
1001 }
1002
1003 if (frame->samples_cnt == strm->param.samples_per_frame) {
1004 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1005 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1006 frame->samples_cnt = 0;
1007 frame->subframe_cnt = 0;
1008 }
1009 }
1010 break;
1011
1012 case PJMEDIA_FORMAT_ILBC:
1013 {
1014 unsigned samples_got;
1015
1016 samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
1017
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001018 /* Check if we got a normal or SID frame. */
1019 if (buffer[0] != 0) {
Nanang Izzuddind687a502009-06-30 13:37:26 +00001020 const pj_uint8_t *p = (const pj_uint8_t*)buffer.Ptr() + 2;
1021 unsigned len = buffer.Length() - 2;
1022
1023 pjmedia_frame_ext_append_subframe(frame, p, len << 3,
1024 samples_got);
1025 } else { /* We got null frame. */
1026 pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
1027 }
1028
1029 if (frame->samples_cnt == strm->param.samples_per_frame) {
1030 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1031 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1032 frame->samples_cnt = 0;
1033 frame->subframe_cnt = 0;
1034 }
1035 }
1036 break;
1037
1038 case PJMEDIA_FORMAT_PCMU:
1039 case PJMEDIA_FORMAT_PCMA:
1040 {
1041 unsigned samples_processed = 0;
1042
1043 /* Make sure it is normal frame. */
1044 pj_assert(buffer[0] == 1 && buffer[1] == 0);
1045
1046 /* Detect the recorder G.711 frame size, player frame size will
1047 * follow this recorder frame size.
1048 */
1049 if (vas_g711_frame_len == 0) {
1050 vas_g711_frame_len = buffer.Length() < 160? 80 : 160;
1051 TRACE_((THIS_FILE, "Detected VAS G.711 frame size = %u samples",
1052 vas_g711_frame_len));
1053 }
1054
1055 /* Convert VAS buffer format into pjmedia_frame_ext. Whenever
1056 * samples count in the frame is equal to stream's samples per
1057 * frame, call parent stream callback.
1058 */
1059 while (samples_processed < vas_g711_frame_len) {
1060 unsigned tmp;
1061 const pj_uint8_t *pb = (const pj_uint8_t*)buffer.Ptr() +
1062 2 + samples_processed;
1063
1064 tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
1065 vas_g711_frame_len - samples_processed);
1066
1067 pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
1068 samples_processed += tmp;
1069
1070 if (frame->samples_cnt == strm->param.samples_per_frame) {
1071 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1072 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1073 frame->samples_cnt = 0;
1074 frame->subframe_cnt = 0;
1075 }
1076 }
1077 }
1078 break;
1079
1080 default:
1081 break;
1082 }
1083}
1084
1085static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
1086{
1087 struct vas_stream *strm = (struct vas_stream*) user_data;
1088 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
1089 TPtr8 buffer(0, 0, 0);
1090
1091 /* Get the buffer */
1092 buf->GetPayloadPtr(buffer);
1093
1094 /* Init buffer attributes and header. */
1095 buffer.Zero();
1096
1097 switch(strm->param.ext_fmt.id) {
1098 case PJMEDIA_FORMAT_AMR:
1099 {
1100 if (frame->samples_cnt == 0) {
1101 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1102 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1103 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1104 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1105 }
1106
1107 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1108 pjmedia_frame_ext_subframe *sf;
1109 unsigned samples_cnt;
1110
1111 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1112 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1113
1114 if (sf->data && sf->bitlen) {
1115 /* AMR header for VAS is one byte, the format (may be!):
1116 * 0xxxxy00, where xxxx:frame type, y:not sure.
1117 */
1118 unsigned len = (sf->bitlen+7)>>3;
1119 enum {SID_FT = 8 };
1120 pj_uint8_t amr_header = 4, ft = SID_FT;
1121
1122 if (len >= pjmedia_codec_amrnb_framelen[0])
1123 ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
1124
1125 amr_header |= ft << 3;
1126 buffer.Append(amr_header);
1127
1128 buffer.Append((TUint8*)sf->data, len);
1129 } else {
Nanang Izzuddin308d5332010-03-04 15:47:25 +00001130 enum {NO_DATA_FT = 15 };
1131 pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
1132
1133 buffer.Append(amr_header);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001134 }
1135
1136 pjmedia_frame_ext_pop_subframes(frame, 1);
1137
1138 } else { /* PJMEDIA_FRAME_TYPE_NONE */
Nanang Izzuddin308d5332010-03-04 15:47:25 +00001139 enum {NO_DATA_FT = 15 };
1140 pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
1141
1142 buffer.Append(amr_header);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001143
1144 frame->samples_cnt = 0;
1145 frame->subframe_cnt = 0;
1146 }
1147 }
1148 break;
1149
1150 case PJMEDIA_FORMAT_G729:
1151 {
1152 if (frame->samples_cnt == 0) {
1153 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1154 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1155 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1156 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1157 }
1158
1159 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1160 pjmedia_frame_ext_subframe *sf;
1161 unsigned samples_cnt;
1162
1163 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1164 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1165
1166 if (sf->data && sf->bitlen) {
1167 enum { NORMAL_LEN = 10, SID_LEN = 2 };
1168 pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
1169 TBitStream *bitstream = (TBitStream*)strm->strm_data;
1170 const TPtrC8 src(sf->data, sf->bitlen>>3);
1171 const TDesC8 &dst = bitstream->ExpandG729Frame(src,
1172 sid_frame);
1173 if (sid_frame) {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001174 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001175 buffer.Append(0);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001176 } else {
1177 buffer.Append(1);
1178 buffer.Append(0);
1179 }
1180 buffer.Append(dst);
1181 } else {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001182 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001183 buffer.Append(0);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001184
1185 buffer.AppendFill(0, 22);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001186 }
1187
1188 pjmedia_frame_ext_pop_subframes(frame, 1);
1189
1190 } else { /* PJMEDIA_FRAME_TYPE_NONE */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001191 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001192 buffer.Append(0);
1193
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001194 buffer.AppendFill(0, 22);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001195 }
1196 }
1197 break;
1198
1199 case PJMEDIA_FORMAT_ILBC:
1200 {
1201 if (frame->samples_cnt == 0) {
1202 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1203 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1204 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1205 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1206 }
1207
1208 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1209 pjmedia_frame_ext_subframe *sf;
1210 unsigned samples_cnt;
1211
1212 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1213 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1214
1215 pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
1216 samples_cnt == 160) ||
1217 (strm->param.ext_fmt.bitrate != 15200 &&
1218 samples_cnt == 240));
1219
1220 if (sf->data && sf->bitlen) {
1221 buffer.Append(1);
1222 buffer.Append(0);
1223 buffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1224 } else {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001225 unsigned frame_len;
1226
1227 buffer.Append(1);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001228 buffer.Append(0);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001229
1230 /* VAS iLBC frame is 20ms or 30ms */
1231 frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
1232 buffer.AppendFill(0, frame_len);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001233 }
1234
1235 pjmedia_frame_ext_pop_subframes(frame, 1);
1236
1237 } else { /* PJMEDIA_FRAME_TYPE_NONE */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001238
1239 unsigned frame_len;
1240
1241 buffer.Append(1);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001242 buffer.Append(0);
1243
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001244 /* VAS iLBC frame is 20ms or 30ms */
1245 frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
1246 buffer.AppendFill(0, frame_len);
1247
Nanang Izzuddind687a502009-06-30 13:37:26 +00001248 }
1249 }
1250 break;
1251
1252 case PJMEDIA_FORMAT_PCMU:
1253 case PJMEDIA_FORMAT_PCMA:
1254 {
1255 unsigned samples_ready = 0;
1256 unsigned samples_req = vas_g711_frame_len;
1257
1258 /* Assume frame size is 10ms if frame size hasn't been known. */
1259 if (samples_req == 0)
1260 samples_req = 80;
1261
1262 buffer.Append(1);
1263 buffer.Append(0);
1264
1265 /* Call parent stream callback to get samples to play. */
1266 while (samples_ready < samples_req) {
1267 if (frame->samples_cnt == 0) {
1268 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1269 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1270 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1271 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1272 }
1273
1274 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1275 pjmedia_frame_ext_subframe *sf;
1276 unsigned samples_cnt;
1277
1278 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1279 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1280 if (sf->data && sf->bitlen) {
1281 buffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1282 } else {
1283 pj_uint8_t silc;
1284 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1285 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1286 buffer.AppendFill(silc, samples_cnt);
1287 }
1288 samples_ready += samples_cnt;
1289
1290 pjmedia_frame_ext_pop_subframes(frame, 1);
1291
1292 } else { /* PJMEDIA_FRAME_TYPE_NONE */
1293 pj_uint8_t silc;
1294
1295 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1296 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1297 buffer.AppendFill(silc, samples_req - samples_ready);
1298
1299 samples_ready = samples_req;
1300 frame->samples_cnt = 0;
1301 frame->subframe_cnt = 0;
1302 }
1303 }
1304 }
1305 break;
1306
1307 default:
1308 break;
1309 }
1310
1311 /* Set the buffer */
1312 buf->SetPayloadPtr(buffer);
1313}
1314
1315
1316/****************************************************************************
1317 * Factory operations
1318 */
1319
1320/*
1321 * C compatible declaration of VAS factory.
1322 */
1323PJ_BEGIN_DECL
1324PJ_DECL(pjmedia_aud_dev_factory*)pjmedia_symb_vas_factory(pj_pool_factory *pf);
1325PJ_END_DECL
1326
1327/*
1328 * Init VAS audio driver.
1329 */
1330PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_symb_vas_factory(pj_pool_factory *pf)
1331{
1332 struct vas_factory *f;
1333 pj_pool_t *pool;
1334
1335 pool = pj_pool_create(pf, "VAS", 1000, 1000, NULL);
1336 f = PJ_POOL_ZALLOC_T(pool, struct vas_factory);
1337 f->pf = pf;
1338 f->pool = pool;
1339 f->base.op = &factory_op;
1340
1341 return &f->base;
1342}
1343
1344/* API: init factory */
1345static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
1346{
1347 struct vas_factory *af = (struct vas_factory*)f;
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001348 CVoIPUtilityFactory *vas_factory_;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001349 CVoIPAudioUplinkStream *vas_uplink;
1350 CVoIPAudioDownlinkStream *vas_dnlink;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001351 RArray<TVoIPCodecFormat> uplink_formats, dnlink_formats;
1352 unsigned ext_fmt_cnt = 0;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001353 TVersion vas_version(1, 0, 0); /* Not really used at this time */
Nanang Izzuddind687a502009-06-30 13:37:26 +00001354 TInt err;
1355
1356 pj_ansi_strcpy(af->dev_info.name, "S60 VAS");
1357 af->dev_info.default_samples_per_sec = 8000;
1358 af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
1359 //PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
1360 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
1361 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
1362 PJMEDIA_AUD_DEV_CAP_VAD |
1363 PJMEDIA_AUD_DEV_CAP_CNG;
1364 af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
1365 PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1366 af->dev_info.input_count = 1;
1367 af->dev_info.output_count = 1;
1368 af->dev_info.ext_fmt_cnt = 0;
1369
1370 /* Enumerate supported formats */
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001371 err = CVoIPUtilityFactory::CreateFactory(vas_factory_);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001372 if (err != KErrNone)
1373 goto on_error;
1374
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001375 /* On VAS 2.0, uplink & downlink stream should be instantiated before
1376 * querying formats.
1377 */
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001378 err = vas_factory_->CreateUplinkStream(vas_version,
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001379 CVoIPUtilityFactory::EVoIPCall,
1380 vas_uplink);
1381 if (err != KErrNone)
1382 goto on_error;
1383
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001384 err = vas_factory_->CreateDownlinkStream(vas_version,
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001385 CVoIPUtilityFactory::EVoIPCall,
1386 vas_dnlink);
1387 if (err != KErrNone)
1388 goto on_error;
1389
Nanang Izzuddind687a502009-06-30 13:37:26 +00001390 uplink_formats.Reset();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001391 err = vas_factory_->GetSupportedUplinkFormats(uplink_formats);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001392 if (err != KErrNone)
1393 goto on_error;
1394
1395 dnlink_formats.Reset();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001396 err = vas_factory_->GetSupportedDownlinkFormats(dnlink_formats);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001397 if (err != KErrNone)
1398 goto on_error;
1399
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001400 /* Free the streams, they are just used for querying formats */
1401 delete vas_uplink;
1402 vas_uplink = NULL;
1403 delete vas_dnlink;
1404 vas_dnlink = NULL;
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001405 delete vas_factory_;
1406 vas_factory_ = NULL;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001407
Nanang Izzuddind687a502009-06-30 13:37:26 +00001408 for (TInt i = 0; i < dnlink_formats.Count(); i++) {
1409 /* Format must be supported by both downlink & uplink. */
1410 if (uplink_formats.Find(dnlink_formats[i]) == KErrNotFound)
1411 continue;
1412
1413 switch (dnlink_formats[i]) {
1414 case EAMR_NB:
1415 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_AMR;
1416 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 7400;
1417 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
1418 break;
1419
1420 case EG729:
1421 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_G729;
1422 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 8000;
1423 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1424 break;
1425
1426 case EILBC:
1427 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
1428 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 13333;
1429 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
1430 break;
1431
1432 case EG711:
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001433#if PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION==2
1434 case EG711_10MS:
1435#endif
Nanang Izzuddind687a502009-06-30 13:37:26 +00001436 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
1437 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
1438 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1439 ++ext_fmt_cnt;
1440 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
1441 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
1442 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1443 break;
1444
1445 default:
1446 continue;
1447 }
1448
1449 ++ext_fmt_cnt;
1450 }
1451
1452 af->dev_info.ext_fmt_cnt = ext_fmt_cnt;
1453
1454 uplink_formats.Close();
1455 dnlink_formats.Close();
1456
1457 PJ_LOG(3, (THIS_FILE, "VAS initialized"));
1458
1459 return PJ_SUCCESS;
1460
1461on_error:
1462 return PJ_RETURN_OS_ERROR(err);
1463}
1464
1465/* API: destroy factory */
1466static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
1467{
1468 struct vas_factory *af = (struct vas_factory*)f;
1469 pj_pool_t *pool = af->pool;
1470
1471 af->pool = NULL;
1472 pj_pool_release(pool);
1473
1474 PJ_LOG(3, (THIS_FILE, "VAS destroyed"));
1475
1476 return PJ_SUCCESS;
1477}
1478
1479/* API: get number of devices */
1480static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
1481{
1482 PJ_UNUSED_ARG(f);
1483 return 1;
1484}
1485
1486/* API: get device info */
1487static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
1488 unsigned index,
1489 pjmedia_aud_dev_info *info)
1490{
1491 struct vas_factory *af = (struct vas_factory*)f;
1492
1493 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1494
1495 pj_memcpy(info, &af->dev_info, sizeof(*info));
1496
1497 return PJ_SUCCESS;
1498}
1499
1500/* API: create default device parameter */
1501static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
1502 unsigned index,
1503 pjmedia_aud_param *param)
1504{
1505 struct vas_factory *af = (struct vas_factory*)f;
1506
1507 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1508
1509 pj_bzero(param, sizeof(*param));
1510 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1511 param->rec_id = index;
1512 param->play_id = index;
1513 param->clock_rate = af->dev_info.default_samples_per_sec;
1514 param->channel_count = 1;
1515 param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
1516 param->bits_per_sample = BITS_PER_SAMPLE;
1517 param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1518 param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1519
1520 return PJ_SUCCESS;
1521}
1522
1523
1524/* API: create stream */
1525static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1526 const pjmedia_aud_param *param,
1527 pjmedia_aud_rec_cb rec_cb,
1528 pjmedia_aud_play_cb play_cb,
1529 void *user_data,
1530 pjmedia_aud_stream **p_aud_strm)
1531{
1532 struct vas_factory *af = (struct vas_factory*)f;
1533 pj_pool_t *pool;
1534 struct vas_stream *strm;
1535
1536 CPjAudioSetting vas_setting;
1537 PjAudioCallback vas_rec_cb;
1538 PjAudioCallback vas_play_cb;
1539
1540 /* Can only support 16bits per sample */
1541 PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
1542
1543 /* Supported clock rates:
1544 * - for non-PCM format: 8kHz
1545 * - for PCM format: 8kHz and 16kHz
1546 */
1547 PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
1548 (param->clock_rate == 16000 &&
1549 param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1550 PJ_EINVAL);
1551
1552 /* Supported channels number:
1553 * - for non-PCM format: mono
1554 * - for PCM format: mono and stereo
1555 */
1556 PJ_ASSERT_RETURN(param->channel_count == 1 ||
1557 (param->channel_count == 2 &&
1558 param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1559 PJ_EINVAL);
1560
1561 /* Create and Initialize stream descriptor */
1562 pool = pj_pool_create(af->pf, "vas-dev", 1000, 1000, NULL);
1563 PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
1564
1565 strm = PJ_POOL_ZALLOC_T(pool, struct vas_stream);
1566 strm->pool = pool;
1567 strm->param = *param;
1568
1569 if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
1570 strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
1571
1572 /* Set audio engine fourcc. */
1573 switch(strm->param.ext_fmt.id) {
1574 case PJMEDIA_FORMAT_L16:
1575#ifdef USE_NATIVE_PCM
1576 vas_setting.format = EPCM16;
1577#else
1578 vas_setting.format = EG711;
1579#endif
1580 break;
1581 case PJMEDIA_FORMAT_PCMU:
1582 case PJMEDIA_FORMAT_PCMA:
1583 vas_setting.format = EG711;
1584 break;
1585 case PJMEDIA_FORMAT_AMR:
1586 vas_setting.format = EAMR_NB;
1587 break;
1588 case PJMEDIA_FORMAT_G729:
1589 vas_setting.format = EG729;
1590 break;
1591 case PJMEDIA_FORMAT_ILBC:
1592 vas_setting.format = EILBC;
1593 break;
1594 default:
1595 vas_setting.format = ENULL;
1596 break;
1597 }
1598
1599 /* Set audio engine mode. */
1600 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1601 {
1602#ifdef USE_NATIVE_PCM
1603 vas_setting.mode = 0;
1604#else
1605 vas_setting.mode = CVoIPFormatIntfc::EG711uLaw;
1606#endif
1607 }
1608 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
1609 {
1610 vas_setting.mode = strm->param.ext_fmt.bitrate;
1611 }
1612 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU)
1613 {
1614 vas_setting.mode = CVoIPFormatIntfc::EG711uLaw;
1615 }
1616 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA)
1617 {
1618 vas_setting.mode = CVoIPFormatIntfc::EG711ALaw;
1619 }
1620 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC)
1621 {
1622 if (strm->param.ext_fmt.bitrate == 15200)
1623 vas_setting.mode = CVoIPFormatIntfc::EiLBC20mSecFrame;
1624 else
1625 vas_setting.mode = CVoIPFormatIntfc::EiLBC30mSecFrame;
1626 } else {
1627 vas_setting.mode = 0;
1628 }
1629
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001630 /* Disable VAD on L16, G711, iLBC, and also G729 (G729's SID
1631 * potentially cause noise?).
Nanang Izzuddind687a502009-06-30 13:37:26 +00001632 */
1633 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1634 strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1635 strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001636 strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC ||
Nanang Izzuddind687a502009-06-30 13:37:26 +00001637 strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
1638 {
1639 vas_setting.vad = EFalse;
1640 } else {
1641 vas_setting.vad = strm->param.ext_fmt.vad;
1642 }
1643
1644 /* Set other audio engine attributes. */
1645 vas_setting.plc = strm->param.plc_enabled;
1646 vas_setting.cng = vas_setting.vad;
1647 vas_setting.loudspk =
1648 strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1649
1650 /* Set audio engine callbacks. */
1651 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
1652#ifdef USE_NATIVE_PCM
1653 vas_play_cb = &PlayCbPcm2;
1654 vas_rec_cb = &RecCbPcm2;
1655#else
1656 vas_play_cb = &PlayCbPcm;
1657 vas_rec_cb = &RecCbPcm;
1658#endif
1659 } else {
1660 vas_play_cb = &PlayCb;
1661 vas_rec_cb = &RecCb;
1662 }
1663
1664 strm->rec_cb = rec_cb;
1665 strm->play_cb = play_cb;
1666 strm->user_data = user_data;
1667 strm->resample_factor = strm->param.clock_rate / 8000;
1668
1669 /* play_buf size is samples per frame scaled in to 8kHz mono. */
1670 strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
1671 pool,
1672 (strm->param.samples_per_frame /
1673 strm->resample_factor /
1674 strm->param.channel_count) << 1);
1675 strm->play_buf_len = 0;
1676 strm->play_buf_start = 0;
1677
1678 /* rec_buf size is samples per frame scaled in to 8kHz mono. */
1679 strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(
1680 pool,
1681 (strm->param.samples_per_frame /
1682 strm->resample_factor /
1683 strm->param.channel_count) << 1);
1684 strm->rec_buf_len = 0;
1685
1686 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1687 TBitStream *g729_bitstream = new TBitStream;
1688
1689 PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
1690 strm->strm_data = (void*)g729_bitstream;
1691 }
1692
1693 /* Init resampler when format is PCM and clock rate is not 8kHz */
1694 if (strm->param.clock_rate != 8000 &&
1695 strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1696 {
1697 pj_status_t status;
1698
1699 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1700 /* Create resample for recorder */
1701 status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1702 8000,
1703 strm->param.clock_rate,
1704 80,
1705 &strm->rec_resample);
1706 if (status != PJ_SUCCESS)
1707 return status;
1708 }
1709
1710 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1711 /* Create resample for player */
1712 status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1713 strm->param.clock_rate,
1714 8000,
1715 80 * strm->resample_factor,
1716 &strm->play_resample);
1717 if (status != PJ_SUCCESS)
1718 return status;
1719 }
1720 }
1721
1722 /* Create PCM buffer, when the clock rate is not 8kHz or not mono */
1723 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
1724 (strm->resample_factor > 1 || strm->param.channel_count != 1))
1725 {
1726 strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
1727 strm->param.samples_per_frame << 1);
1728 }
1729
1730
1731 /* Create the audio engine. */
1732 TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
1733 vas_rec_cb, vas_play_cb,
1734 strm, vas_setting));
1735 if (err != KErrNone) {
1736 pj_pool_release(pool);
1737 return PJ_RETURN_OS_ERROR(err);
1738 }
1739
1740 /* Apply output volume setting if specified */
1741 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1742 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1743 &param->output_vol);
1744 }
1745
1746 /* Done */
1747 strm->base.op = &stream_op;
1748 *p_aud_strm = &strm->base;
1749
1750 return PJ_SUCCESS;
1751}
1752
1753/* API: Get stream info. */
1754static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1755 pjmedia_aud_param *pi)
1756{
1757 struct vas_stream *strm = (struct vas_stream*)s;
1758
1759 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1760
1761 pj_memcpy(pi, &strm->param, sizeof(*pi));
1762
1763 /* Update the output volume setting */
1764 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1765 &pi->output_vol) == PJ_SUCCESS)
1766 {
1767 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1768 }
1769
1770 return PJ_SUCCESS;
1771}
1772
1773/* API: get capability */
1774static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1775 pjmedia_aud_dev_cap cap,
1776 void *pval)
1777{
1778 struct vas_stream *strm = (struct vas_stream*)s;
1779 pj_status_t status = PJ_ENOTSUP;
1780
1781 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1782
1783 switch (cap) {
1784 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1785 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1786 *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
1787 status = PJ_SUCCESS;
1788 }
1789 break;
1790
1791 /* There is a case that GetMaxGain() stucks, e.g: in N95. */
1792 /*
1793 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1794 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1795 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1796
1797 TInt max_gain = strm->engine->GetMaxGain();
1798 TInt gain = strm->engine->GetGain();
1799
1800 if (max_gain > 0 && gain >= 0) {
1801 *(unsigned*)pval = gain * 100 / max_gain;
1802 status = PJ_SUCCESS;
1803 } else {
1804 status = PJMEDIA_EAUD_NOTREADY;
1805 }
1806 }
1807 break;
1808 */
1809
1810 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1811 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1812 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1813
1814 TInt max_vol = strm->engine->GetMaxVolume();
1815 TInt vol = strm->engine->GetVolume();
1816
1817 if (max_vol > 0 && vol >= 0) {
1818 *(unsigned*)pval = vol * 100 / max_vol;
1819 status = PJ_SUCCESS;
1820 } else {
1821 status = PJMEDIA_EAUD_NOTREADY;
1822 }
1823 }
1824 break;
1825 default:
1826 break;
1827 }
1828
1829 return status;
1830}
1831
1832/* API: set capability */
1833static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1834 pjmedia_aud_dev_cap cap,
1835 const void *pval)
1836{
1837 struct vas_stream *strm = (struct vas_stream*)s;
1838 pj_status_t status = PJ_ENOTSUP;
1839
1840 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1841
1842 switch (cap) {
1843 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1844 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1845 pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
1846 TInt err;
1847
1848 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1849
1850 switch (r) {
1851 case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
1852 case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
1853 err = strm->engine->ActivateSpeaker(EFalse);
1854 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1855 break;
1856 case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
1857 err = strm->engine->ActivateSpeaker(ETrue);
1858 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1859 break;
1860 default:
1861 status = PJ_EINVAL;
1862 break;
1863 }
1864 if (status == PJ_SUCCESS)
1865 strm->param.output_route = r;
1866 }
1867 break;
1868
1869 /* There is a case that GetMaxGain() stucks, e.g: in N95. */
1870 /*
1871 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1872 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1873 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1874
1875 TInt max_gain = strm->engine->GetMaxGain();
1876 if (max_gain > 0) {
1877 TInt gain, err;
1878
1879 gain = *(unsigned*)pval * max_gain / 100;
1880 err = strm->engine->SetGain(gain);
1881 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1882 } else {
1883 status = PJMEDIA_EAUD_NOTREADY;
1884 }
1885 if (status == PJ_SUCCESS)
1886 strm->param.input_vol = *(unsigned*)pval;
1887 }
1888 break;
1889 */
1890
1891 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1892 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1893 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1894
1895 TInt max_vol = strm->engine->GetMaxVolume();
1896 if (max_vol > 0) {
1897 TInt vol, err;
1898
1899 vol = *(unsigned*)pval * max_vol / 100;
1900 err = strm->engine->SetVolume(vol);
1901 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1902 } else {
1903 status = PJMEDIA_EAUD_NOTREADY;
1904 }
1905 if (status == PJ_SUCCESS)
1906 strm->param.output_vol = *(unsigned*)pval;
1907 }
1908 break;
1909 default:
1910 break;
1911 }
1912
1913 return status;
1914}
1915
1916/* API: Start stream. */
1917static pj_status_t stream_start(pjmedia_aud_stream *strm)
1918{
1919 struct vas_stream *stream = (struct vas_stream*)strm;
1920
1921 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1922
1923 if (stream->engine) {
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001924 enum { VAS_WAIT_START = 2000 }; /* in msecs */
1925 TTime start, now;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001926 TInt err = stream->engine->Start();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001927
Nanang Izzuddind687a502009-06-30 13:37:26 +00001928 if (err != KErrNone)
1929 return PJ_RETURN_OS_ERROR(err);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001930
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001931 /* Perform synchronous start, timeout after VAS_WAIT_START ms */
1932 start.UniversalTime();
1933 do {
1934 pj_symbianos_poll(-1, 100);
1935 now.UniversalTime();
1936 } while (!stream->engine->IsStarted() &&
1937 (now.MicroSecondsFrom(start) < VAS_WAIT_START * 1000));
1938
1939 if (stream->engine->IsStarted())
1940 return PJ_SUCCESS;
1941 else
1942 return PJ_ETIMEDOUT;
1943 }
1944
1945 return PJ_EINVALIDOP;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001946}
1947
1948/* API: Stop stream. */
1949static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1950{
1951 struct vas_stream *stream = (struct vas_stream*)strm;
1952
1953 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1954
1955 if (stream->engine) {
1956 stream->engine->Stop();
1957 }
1958
1959 return PJ_SUCCESS;
1960}
1961
1962
1963/* API: Destroy stream. */
1964static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1965{
1966 struct vas_stream *stream = (struct vas_stream*)strm;
1967
1968 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1969
1970 stream_stop(strm);
1971
1972 delete stream->engine;
1973 stream->engine = NULL;
1974
1975 if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1976 TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
1977 stream->strm_data = NULL;
1978 delete g729_bitstream;
1979 }
1980
1981 pj_pool_t *pool;
1982 pool = stream->pool;
1983 if (pool) {
1984 stream->pool = NULL;
1985 pj_pool_release(pool);
1986 }
1987
1988 return PJ_SUCCESS;
1989}
1990
1991#endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
1992