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