blob: ae9b43c643a5637652758d6f7d39f4b14c521f10 [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
367 delete iVoIPDnlink;
368 delete iVoIPUplink;
369 delete iFactory;
370
371 TRACE_((THIS_FILE, "Sound device destroyed"));
372}
373
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000374TBool CPjAudioEngine::IsStarted()
375{
376 return ((((parentStrm_->param.dir & PJMEDIA_DIR_CAPTURE) == 0) ||
377 up_state_ == STATE_STREAMING) &&
378 (((parentStrm_->param.dir & PJMEDIA_DIR_PLAYBACK) == 0) ||
379 dn_state_ == STATE_STREAMING));
380}
381
Nanang Izzuddind687a502009-06-30 13:37:26 +0000382TInt CPjAudioEngine::InitPlay()
383{
384 TInt err;
385
386 pj_assert(iVoIPDnlink);
387
388 err = iVoIPDnlink->SetFormat(setting_.format, dec_fmt_if);
389 if (err != KErrNone)
390 return err;
391
392 err = dec_fmt_if->SetObserver(*this);
393 if (err != KErrNone)
394 return err;
395
396 return iVoIPDnlink->Open(*this);
397}
398
399TInt CPjAudioEngine::InitRec()
400{
401 TInt err;
402
403 pj_assert(iVoIPUplink);
404
405 err = iVoIPUplink->SetFormat(setting_.format, enc_fmt_if);
406 if (err != KErrNone)
407 return err;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000408
409 err = enc_fmt_if->SetObserver(*this);
410 if (err != KErrNone)
411 return err;
412
Nanang Izzuddind687a502009-06-30 13:37:26 +0000413 return iVoIPUplink->Open(*this);
414}
415
416TInt CPjAudioEngine::StartPlay()
417{
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000418 TInt err;
419
Nanang Izzuddind687a502009-06-30 13:37:26 +0000420 pj_assert(iVoIPDnlink);
421 pj_assert(dn_state_ == STATE_READY);
422
423 /* Configure specific codec setting */
424 switch (setting_.format) {
425 case EG711:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000426 {
427 CVoIPG711DecoderIntfc *g711dec_if = (CVoIPG711DecoderIntfc*)
428 dec_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000429 err = g711dec_if->SetMode((CVoIPFormatIntfc::TG711CodecMode)
430 setting_.mode);
431 pj_assert(err == KErrNone);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000432 }
433 break;
434
435 case EILBC:
436 {
437 CVoIPILBCDecoderIntfc *ilbcdec_if = (CVoIPILBCDecoderIntfc*)
438 dec_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000439 err = ilbcdec_if->SetMode((CVoIPFormatIntfc::TILBCCodecMode)
440 setting_.mode);
441 pj_assert(err == KErrNone);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000442 }
443 break;
444
445 default:
446 break;
447 }
448
449 /* Configure audio routing */
450 ActivateSpeaker(setting_.loudspk);
451
452 /* Start player */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000453 err = iVoIPDnlink->Start();
Nanang Izzuddind687a502009-06-30 13:37:26 +0000454
455 if (err == KErrNone) {
456 dn_state_ = STATE_STREAMING;
457 TRACE_((THIS_FILE, "Downlink started"));
458 } else {
459 snd_perror("Failed starting downlink", err);
460 }
461
462 return err;
463}
464
465TInt CPjAudioEngine::StartRec()
466{
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000467 TInt err;
468
Nanang Izzuddind687a502009-06-30 13:37:26 +0000469 pj_assert(iVoIPUplink);
470 pj_assert(up_state_ == STATE_READY);
471
Nanang Izzuddind687a502009-06-30 13:37:26 +0000472 /* Configure specific codec setting */
473 switch (setting_.format) {
474 case EG711:
Nanang Izzuddind687a502009-06-30 13:37:26 +0000475 {
476 CVoIPG711EncoderIntfc *g711enc_if = (CVoIPG711EncoderIntfc*)
477 enc_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000478 err = g711enc_if->SetMode((CVoIPFormatIntfc::TG711CodecMode)
479 setting_.mode);
480 pj_assert(err == KErrNone);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000481 }
482 break;
483
484 case EILBC:
485 {
486 CVoIPILBCEncoderIntfc *ilbcenc_if = (CVoIPILBCEncoderIntfc*)
487 enc_fmt_if;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000488 err = ilbcenc_if->SetMode((CVoIPFormatIntfc::TILBCCodecMode)
489 setting_.mode);
490 pj_assert(err == KErrNone);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000491 }
492 break;
493
Nanang Izzuddin06839e72010-01-27 11:48:31 +0000494 case EAMR_NB:
495 enc_fmt_if->SetBitRate(setting_.mode);
496 break;
497
Nanang Izzuddind687a502009-06-30 13:37:26 +0000498 default:
499 break;
500 }
501
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000502 /* Configure general codec setting */
503 enc_fmt_if->SetVAD(setting_.vad);
504
Nanang Izzuddind687a502009-06-30 13:37:26 +0000505 /* Start recorder */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000506 err = iVoIPUplink->Start();
Nanang Izzuddind687a502009-06-30 13:37:26 +0000507
508 if (err == KErrNone) {
509 up_state_ = STATE_STREAMING;
510 TRACE_((THIS_FILE, "Uplink started"));
511 } else {
512 snd_perror("Failed starting uplink", err);
513 }
514
515 return err;
516}
517
518TInt CPjAudioEngine::Start()
519{
520 TInt err = KErrNone;
521
522 if (iVoIPDnlink) {
523 switch(dn_state_) {
524 case STATE_READY:
525 err = StartPlay();
526 break;
527 case STATE_NULL:
528 err = InitPlay();
529 if (err != KErrNone)
530 return err;
531 dn_state_ = STATE_STARTING;
532 break;
533 default:
534 break;
535 }
536 }
537
538 if (iVoIPUplink) {
539 switch(up_state_) {
540 case STATE_READY:
541 err = StartRec();
542 break;
543 case STATE_NULL:
544 err = InitRec();
545 if (err != KErrNone)
546 return err;
547 up_state_ = STATE_STARTING;
548 break;
549 default:
550 break;
551 }
552 }
553
554 return err;
555}
556
557void CPjAudioEngine::Stop()
558{
559 if (iVoIPDnlink) {
560 switch(dn_state_) {
561 case STATE_STREAMING:
562 iVoIPDnlink->Stop();
563 dn_state_ = STATE_READY;
564 break;
565 case STATE_STARTING:
566 dn_state_ = STATE_NULL;
567 break;
568 default:
569 break;
570 }
571 }
572
573 if (iVoIPUplink) {
574 switch(up_state_) {
575 case STATE_STREAMING:
576 iVoIPUplink->Stop();
577 up_state_ = STATE_READY;
578 break;
579 case STATE_STARTING:
580 up_state_ = STATE_NULL;
581 break;
582 default:
583 break;
584 }
585 }
586}
587
588
589TInt CPjAudioEngine::ActivateSpeaker(TBool active)
590{
591 TInt err = KErrNotSupported;
592
593 if (iVoIPDnlink) {
594 err = iVoIPDnlink->SetAudioDevice(active?
595 CVoIPAudioDownlinkStream::ELoudSpeaker :
596 CVoIPAudioDownlinkStream::EHandset);
597 TRACE_((THIS_FILE, "Loudspeaker turned %s", (active? "on":"off")));
598 }
599
600 return err;
601}
602
603// Callback from MVoIPDownlinkObserver
604void CPjAudioEngine::FillBuffer(const CVoIPAudioDownlinkStream& aSrc,
605 CVoIPDataBuffer* aBuffer)
606{
607 play_cb_(aBuffer, user_data_);
608 iVoIPDnlink->BufferFilled(aBuffer);
609}
610
611// Callback from MVoIPUplinkObserver
612void CPjAudioEngine::EmptyBuffer(const CVoIPAudioUplinkStream& aSrc,
613 CVoIPDataBuffer* aBuffer)
614{
615 rec_cb_(aBuffer, user_data_);
616 iVoIPUplink->BufferEmptied(aBuffer);
617}
618
619// Callback from MVoIPDownlinkObserver
620void CPjAudioEngine::Event(const CVoIPAudioDownlinkStream& /*aSrc*/,
621 TInt aEventType,
622 TInt aError)
623{
624 switch (aEventType) {
625 case MVoIPDownlinkObserver::KOpenComplete:
626 if (aError == KErrNone) {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000627 State last_state = dn_state_;
Nanang Izzuddind687a502009-06-30 13:37:26 +0000628
629 dn_state_ = STATE_READY;
630 TRACE_((THIS_FILE, "Downlink opened"));
631
632 if (last_state == STATE_STARTING)
633 StartPlay();
634 }
635 break;
636
637 case MVoIPDownlinkObserver::KDownlinkClosed:
638 dn_state_ = STATE_NULL;
639 TRACE_((THIS_FILE, "Downlink closed"));
640 break;
641
642 case MVoIPDownlinkObserver::KDownlinkError:
643 dn_state_ = STATE_READY;
644 snd_perror("Downlink problem", aError);
645 break;
646 default:
647 break;
648 }
649}
650
651// Callback from MVoIPUplinkObserver
652void CPjAudioEngine::Event(const CVoIPAudioUplinkStream& /*aSrc*/,
653 TInt aEventType,
654 TInt aError)
655{
656 switch (aEventType) {
657 case MVoIPUplinkObserver::KOpenComplete:
658 if (aError == KErrNone) {
659 State last_state = up_state_;
660
661 up_state_ = STATE_READY;
662 TRACE_((THIS_FILE, "Uplink opened"));
663
664 if (last_state == STATE_STARTING)
665 StartRec();
666 }
667 break;
668
669 case MVoIPUplinkObserver::KUplinkClosed:
670 up_state_ = STATE_NULL;
671 TRACE_((THIS_FILE, "Uplink closed"));
672 break;
673
674 case MVoIPUplinkObserver::KUplinkError:
675 up_state_ = STATE_READY;
676 snd_perror("Uplink problem", aError);
677 break;
678 default:
679 break;
680 }
681}
682
683// Callback from MVoIPFormatObserver
684void CPjAudioEngine::Event(const CVoIPFormatIntfc& /*aSrc*/,
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000685 TInt aEventType)
Nanang Izzuddind687a502009-06-30 13:37:26 +0000686{
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +0000687 snd_perror("Format event", aEventType);
Nanang Izzuddind687a502009-06-30 13:37:26 +0000688}
689
690/****************************************************************************
691 * Internal VAS callbacks for PCM format
692 */
693
694#ifdef USE_NATIVE_PCM
695
696static void RecCbPcm2(CVoIPDataBuffer *buf, void *user_data)
697{
698 struct vas_stream *strm = (struct vas_stream*) user_data;
699 TPtr8 buffer(0, 0, 0);
700 pj_int16_t *p_buf;
701 unsigned buf_len;
702
703 /* Get the buffer */
704 buf->GetPayloadPtr(buffer);
705
706 /* Call parent callback */
707 p_buf = (pj_int16_t*) buffer.Ptr();
708 buf_len = buffer.Length() >> 1;
709 while (buf_len) {
710 unsigned req;
711
712 req = strm->param.samples_per_frame - strm->rec_buf_len;
713 if (req > buf_len)
714 req = buf_len;
715 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_len, p_buf, req);
716 p_buf += req;
717 buf_len -= req;
718 strm->rec_buf_len += req;
719
720 if (strm->rec_buf_len >= strm->param.samples_per_frame) {
721 pjmedia_frame f;
722
723 f.buf = strm->rec_buf;
724 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
725 f.size = strm->param.samples_per_frame << 1;
726 strm->rec_cb(strm->user_data, &f);
727 strm->rec_buf_len = 0;
728 }
729 }
730}
731
732static void PlayCbPcm2(CVoIPDataBuffer *buf, void *user_data)
733{
734 struct vas_stream *strm = (struct vas_stream*) user_data;
735 TPtr8 buffer(0, 0, 0);
736 pjmedia_frame f;
737
738 /* Get the buffer */
739 buf->GetPayloadPtr(buffer);
740
741 /* Call parent callback */
742 f.buf = strm->play_buf;
743 f.size = strm->param.samples_per_frame << 1;
744 strm->play_cb(strm->user_data, &f);
745 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
746 pjmedia_zero_samples((pj_int16_t*)f.buf,
747 strm->param.samples_per_frame);
748 }
749 f.size = strm->param.samples_per_frame << 1;
750
751 /* Init buffer attributes and header. */
752 buffer.Zero();
753 buffer.Append((TUint8*)f.buf, f.size);
754
755 /* Set the buffer */
756 buf->SetPayloadPtr(buffer);
757}
758
759#else // not USE_NATIVE_PCM
760
761static void RecCbPcm(CVoIPDataBuffer *buf, void *user_data)
762{
763 struct vas_stream *strm = (struct vas_stream*) user_data;
764 TPtr8 buffer(0, 0, 0);
765
766 /* Get the buffer */
767 buf->GetPayloadPtr(buffer);
768
769 /* Buffer has to contain normal speech. */
770 pj_assert(buffer[0] == 1 && buffer[1] == 0);
771
772 /* Detect the recorder G.711 frame size, player frame size will follow
773 * this recorder frame size.
774 */
775 if (vas_g711_frame_len == 0) {
776 vas_g711_frame_len = buffer.Length() < 160? 80 : 160;
777 TRACE_((THIS_FILE, "Detected VAS G.711 frame size = %u samples",
778 vas_g711_frame_len));
779 }
780
781 /* Decode VAS buffer (coded in G.711) and put the PCM result into rec_buf.
782 * Whenever rec_buf is full, call parent stream callback.
783 */
784 unsigned samples_processed = 0;
785
786 while (samples_processed < vas_g711_frame_len) {
787 unsigned samples_to_process;
788 unsigned samples_req;
789
790 samples_to_process = vas_g711_frame_len - samples_processed;
791 samples_req = (strm->param.samples_per_frame /
792 strm->param.channel_count /
793 strm->resample_factor) -
794 strm->rec_buf_len;
795 if (samples_to_process > samples_req)
796 samples_to_process = samples_req;
797
798 pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
799 buffer.Ptr() + 2 + samples_processed,
800 samples_to_process);
801
802 strm->rec_buf_len += samples_to_process;
803 samples_processed += samples_to_process;
804
805 /* Buffer is full, time to call parent callback */
806 if (strm->rec_buf_len == strm->param.samples_per_frame /
807 strm->param.channel_count /
808 strm->resample_factor)
809 {
810 pjmedia_frame f;
811
812 /* Need to resample clock rate? */
813 if (strm->rec_resample) {
814 unsigned resampled = 0;
815
816 while (resampled < strm->rec_buf_len) {
817 pjmedia_resample_run(strm->rec_resample,
818 &strm->rec_buf[resampled],
819 strm->pcm_buf +
820 resampled * strm->resample_factor);
821 resampled += 80;
822 }
823 f.buf = strm->pcm_buf;
824 } else {
825 f.buf = strm->rec_buf;
826 }
827
828 /* Need to convert channel count? */
829 if (strm->param.channel_count != 1) {
830 pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
831 (pj_int16_t*)f.buf,
832 strm->param.channel_count,
833 strm->param.samples_per_frame /
834 strm->param.channel_count,
835 0);
836 }
837
838 /* Call parent callback */
839 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
840 f.size = strm->param.samples_per_frame << 1;
841 strm->rec_cb(strm->user_data, &f);
842 strm->rec_buf_len = 0;
843 }
844 }
845}
846
847#endif // USE_NATIVE_PCM
848
849static void PlayCbPcm(CVoIPDataBuffer *buf, void *user_data)
850{
851 struct vas_stream *strm = (struct vas_stream*) user_data;
852 unsigned g711_frame_len = vas_g711_frame_len;
853 TPtr8 buffer(0, 0, 0);
854
855 /* Get the buffer */
856 buf->GetPayloadPtr(buffer);
857
858 /* Init buffer attributes and header. */
859 buffer.Zero();
860 buffer.Append(1);
861 buffer.Append(0);
862
863 /* Assume frame size is 10ms if frame size hasn't been known. */
864 if (g711_frame_len == 0)
865 g711_frame_len = 80;
866
867 /* Call parent stream callback to get PCM samples to play,
868 * encode the PCM samples into G.711 and put it into VAS buffer.
869 */
870 unsigned samples_processed = 0;
871
872 while (samples_processed < g711_frame_len) {
873 /* Need more samples to play, time to call parent callback */
874 if (strm->play_buf_len == 0) {
875 pjmedia_frame f;
876 unsigned samples_got;
877
878 f.size = strm->param.samples_per_frame << 1;
879 if (strm->play_resample || strm->param.channel_count != 1)
880 f.buf = strm->pcm_buf;
881 else
882 f.buf = strm->play_buf;
883
884 /* Call parent callback */
885 strm->play_cb(strm->user_data, &f);
886 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
887 pjmedia_zero_samples((pj_int16_t*)f.buf,
888 strm->param.samples_per_frame);
889 }
890
891 samples_got = strm->param.samples_per_frame /
892 strm->param.channel_count /
893 strm->resample_factor;
894
895 /* Need to convert channel count? */
896 if (strm->param.channel_count != 1) {
897 pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
898 (pj_int16_t*)f.buf,
899 strm->param.channel_count,
900 strm->param.samples_per_frame,
901 PJ_FALSE,
902 0);
903 }
904
905 /* Need to resample clock rate? */
906 if (strm->play_resample) {
907 unsigned resampled = 0;
908
909 while (resampled < samples_got)
910 {
911 pjmedia_resample_run(strm->play_resample,
912 strm->pcm_buf +
913 resampled * strm->resample_factor,
914 &strm->play_buf[resampled]);
915 resampled += 80;
916 }
917 }
918
919 strm->play_buf_len = samples_got;
920 strm->play_buf_start = 0;
921 }
922
923 unsigned tmp;
924
925 tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
926 pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
927 &strm->play_buf[strm->play_buf_start],
928 tmp);
929 buffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
930 samples_processed += tmp;
931 strm->play_buf_len -= tmp;
932 strm->play_buf_start += tmp;
933 }
934
935 /* Set the buffer */
936 buf->SetPayloadPtr(buffer);
937}
938
939/****************************************************************************
940 * Internal VAS callbacks for non-PCM format
941 */
942
943static void RecCb(CVoIPDataBuffer *buf, void *user_data)
944{
945 struct vas_stream *strm = (struct vas_stream*) user_data;
946 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
947 TPtr8 buffer(0, 0, 0);
948
949 /* Get the buffer */
950 buf->GetPayloadPtr(buffer);
951
952 switch(strm->param.ext_fmt.id) {
953 case PJMEDIA_FORMAT_AMR:
954 {
955 const pj_uint8_t *p = (const pj_uint8_t*)buffer.Ptr() + 1;
956 unsigned len = buffer.Length() - 1;
957
958 pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160);
959 if (frame->samples_cnt == strm->param.samples_per_frame) {
960 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
961 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
962 frame->samples_cnt = 0;
963 frame->subframe_cnt = 0;
964 }
965 }
966 break;
967
968 case PJMEDIA_FORMAT_G729:
969 {
970 /* Check if we got a normal or SID frame. */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000971 if (buffer[0] != 0) {
Nanang Izzuddind687a502009-06-30 13:37:26 +0000972 enum { NORMAL_LEN = 22, SID_LEN = 8 };
973 TBitStream *bitstream = (TBitStream*)strm->strm_data;
974 unsigned src_len = buffer.Length()- 2;
975
976 pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +0000977
Nanang Izzuddind687a502009-06-30 13:37:26 +0000978 const TDesC8& p = bitstream->CompressG729Frame(
979 buffer.Right(src_len),
980 src_len == SID_LEN);
981
982 pjmedia_frame_ext_append_subframe(frame, p.Ptr(),
983 p.Length() << 3, 80);
984 } else { /* We got null frame. */
985 pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80);
986 }
987
988 if (frame->samples_cnt == strm->param.samples_per_frame) {
989 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
990 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
991 frame->samples_cnt = 0;
992 frame->subframe_cnt = 0;
993 }
994 }
995 break;
996
997 case PJMEDIA_FORMAT_ILBC:
998 {
999 unsigned samples_got;
1000
1001 samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
1002
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001003 /* Check if we got a normal or SID frame. */
1004 if (buffer[0] != 0) {
Nanang Izzuddind687a502009-06-30 13:37:26 +00001005 const pj_uint8_t *p = (const pj_uint8_t*)buffer.Ptr() + 2;
1006 unsigned len = buffer.Length() - 2;
1007
1008 pjmedia_frame_ext_append_subframe(frame, p, len << 3,
1009 samples_got);
1010 } else { /* We got null frame. */
1011 pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
1012 }
1013
1014 if (frame->samples_cnt == strm->param.samples_per_frame) {
1015 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1016 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1017 frame->samples_cnt = 0;
1018 frame->subframe_cnt = 0;
1019 }
1020 }
1021 break;
1022
1023 case PJMEDIA_FORMAT_PCMU:
1024 case PJMEDIA_FORMAT_PCMA:
1025 {
1026 unsigned samples_processed = 0;
1027
1028 /* Make sure it is normal frame. */
1029 pj_assert(buffer[0] == 1 && buffer[1] == 0);
1030
1031 /* Detect the recorder G.711 frame size, player frame size will
1032 * follow this recorder frame size.
1033 */
1034 if (vas_g711_frame_len == 0) {
1035 vas_g711_frame_len = buffer.Length() < 160? 80 : 160;
1036 TRACE_((THIS_FILE, "Detected VAS G.711 frame size = %u samples",
1037 vas_g711_frame_len));
1038 }
1039
1040 /* Convert VAS buffer format into pjmedia_frame_ext. Whenever
1041 * samples count in the frame is equal to stream's samples per
1042 * frame, call parent stream callback.
1043 */
1044 while (samples_processed < vas_g711_frame_len) {
1045 unsigned tmp;
1046 const pj_uint8_t *pb = (const pj_uint8_t*)buffer.Ptr() +
1047 2 + samples_processed;
1048
1049 tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
1050 vas_g711_frame_len - samples_processed);
1051
1052 pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
1053 samples_processed += tmp;
1054
1055 if (frame->samples_cnt == strm->param.samples_per_frame) {
1056 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1057 strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
1058 frame->samples_cnt = 0;
1059 frame->subframe_cnt = 0;
1060 }
1061 }
1062 }
1063 break;
1064
1065 default:
1066 break;
1067 }
1068}
1069
1070static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
1071{
1072 struct vas_stream *strm = (struct vas_stream*) user_data;
1073 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
1074 TPtr8 buffer(0, 0, 0);
1075
1076 /* Get the buffer */
1077 buf->GetPayloadPtr(buffer);
1078
1079 /* Init buffer attributes and header. */
1080 buffer.Zero();
1081
1082 switch(strm->param.ext_fmt.id) {
1083 case PJMEDIA_FORMAT_AMR:
1084 {
1085 if (frame->samples_cnt == 0) {
1086 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1087 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1088 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1089 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1090 }
1091
1092 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1093 pjmedia_frame_ext_subframe *sf;
1094 unsigned samples_cnt;
1095
1096 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1097 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1098
1099 if (sf->data && sf->bitlen) {
1100 /* AMR header for VAS is one byte, the format (may be!):
1101 * 0xxxxy00, where xxxx:frame type, y:not sure.
1102 */
1103 unsigned len = (sf->bitlen+7)>>3;
1104 enum {SID_FT = 8 };
1105 pj_uint8_t amr_header = 4, ft = SID_FT;
1106
1107 if (len >= pjmedia_codec_amrnb_framelen[0])
1108 ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
1109
1110 amr_header |= ft << 3;
1111 buffer.Append(amr_header);
1112
1113 buffer.Append((TUint8*)sf->data, len);
1114 } else {
1115 buffer.Append(0);
1116 }
1117
1118 pjmedia_frame_ext_pop_subframes(frame, 1);
1119
1120 } else { /* PJMEDIA_FRAME_TYPE_NONE */
1121 buffer.Append(0);
1122
1123 frame->samples_cnt = 0;
1124 frame->subframe_cnt = 0;
1125 }
1126 }
1127 break;
1128
1129 case PJMEDIA_FORMAT_G729:
1130 {
1131 if (frame->samples_cnt == 0) {
1132 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1133 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1134 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1135 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1136 }
1137
1138 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1139 pjmedia_frame_ext_subframe *sf;
1140 unsigned samples_cnt;
1141
1142 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1143 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1144
1145 if (sf->data && sf->bitlen) {
1146 enum { NORMAL_LEN = 10, SID_LEN = 2 };
1147 pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
1148 TBitStream *bitstream = (TBitStream*)strm->strm_data;
1149 const TPtrC8 src(sf->data, sf->bitlen>>3);
1150 const TDesC8 &dst = bitstream->ExpandG729Frame(src,
1151 sid_frame);
1152 if (sid_frame) {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001153 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001154 buffer.Append(0);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001155 } else {
1156 buffer.Append(1);
1157 buffer.Append(0);
1158 }
1159 buffer.Append(dst);
1160 } else {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001161 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001162 buffer.Append(0);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001163
1164 buffer.AppendFill(0, 22);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001165 }
1166
1167 pjmedia_frame_ext_pop_subframes(frame, 1);
1168
1169 } else { /* PJMEDIA_FRAME_TYPE_NONE */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001170 buffer.Append(2);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001171 buffer.Append(0);
1172
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001173 buffer.AppendFill(0, 22);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001174 }
1175 }
1176 break;
1177
1178 case PJMEDIA_FORMAT_ILBC:
1179 {
1180 if (frame->samples_cnt == 0) {
1181 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1182 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1183 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1184 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1185 }
1186
1187 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1188 pjmedia_frame_ext_subframe *sf;
1189 unsigned samples_cnt;
1190
1191 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1192 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1193
1194 pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
1195 samples_cnt == 160) ||
1196 (strm->param.ext_fmt.bitrate != 15200 &&
1197 samples_cnt == 240));
1198
1199 if (sf->data && sf->bitlen) {
1200 buffer.Append(1);
1201 buffer.Append(0);
1202 buffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1203 } else {
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001204 unsigned frame_len;
1205
1206 buffer.Append(1);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001207 buffer.Append(0);
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001208
1209 /* VAS iLBC frame is 20ms or 30ms */
1210 frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
1211 buffer.AppendFill(0, frame_len);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001212 }
1213
1214 pjmedia_frame_ext_pop_subframes(frame, 1);
1215
1216 } else { /* PJMEDIA_FRAME_TYPE_NONE */
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001217
1218 unsigned frame_len;
1219
1220 buffer.Append(1);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001221 buffer.Append(0);
1222
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001223 /* VAS iLBC frame is 20ms or 30ms */
1224 frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
1225 buffer.AppendFill(0, frame_len);
1226
Nanang Izzuddind687a502009-06-30 13:37:26 +00001227 }
1228 }
1229 break;
1230
1231 case PJMEDIA_FORMAT_PCMU:
1232 case PJMEDIA_FORMAT_PCMA:
1233 {
1234 unsigned samples_ready = 0;
1235 unsigned samples_req = vas_g711_frame_len;
1236
1237 /* Assume frame size is 10ms if frame size hasn't been known. */
1238 if (samples_req == 0)
1239 samples_req = 80;
1240
1241 buffer.Append(1);
1242 buffer.Append(0);
1243
1244 /* Call parent stream callback to get samples to play. */
1245 while (samples_ready < samples_req) {
1246 if (frame->samples_cnt == 0) {
1247 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1248 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1249 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1250 frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1251 }
1252
1253 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1254 pjmedia_frame_ext_subframe *sf;
1255 unsigned samples_cnt;
1256
1257 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1258 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1259 if (sf->data && sf->bitlen) {
1260 buffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1261 } else {
1262 pj_uint8_t silc;
1263 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1264 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1265 buffer.AppendFill(silc, samples_cnt);
1266 }
1267 samples_ready += samples_cnt;
1268
1269 pjmedia_frame_ext_pop_subframes(frame, 1);
1270
1271 } else { /* PJMEDIA_FRAME_TYPE_NONE */
1272 pj_uint8_t silc;
1273
1274 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1275 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1276 buffer.AppendFill(silc, samples_req - samples_ready);
1277
1278 samples_ready = samples_req;
1279 frame->samples_cnt = 0;
1280 frame->subframe_cnt = 0;
1281 }
1282 }
1283 }
1284 break;
1285
1286 default:
1287 break;
1288 }
1289
1290 /* Set the buffer */
1291 buf->SetPayloadPtr(buffer);
1292}
1293
1294
1295/****************************************************************************
1296 * Factory operations
1297 */
1298
1299/*
1300 * C compatible declaration of VAS factory.
1301 */
1302PJ_BEGIN_DECL
1303PJ_DECL(pjmedia_aud_dev_factory*)pjmedia_symb_vas_factory(pj_pool_factory *pf);
1304PJ_END_DECL
1305
1306/*
1307 * Init VAS audio driver.
1308 */
1309PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_symb_vas_factory(pj_pool_factory *pf)
1310{
1311 struct vas_factory *f;
1312 pj_pool_t *pool;
1313
1314 pool = pj_pool_create(pf, "VAS", 1000, 1000, NULL);
1315 f = PJ_POOL_ZALLOC_T(pool, struct vas_factory);
1316 f->pf = pf;
1317 f->pool = pool;
1318 f->base.op = &factory_op;
1319
1320 return &f->base;
1321}
1322
1323/* API: init factory */
1324static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
1325{
1326 struct vas_factory *af = (struct vas_factory*)f;
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001327 CVoIPUtilityFactory *vas_factory_;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001328 CVoIPAudioUplinkStream *vas_uplink;
1329 CVoIPAudioDownlinkStream *vas_dnlink;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001330 RArray<TVoIPCodecFormat> uplink_formats, dnlink_formats;
1331 unsigned ext_fmt_cnt = 0;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001332 TVersion vas_version(1, 0, 0); /* Not really used at this time */
Nanang Izzuddind687a502009-06-30 13:37:26 +00001333 TInt err;
1334
1335 pj_ansi_strcpy(af->dev_info.name, "S60 VAS");
1336 af->dev_info.default_samples_per_sec = 8000;
1337 af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
1338 //PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
1339 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
1340 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
1341 PJMEDIA_AUD_DEV_CAP_VAD |
1342 PJMEDIA_AUD_DEV_CAP_CNG;
1343 af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
1344 PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1345 af->dev_info.input_count = 1;
1346 af->dev_info.output_count = 1;
1347 af->dev_info.ext_fmt_cnt = 0;
1348
1349 /* Enumerate supported formats */
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001350 err = CVoIPUtilityFactory::CreateFactory(vas_factory_);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001351 if (err != KErrNone)
1352 goto on_error;
1353
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001354 /* On VAS 2.0, uplink & downlink stream should be instantiated before
1355 * querying formats.
1356 */
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001357 err = vas_factory_->CreateUplinkStream(vas_version,
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001358 CVoIPUtilityFactory::EVoIPCall,
1359 vas_uplink);
1360 if (err != KErrNone)
1361 goto on_error;
1362
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001363 err = vas_factory_->CreateDownlinkStream(vas_version,
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001364 CVoIPUtilityFactory::EVoIPCall,
1365 vas_dnlink);
1366 if (err != KErrNone)
1367 goto on_error;
1368
Nanang Izzuddind687a502009-06-30 13:37:26 +00001369 uplink_formats.Reset();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001370 err = vas_factory_->GetSupportedUplinkFormats(uplink_formats);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001371 if (err != KErrNone)
1372 goto on_error;
1373
1374 dnlink_formats.Reset();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001375 err = vas_factory_->GetSupportedDownlinkFormats(dnlink_formats);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001376 if (err != KErrNone)
1377 goto on_error;
1378
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001379 /* Free the streams, they are just used for querying formats */
1380 delete vas_uplink;
1381 vas_uplink = NULL;
1382 delete vas_dnlink;
1383 vas_dnlink = NULL;
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001384 delete vas_factory_;
1385 vas_factory_ = NULL;
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001386
Nanang Izzuddind687a502009-06-30 13:37:26 +00001387 for (TInt i = 0; i < dnlink_formats.Count(); i++) {
1388 /* Format must be supported by both downlink & uplink. */
1389 if (uplink_formats.Find(dnlink_formats[i]) == KErrNotFound)
1390 continue;
1391
1392 switch (dnlink_formats[i]) {
1393 case EAMR_NB:
1394 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_AMR;
1395 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 7400;
1396 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
1397 break;
1398
1399 case EG729:
1400 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_G729;
1401 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 8000;
1402 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1403 break;
1404
1405 case EILBC:
1406 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
1407 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 13333;
1408 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
1409 break;
1410
1411 case EG711:
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001412#if PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION==2
1413 case EG711_10MS:
1414#endif
Nanang Izzuddind687a502009-06-30 13:37:26 +00001415 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
1416 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
1417 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1418 ++ext_fmt_cnt;
1419 af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
1420 af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
1421 af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
1422 break;
1423
1424 default:
1425 continue;
1426 }
1427
1428 ++ext_fmt_cnt;
1429 }
1430
1431 af->dev_info.ext_fmt_cnt = ext_fmt_cnt;
1432
1433 uplink_formats.Close();
1434 dnlink_formats.Close();
1435
1436 PJ_LOG(3, (THIS_FILE, "VAS initialized"));
1437
1438 return PJ_SUCCESS;
1439
1440on_error:
1441 return PJ_RETURN_OS_ERROR(err);
1442}
1443
1444/* API: destroy factory */
1445static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
1446{
1447 struct vas_factory *af = (struct vas_factory*)f;
1448 pj_pool_t *pool = af->pool;
1449
1450 af->pool = NULL;
1451 pj_pool_release(pool);
1452
1453 PJ_LOG(3, (THIS_FILE, "VAS destroyed"));
1454
1455 return PJ_SUCCESS;
1456}
1457
1458/* API: get number of devices */
1459static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
1460{
1461 PJ_UNUSED_ARG(f);
1462 return 1;
1463}
1464
1465/* API: get device info */
1466static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
1467 unsigned index,
1468 pjmedia_aud_dev_info *info)
1469{
1470 struct vas_factory *af = (struct vas_factory*)f;
1471
1472 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1473
1474 pj_memcpy(info, &af->dev_info, sizeof(*info));
1475
1476 return PJ_SUCCESS;
1477}
1478
1479/* API: create default device parameter */
1480static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
1481 unsigned index,
1482 pjmedia_aud_param *param)
1483{
1484 struct vas_factory *af = (struct vas_factory*)f;
1485
1486 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1487
1488 pj_bzero(param, sizeof(*param));
1489 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1490 param->rec_id = index;
1491 param->play_id = index;
1492 param->clock_rate = af->dev_info.default_samples_per_sec;
1493 param->channel_count = 1;
1494 param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
1495 param->bits_per_sample = BITS_PER_SAMPLE;
1496 param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1497 param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1498
1499 return PJ_SUCCESS;
1500}
1501
1502
1503/* API: create stream */
1504static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1505 const pjmedia_aud_param *param,
1506 pjmedia_aud_rec_cb rec_cb,
1507 pjmedia_aud_play_cb play_cb,
1508 void *user_data,
1509 pjmedia_aud_stream **p_aud_strm)
1510{
1511 struct vas_factory *af = (struct vas_factory*)f;
1512 pj_pool_t *pool;
1513 struct vas_stream *strm;
1514
1515 CPjAudioSetting vas_setting;
1516 PjAudioCallback vas_rec_cb;
1517 PjAudioCallback vas_play_cb;
1518
1519 /* Can only support 16bits per sample */
1520 PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
1521
1522 /* Supported clock rates:
1523 * - for non-PCM format: 8kHz
1524 * - for PCM format: 8kHz and 16kHz
1525 */
1526 PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
1527 (param->clock_rate == 16000 &&
1528 param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1529 PJ_EINVAL);
1530
1531 /* Supported channels number:
1532 * - for non-PCM format: mono
1533 * - for PCM format: mono and stereo
1534 */
1535 PJ_ASSERT_RETURN(param->channel_count == 1 ||
1536 (param->channel_count == 2 &&
1537 param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1538 PJ_EINVAL);
1539
1540 /* Create and Initialize stream descriptor */
1541 pool = pj_pool_create(af->pf, "vas-dev", 1000, 1000, NULL);
1542 PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
1543
1544 strm = PJ_POOL_ZALLOC_T(pool, struct vas_stream);
1545 strm->pool = pool;
1546 strm->param = *param;
1547
1548 if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
1549 strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
1550
1551 /* Set audio engine fourcc. */
1552 switch(strm->param.ext_fmt.id) {
1553 case PJMEDIA_FORMAT_L16:
1554#ifdef USE_NATIVE_PCM
1555 vas_setting.format = EPCM16;
1556#else
1557 vas_setting.format = EG711;
1558#endif
1559 break;
1560 case PJMEDIA_FORMAT_PCMU:
1561 case PJMEDIA_FORMAT_PCMA:
1562 vas_setting.format = EG711;
1563 break;
1564 case PJMEDIA_FORMAT_AMR:
1565 vas_setting.format = EAMR_NB;
1566 break;
1567 case PJMEDIA_FORMAT_G729:
1568 vas_setting.format = EG729;
1569 break;
1570 case PJMEDIA_FORMAT_ILBC:
1571 vas_setting.format = EILBC;
1572 break;
1573 default:
1574 vas_setting.format = ENULL;
1575 break;
1576 }
1577
1578 /* Set audio engine mode. */
1579 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1580 {
1581#ifdef USE_NATIVE_PCM
1582 vas_setting.mode = 0;
1583#else
1584 vas_setting.mode = CVoIPFormatIntfc::EG711uLaw;
1585#endif
1586 }
1587 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
1588 {
1589 vas_setting.mode = strm->param.ext_fmt.bitrate;
1590 }
1591 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU)
1592 {
1593 vas_setting.mode = CVoIPFormatIntfc::EG711uLaw;
1594 }
1595 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA)
1596 {
1597 vas_setting.mode = CVoIPFormatIntfc::EG711ALaw;
1598 }
1599 else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC)
1600 {
1601 if (strm->param.ext_fmt.bitrate == 15200)
1602 vas_setting.mode = CVoIPFormatIntfc::EiLBC20mSecFrame;
1603 else
1604 vas_setting.mode = CVoIPFormatIntfc::EiLBC30mSecFrame;
1605 } else {
1606 vas_setting.mode = 0;
1607 }
1608
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001609 /* Disable VAD on L16, G711, iLBC, and also G729 (G729's SID
1610 * potentially cause noise?).
Nanang Izzuddind687a502009-06-30 13:37:26 +00001611 */
1612 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1613 strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1614 strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
Nanang Izzuddinfc279de2009-07-14 14:33:39 +00001615 strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC ||
Nanang Izzuddind687a502009-06-30 13:37:26 +00001616 strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
1617 {
1618 vas_setting.vad = EFalse;
1619 } else {
1620 vas_setting.vad = strm->param.ext_fmt.vad;
1621 }
1622
1623 /* Set other audio engine attributes. */
1624 vas_setting.plc = strm->param.plc_enabled;
1625 vas_setting.cng = vas_setting.vad;
1626 vas_setting.loudspk =
1627 strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1628
1629 /* Set audio engine callbacks. */
1630 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
1631#ifdef USE_NATIVE_PCM
1632 vas_play_cb = &PlayCbPcm2;
1633 vas_rec_cb = &RecCbPcm2;
1634#else
1635 vas_play_cb = &PlayCbPcm;
1636 vas_rec_cb = &RecCbPcm;
1637#endif
1638 } else {
1639 vas_play_cb = &PlayCb;
1640 vas_rec_cb = &RecCb;
1641 }
1642
1643 strm->rec_cb = rec_cb;
1644 strm->play_cb = play_cb;
1645 strm->user_data = user_data;
1646 strm->resample_factor = strm->param.clock_rate / 8000;
1647
1648 /* play_buf size is samples per frame scaled in to 8kHz mono. */
1649 strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
1650 pool,
1651 (strm->param.samples_per_frame /
1652 strm->resample_factor /
1653 strm->param.channel_count) << 1);
1654 strm->play_buf_len = 0;
1655 strm->play_buf_start = 0;
1656
1657 /* rec_buf size is samples per frame scaled in to 8kHz mono. */
1658 strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(
1659 pool,
1660 (strm->param.samples_per_frame /
1661 strm->resample_factor /
1662 strm->param.channel_count) << 1);
1663 strm->rec_buf_len = 0;
1664
1665 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1666 TBitStream *g729_bitstream = new TBitStream;
1667
1668 PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
1669 strm->strm_data = (void*)g729_bitstream;
1670 }
1671
1672 /* Init resampler when format is PCM and clock rate is not 8kHz */
1673 if (strm->param.clock_rate != 8000 &&
1674 strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1675 {
1676 pj_status_t status;
1677
1678 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1679 /* Create resample for recorder */
1680 status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1681 8000,
1682 strm->param.clock_rate,
1683 80,
1684 &strm->rec_resample);
1685 if (status != PJ_SUCCESS)
1686 return status;
1687 }
1688
1689 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1690 /* Create resample for player */
1691 status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
1692 strm->param.clock_rate,
1693 8000,
1694 80 * strm->resample_factor,
1695 &strm->play_resample);
1696 if (status != PJ_SUCCESS)
1697 return status;
1698 }
1699 }
1700
1701 /* Create PCM buffer, when the clock rate is not 8kHz or not mono */
1702 if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
1703 (strm->resample_factor > 1 || strm->param.channel_count != 1))
1704 {
1705 strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
1706 strm->param.samples_per_frame << 1);
1707 }
1708
1709
1710 /* Create the audio engine. */
1711 TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
1712 vas_rec_cb, vas_play_cb,
1713 strm, vas_setting));
1714 if (err != KErrNone) {
1715 pj_pool_release(pool);
1716 return PJ_RETURN_OS_ERROR(err);
1717 }
1718
1719 /* Apply output volume setting if specified */
1720 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1721 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1722 &param->output_vol);
1723 }
1724
1725 /* Done */
1726 strm->base.op = &stream_op;
1727 *p_aud_strm = &strm->base;
1728
1729 return PJ_SUCCESS;
1730}
1731
1732/* API: Get stream info. */
1733static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1734 pjmedia_aud_param *pi)
1735{
1736 struct vas_stream *strm = (struct vas_stream*)s;
1737
1738 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1739
1740 pj_memcpy(pi, &strm->param, sizeof(*pi));
1741
1742 /* Update the output volume setting */
1743 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1744 &pi->output_vol) == PJ_SUCCESS)
1745 {
1746 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1747 }
1748
1749 return PJ_SUCCESS;
1750}
1751
1752/* API: get capability */
1753static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1754 pjmedia_aud_dev_cap cap,
1755 void *pval)
1756{
1757 struct vas_stream *strm = (struct vas_stream*)s;
1758 pj_status_t status = PJ_ENOTSUP;
1759
1760 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1761
1762 switch (cap) {
1763 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1764 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1765 *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
1766 status = PJ_SUCCESS;
1767 }
1768 break;
1769
1770 /* There is a case that GetMaxGain() stucks, e.g: in N95. */
1771 /*
1772 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1773 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1774 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1775
1776 TInt max_gain = strm->engine->GetMaxGain();
1777 TInt gain = strm->engine->GetGain();
1778
1779 if (max_gain > 0 && gain >= 0) {
1780 *(unsigned*)pval = gain * 100 / max_gain;
1781 status = PJ_SUCCESS;
1782 } else {
1783 status = PJMEDIA_EAUD_NOTREADY;
1784 }
1785 }
1786 break;
1787 */
1788
1789 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1790 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1791 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1792
1793 TInt max_vol = strm->engine->GetMaxVolume();
1794 TInt vol = strm->engine->GetVolume();
1795
1796 if (max_vol > 0 && vol >= 0) {
1797 *(unsigned*)pval = vol * 100 / max_vol;
1798 status = PJ_SUCCESS;
1799 } else {
1800 status = PJMEDIA_EAUD_NOTREADY;
1801 }
1802 }
1803 break;
1804 default:
1805 break;
1806 }
1807
1808 return status;
1809}
1810
1811/* API: set capability */
1812static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1813 pjmedia_aud_dev_cap cap,
1814 const void *pval)
1815{
1816 struct vas_stream *strm = (struct vas_stream*)s;
1817 pj_status_t status = PJ_ENOTSUP;
1818
1819 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1820
1821 switch (cap) {
1822 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
1823 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1824 pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
1825 TInt err;
1826
1827 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1828
1829 switch (r) {
1830 case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
1831 case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
1832 err = strm->engine->ActivateSpeaker(EFalse);
1833 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1834 break;
1835 case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
1836 err = strm->engine->ActivateSpeaker(ETrue);
1837 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1838 break;
1839 default:
1840 status = PJ_EINVAL;
1841 break;
1842 }
1843 if (status == PJ_SUCCESS)
1844 strm->param.output_route = r;
1845 }
1846 break;
1847
1848 /* There is a case that GetMaxGain() stucks, e.g: in N95. */
1849 /*
1850 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1851 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1852 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1853
1854 TInt max_gain = strm->engine->GetMaxGain();
1855 if (max_gain > 0) {
1856 TInt gain, err;
1857
1858 gain = *(unsigned*)pval * max_gain / 100;
1859 err = strm->engine->SetGain(gain);
1860 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1861 } else {
1862 status = PJMEDIA_EAUD_NOTREADY;
1863 }
1864 if (status == PJ_SUCCESS)
1865 strm->param.input_vol = *(unsigned*)pval;
1866 }
1867 break;
1868 */
1869
1870 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1871 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1872 PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1873
1874 TInt max_vol = strm->engine->GetMaxVolume();
1875 if (max_vol > 0) {
1876 TInt vol, err;
1877
1878 vol = *(unsigned*)pval * max_vol / 100;
1879 err = strm->engine->SetVolume(vol);
1880 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1881 } else {
1882 status = PJMEDIA_EAUD_NOTREADY;
1883 }
1884 if (status == PJ_SUCCESS)
1885 strm->param.output_vol = *(unsigned*)pval;
1886 }
1887 break;
1888 default:
1889 break;
1890 }
1891
1892 return status;
1893}
1894
1895/* API: Start stream. */
1896static pj_status_t stream_start(pjmedia_aud_stream *strm)
1897{
1898 struct vas_stream *stream = (struct vas_stream*)strm;
1899
1900 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1901
1902 if (stream->engine) {
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001903 enum { VAS_WAIT_START = 2000 }; /* in msecs */
1904 TTime start, now;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001905 TInt err = stream->engine->Start();
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001906
Nanang Izzuddind687a502009-06-30 13:37:26 +00001907 if (err != KErrNone)
1908 return PJ_RETURN_OS_ERROR(err);
Nanang Izzuddind687a502009-06-30 13:37:26 +00001909
Nanang Izzuddin9d0da8d2009-11-24 12:24:35 +00001910 /* Perform synchronous start, timeout after VAS_WAIT_START ms */
1911 start.UniversalTime();
1912 do {
1913 pj_symbianos_poll(-1, 100);
1914 now.UniversalTime();
1915 } while (!stream->engine->IsStarted() &&
1916 (now.MicroSecondsFrom(start) < VAS_WAIT_START * 1000));
1917
1918 if (stream->engine->IsStarted())
1919 return PJ_SUCCESS;
1920 else
1921 return PJ_ETIMEDOUT;
1922 }
1923
1924 return PJ_EINVALIDOP;
Nanang Izzuddind687a502009-06-30 13:37:26 +00001925}
1926
1927/* API: Stop stream. */
1928static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1929{
1930 struct vas_stream *stream = (struct vas_stream*)strm;
1931
1932 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1933
1934 if (stream->engine) {
1935 stream->engine->Stop();
1936 }
1937
1938 return PJ_SUCCESS;
1939}
1940
1941
1942/* API: Destroy stream. */
1943static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1944{
1945 struct vas_stream *stream = (struct vas_stream*)strm;
1946
1947 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1948
1949 stream_stop(strm);
1950
1951 delete stream->engine;
1952 stream->engine = NULL;
1953
1954 if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1955 TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
1956 stream->strm_data = NULL;
1957 delete g729_bitstream;
1958 }
1959
1960 pj_pool_t *pool;
1961 pool = stream->pool;
1962 if (pool) {
1963 stream->pool = NULL;
1964 pj_pool_release(pool);
1965 }
1966
1967 return PJ_SUCCESS;
1968}
1969
1970#endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
1971