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