Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia-audiodev / symb_aps_dev.cpp
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20 #include <pjmedia-audiodev/audiodev_imp.h>
21 #include <pjmedia-audiodev/errno.h>
22 #include <pjmedia/alaw_ulaw.h>
23 #include <pjmedia/resample.h>
24 #include <pjmedia/stereo.h>
25 #include <pj/assert.h>
26 #include <pj/log.h>
27 #include <pj/math.h>
28 #include <pj/os.h>
29 #include <pj/string.h>
30
31 #if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
32
33 #include <e32msgqueue.h>
34 #include <sounddevice.h>
35 #include <APSClientSession.h>
36 #include <pjmedia-codec/amr_helper.h>
37
38 /* Pack/unpack G.729 frame of S60 DSP codec, taken from:  
39  * http://wiki.forum.nokia.com/index.php/TSS000776_-_Payload_conversion_for_G.729_audio_format
40  */
41 #include "s60_g729_bitstream.h"
42
43
44 #define THIS_FILE                       "symb_aps_dev.c"
45 #define BITS_PER_SAMPLE                 16
46
47
48 #if 1
49 #   define TRACE_(st) PJ_LOG(3, st)
50 #else
51 #   define TRACE_(st)
52 #endif
53
54
55 /* App UID to open global APS queues to communicate with the APS server. */
56 extern TPtrC APP_UID;
57
58 /* APS G.711 frame length */
59 static pj_uint8_t aps_g711_frame_len;
60
61
62 /* APS factory */
63 struct aps_factory
64 {
65     pjmedia_aud_dev_factory      base;
66     pj_pool_t                   *pool;
67     pj_pool_factory             *pf;
68     pjmedia_aud_dev_info         dev_info;
69 };
70
71
72 /* Forward declaration of CPjAudioEngine */
73 class CPjAudioEngine;
74
75
76 /* APS stream. */
77 struct aps_stream
78 {
79     // Base
80     pjmedia_aud_stream   base;                  /**< Base class.        */
81     
82     // Pool
83     pj_pool_t           *pool;                  /**< Memory pool.       */
84
85     // Common settings.
86     pjmedia_aud_param    param;                 /**< Stream param.      */
87     pjmedia_aud_rec_cb   rec_cb;                /**< Record callback.   */
88     pjmedia_aud_play_cb  play_cb;               /**< Playback callback. */
89     void                *user_data;             /**< Application data.  */
90
91     // Audio engine
92     CPjAudioEngine      *engine;                /**< Internal engine.   */
93
94     pj_timestamp         ts_play;               /**< Playback timestamp.*/
95     pj_timestamp         ts_rec;                /**< Record timestamp.  */
96
97     pj_int16_t          *play_buf;              /**< Playback buffer.   */
98     pj_uint16_t          play_buf_len;          /**< Playback buffer length. */
99     pj_uint16_t          play_buf_start;        /**< Playback buffer start index. */
100     pj_int16_t          *rec_buf;               /**< Record buffer.     */
101     pj_uint16_t          rec_buf_len;           /**< Record buffer length. */
102     void                *strm_data;             /**< Stream data.       */
103
104     /* Resampling is needed, in case audio device is opened with clock rate 
105      * other than 8kHz (only for PCM format).
106      */
107     pjmedia_resample    *play_resample;         /**< Resampler for playback. */
108     pjmedia_resample    *rec_resample;          /**< Resampler for recording */
109     pj_uint16_t          resample_factor;       /**< Resample factor, requested
110                                                      clock rate / 8000       */
111
112     /* When stream is working in PCM format, where the samples may need to be
113      * resampled from/to different clock rate and/or channel count, PCM buffer
114      * is needed to perform such resampling operations.
115      */
116     pj_int16_t          *pcm_buf;               /**< PCM buffer.             */
117 };
118
119
120 /* Prototypes */
121 static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
122 static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
123 static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
124 static unsigned    factory_get_dev_count(pjmedia_aud_dev_factory *f);
125 static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, 
126                                         unsigned index,
127                                         pjmedia_aud_dev_info *info);
128 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
129                                          unsigned index,
130                                          pjmedia_aud_param *param);
131 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
132                                          const pjmedia_aud_param *param,
133                                          pjmedia_aud_rec_cb rec_cb,
134                                          pjmedia_aud_play_cb play_cb,
135                                          void *user_data,
136                                          pjmedia_aud_stream **p_aud_strm);
137
138 static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
139                                     pjmedia_aud_param *param);
140 static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
141                                   pjmedia_aud_dev_cap cap,
142                                   void *value);
143 static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
144                                   pjmedia_aud_dev_cap cap,
145                                   const void *value);
146 static pj_status_t stream_start(pjmedia_aud_stream *strm);
147 static pj_status_t stream_stop(pjmedia_aud_stream *strm);
148 static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
149
150
151 /* Operations */
152 static pjmedia_aud_dev_factory_op factory_op =
153 {
154     &factory_init,
155     &factory_destroy,
156     &factory_get_dev_count,
157     &factory_get_dev_info,
158     &factory_default_param,
159     &factory_create_stream,
160     &factory_refresh
161 };
162
163 static pjmedia_aud_stream_op stream_op = 
164 {
165     &stream_get_param,
166     &stream_get_cap,
167     &stream_set_cap,
168     &stream_start,
169     &stream_stop,
170     &stream_destroy
171 };
172
173
174 /****************************************************************************
175  * Internal APS Engine
176  */
177
178 /*
179  * Utility: print sound device error
180  */
181 static void snd_perror(const char *title, TInt rc)
182 {
183     PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
184 }
185
186 /*
187  * Utility: wait for specified time.
188  */
189 static void snd_wait(unsigned ms) 
190 {
191     TTime start, now;
192     
193     start.UniversalTime();
194     do {
195         pj_symbianos_poll(-1, ms);
196         now.UniversalTime();
197     } while (now.MicroSecondsFrom(start) < ms * 1000);
198 }
199
200 typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data);
201
202 /**
203  * Abstract class for handler of callbacks from APS client.
204  */
205 class MQueueHandlerObserver
206 {
207 public:
208     MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_,
209                           void *UserData_)
210     : RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_)
211     {}
212
213     virtual void InputStreamInitialized(const TInt aStatus) = 0;
214     virtual void OutputStreamInitialized(const TInt aStatus) = 0;
215     virtual void NotifyError(const TInt aError) = 0;
216
217 public:
218     PjAudioCallback RecCb;
219     PjAudioCallback PlayCb;
220     void *UserData;
221 };
222
223 /**
224  * Handler for communication and data queue.
225  */
226 class CQueueHandler : public CActive
227 {
228 public:
229     // Types of queue handler
230     enum TQueueHandlerType {
231         ERecordCommQueue,
232         EPlayCommQueue,
233         ERecordQueue,
234         EPlayQueue
235     };
236
237     // The order corresponds to the APS Server state, do not change!
238     enum TState {
239         EAPSPlayerInitialize        = 1,
240         EAPSRecorderInitialize      = 2,
241         EAPSPlayData                = 3,
242         EAPSRecordData              = 4,
243         EAPSPlayerInitComplete      = 5,
244         EAPSRecorderInitComplete    = 6
245     };
246
247     static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
248                                RMsgQueue<TAPSCommBuffer>* aQ,
249                                RMsgQueue<TAPSCommBuffer>* aWriteQ,
250                                TQueueHandlerType aType)
251     {
252         CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ,
253                                                          aType);
254         CleanupStack::PushL(self);
255         self->ConstructL();
256         CleanupStack::Pop(self);
257         return self;
258     }
259
260     // Destructor
261     ~CQueueHandler() { Cancel(); }
262
263     // Start listening queue event
264     void Start() {
265         iQ->NotifyDataAvailable(iStatus);
266         SetActive();
267     }
268
269 private:
270     // Constructor
271     CQueueHandler(MQueueHandlerObserver* aObserver,
272                   RMsgQueue<TAPSCommBuffer>* aQ,
273                   RMsgQueue<TAPSCommBuffer>* aWriteQ,
274                   TQueueHandlerType aType)
275         : CActive(CActive::EPriorityHigh),
276           iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
277     {
278         CActiveScheduler::Add(this);
279
280         // use lower priority for comm queues
281         if ((iType == ERecordCommQueue) || (iType == EPlayCommQueue))
282             SetPriority(CActive::EPriorityStandard);
283     }
284
285     // Second phase constructor
286     void ConstructL() {}
287
288     // Inherited from CActive
289     void DoCancel() { iQ->CancelDataAvailable(); }
290
291     void RunL() {
292         if (iStatus != KErrNone) {
293             iObserver->NotifyError(iStatus.Int());
294             return;
295         }
296
297         TAPSCommBuffer buffer;
298         TInt ret = iQ->Receive(buffer);
299
300         if (ret != KErrNone) {
301             iObserver->NotifyError(ret);
302             return;
303         }
304
305         switch (iType) {
306         case ERecordQueue:
307             if (buffer.iCommand == EAPSRecordData) {
308                 iObserver->RecCb(buffer, iObserver->UserData);
309             } else {
310                 iObserver->NotifyError(buffer.iStatus);
311             }
312             break;
313
314         // Callbacks from the APS main thread
315         case EPlayCommQueue:
316             switch (buffer.iCommand) {
317                 case EAPSPlayData:
318                     if (buffer.iStatus == KErrUnderflow) {
319                         iObserver->PlayCb(buffer, iObserver->UserData);
320                         iWriteQ->Send(buffer);
321                     }
322                     break;
323                 case EAPSPlayerInitialize:
324                     iObserver->NotifyError(buffer.iStatus);
325                     break;
326                 case EAPSPlayerInitComplete:
327                     iObserver->OutputStreamInitialized(buffer.iStatus);
328                     break;
329                 case EAPSRecorderInitComplete:
330                     iObserver->InputStreamInitialized(buffer.iStatus);
331                     break;
332                 default:
333                     iObserver->NotifyError(buffer.iStatus);
334                     break;
335             }
336             break;
337
338         // Callbacks from the APS recorder thread
339         case ERecordCommQueue:
340             switch (buffer.iCommand) {
341                 // The APS recorder thread will only report errors
342                 // through this handler. All other callbacks will be
343                 // sent from the APS main thread through EPlayCommQueue
344                 case EAPSRecorderInitialize:
345                 case EAPSRecordData:
346                 default:
347                     iObserver->NotifyError(buffer.iStatus);
348                     break;
349             }
350             break;
351
352         default:
353             break;
354         }
355
356         // issue next request
357         iQ->NotifyDataAvailable(iStatus);
358         SetActive();
359     }
360
361     TInt RunError(TInt) {
362         return 0;
363     }
364
365     // Data
366     RMsgQueue<TAPSCommBuffer>   *iQ;   // (not owned)
367     RMsgQueue<TAPSCommBuffer>   *iWriteQ;   // (not owned)
368     MQueueHandlerObserver       *iObserver; // (not owned)
369     TQueueHandlerType            iType;
370 };
371
372 /*
373  * Audio setting for CPjAudioEngine.
374  */
375 class CPjAudioSetting
376 {
377 public:
378     TFourCC              fourcc;
379     TAPSCodecMode        mode;
380     TBool                plc;
381     TBool                vad;
382     TBool                cng;
383     TBool                loudspk;
384 };
385
386 /*
387  * Implementation: Symbian Input & Output Stream.
388  */
389 class CPjAudioEngine : public CBase, MQueueHandlerObserver
390 {
391 public:
392     enum State
393     {
394         STATE_NULL,
395         STATE_INITIALIZING,
396         STATE_READY,
397         STATE_STREAMING,
398         STATE_PENDING_STOP
399     };
400
401     ~CPjAudioEngine();
402
403     static CPjAudioEngine *NewL(struct aps_stream *parent_strm,
404                                 PjAudioCallback rec_cb,
405                                 PjAudioCallback play_cb,
406                                 void *user_data,
407                                 const CPjAudioSetting &setting);
408
409     TInt StartL();
410     void Stop();
411
412     TInt ActivateSpeaker(TBool active);
413     
414     TInt SetVolume(TInt vol) { return iSession.SetVolume(vol); }
415     TInt GetVolume() { return iSession.Volume(); }
416     TInt GetMaxVolume() { return iSession.MaxVolume(); }
417     
418     TInt SetGain(TInt gain) { return iSession.SetGain(gain); }
419     TInt GetGain() { return iSession.Gain(); }
420     TInt GetMaxGain() { return iSession.MaxGain(); }
421
422 private:
423     CPjAudioEngine(struct aps_stream *parent_strm,
424                    PjAudioCallback rec_cb,
425                    PjAudioCallback play_cb,
426                    void *user_data,
427                    const CPjAudioSetting &setting);
428     void ConstructL();
429
430     TInt InitPlayL();
431     TInt InitRecL();
432     TInt StartStreamL();
433     void Deinit();
434
435     // Inherited from MQueueHandlerObserver
436     virtual void InputStreamInitialized(const TInt aStatus);
437     virtual void OutputStreamInitialized(const TInt aStatus);
438     virtual void NotifyError(const TInt aError);
439
440     TBool                        session_opened;
441     State                        state_;
442     struct aps_stream           *parentStrm_;
443     CPjAudioSetting              setting_;
444
445     RAPSSession                  iSession;
446     TAPSInitSettings             iPlaySettings;
447     TAPSInitSettings             iRecSettings;
448
449     RMsgQueue<TAPSCommBuffer>    iReadQ;
450     RMsgQueue<TAPSCommBuffer>    iReadCommQ;
451     TBool                        readq_opened;
452     RMsgQueue<TAPSCommBuffer>    iWriteQ;
453     RMsgQueue<TAPSCommBuffer>    iWriteCommQ;
454     TBool                        writeq_opened;
455
456     CQueueHandler               *iPlayCommHandler;
457     CQueueHandler               *iRecCommHandler;
458     CQueueHandler               *iRecHandler;
459 };
460
461
462 CPjAudioEngine* CPjAudioEngine::NewL(struct aps_stream *parent_strm,
463                                      PjAudioCallback rec_cb,
464                                      PjAudioCallback play_cb,
465                                      void *user_data,
466                                      const CPjAudioSetting &setting)
467 {
468     CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
469                                                        rec_cb, play_cb,
470                                                        user_data,
471                                                        setting);
472     CleanupStack::PushL(self);
473     self->ConstructL();
474     CleanupStack::Pop(self);
475     return self;
476 }
477
478 CPjAudioEngine::CPjAudioEngine(struct aps_stream *parent_strm,
479                                PjAudioCallback rec_cb,
480                                PjAudioCallback play_cb,
481                                void *user_data,
482                                const CPjAudioSetting &setting)
483       : MQueueHandlerObserver(rec_cb, play_cb, user_data),
484         session_opened(EFalse),
485         state_(STATE_NULL),
486         parentStrm_(parent_strm),
487         setting_(setting),
488         readq_opened(EFalse),
489         writeq_opened(EFalse),
490         iPlayCommHandler(0),
491         iRecCommHandler(0),
492         iRecHandler(0)
493 {
494 }
495
496 CPjAudioEngine::~CPjAudioEngine()
497 {
498     Deinit();
499
500     TRACE_((THIS_FILE, "Sound device destroyed"));
501 }
502
503 TInt CPjAudioEngine::InitPlayL()
504 {
505     TInt err = iSession.InitializePlayer(iPlaySettings);
506     if (err != KErrNone) {
507         Deinit();
508         snd_perror("Failed to initialize player", err);
509         return err;
510     }
511
512     // Open message queues for the output stream
513     TBuf<128> buf2 = iPlaySettings.iGlobal;
514     buf2.Append(_L("PlayQueue"));
515     TBuf<128> buf3 = iPlaySettings.iGlobal;
516     buf3.Append(_L("PlayCommQueue"));
517
518     while (iWriteQ.OpenGlobal(buf2))
519         User::After(10);
520     while (iWriteCommQ.OpenGlobal(buf3))
521         User::After(10);
522         
523     writeq_opened = ETrue;
524
525     // Construct message queue handler
526     iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
527                                            CQueueHandler::EPlayCommQueue);
528
529     // Start observing APS callbacks on output stream message queue
530     iPlayCommHandler->Start();
531
532     return 0;
533 }
534
535 TInt CPjAudioEngine::InitRecL()
536 {
537     // Initialize input stream device
538     TInt err = iSession.InitializeRecorder(iRecSettings);
539     if (err != KErrNone && err != KErrAlreadyExists) {
540         Deinit();
541         snd_perror("Failed to initialize recorder", err);
542         return err;
543     }
544
545     TBuf<128> buf1 = iRecSettings.iGlobal;
546     buf1.Append(_L("RecordQueue"));
547     TBuf<128> buf4 = iRecSettings.iGlobal;
548     buf4.Append(_L("RecordCommQueue"));
549
550     // Must wait for APS thread to finish creating message queues
551     // before we can open and use them.
552     while (iReadQ.OpenGlobal(buf1))
553         User::After(10);
554     while (iReadCommQ.OpenGlobal(buf4))
555         User::After(10);
556
557     readq_opened = ETrue;
558
559     // Construct message queue handlers
560     iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL,
561                                       CQueueHandler::ERecordQueue);
562     iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL,
563                                           CQueueHandler::ERecordCommQueue);
564
565     // Start observing APS callbacks from on input stream message queue
566     iRecHandler->Start();
567     iRecCommHandler->Start();
568
569     return 0;
570 }
571
572 TInt CPjAudioEngine::StartL()
573 {
574     if (state_ == STATE_READY)
575         return StartStreamL();
576
577     PJ_ASSERT_RETURN(state_ == STATE_NULL, PJMEDIA_EAUD_INVOP);
578     
579     if (!session_opened) {
580         TInt err = iSession.Connect();
581         if (err != KErrNone)
582             return err;
583         session_opened = ETrue;
584     }
585
586     // Even if only capturer are opened, playback thread of APS Server need
587     // to be run(?). Since some messages will be delivered via play comm queue.
588     state_ = STATE_INITIALIZING;
589
590     return InitPlayL();
591 }
592
593 void CPjAudioEngine::Stop()
594 {
595     if (state_ == STATE_STREAMING) {
596         iSession.Stop();
597         state_ = STATE_READY;
598         TRACE_((THIS_FILE, "Sound device stopped"));
599     } else if (state_ == STATE_INITIALIZING) {
600         // Initialization is on progress, so let's set the state to 
601         // STATE_PENDING_STOP to prevent it starting the stream.
602         state_ = STATE_PENDING_STOP;
603         
604         // Then wait until initialization done.
605         while (state_ != STATE_READY && state_ != STATE_NULL)
606             pj_symbianos_poll(-1, 100);
607     }
608 }
609
610 void CPjAudioEngine::ConstructL()
611 {
612     // Recorder settings
613     iRecSettings.iFourCC                = setting_.fourcc;
614     iRecSettings.iGlobal                = APP_UID;
615     iRecSettings.iPriority              = TMdaPriority(100);
616     iRecSettings.iPreference            = TMdaPriorityPreference(0x05210001);
617     iRecSettings.iSettings.iChannels    = EMMFMono;
618     iRecSettings.iSettings.iSampleRate  = EMMFSampleRate8000Hz;
619
620     // Player settings
621     iPlaySettings.iFourCC               = setting_.fourcc;
622     iPlaySettings.iGlobal               = APP_UID;
623     iPlaySettings.iPriority             = TMdaPriority(100);
624     iPlaySettings.iPreference           = TMdaPriorityPreference(0x05220001);
625     iPlaySettings.iSettings.iChannels   = EMMFMono;
626     iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
627     iPlaySettings.iSettings.iVolume     = 0;
628
629     User::LeaveIfError(iSession.Connect());
630     session_opened = ETrue;
631 }
632
633 TInt CPjAudioEngine::StartStreamL()
634 {
635     pj_assert(state_==STATE_READY || state_==STATE_INITIALIZING); 
636     
637     iSession.SetCng(setting_.cng);
638     iSession.SetVadMode(setting_.vad);
639     iSession.SetPlc(setting_.plc);
640     iSession.SetEncoderMode(setting_.mode);
641     iSession.SetDecoderMode(setting_.mode);
642     iSession.ActivateLoudspeaker(setting_.loudspk);
643
644     // Not only capture
645     if (parentStrm_->param.dir != PJMEDIA_DIR_CAPTURE) {
646         iSession.Write();
647         TRACE_((THIS_FILE, "Player started"));
648     }
649
650     // Not only playback
651     if (parentStrm_->param.dir != PJMEDIA_DIR_PLAYBACK) {
652         iSession.Read();
653         TRACE_((THIS_FILE, "Recorder started"));
654     }
655
656     state_ = STATE_STREAMING;
657     
658     return 0;
659 }
660
661 void CPjAudioEngine::Deinit()
662 {
663     Stop();
664
665     delete iRecHandler;
666     delete iPlayCommHandler;
667     delete iRecCommHandler;
668
669     if (session_opened) {
670         enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
671         
672         // On some devices, immediate closing after stopping may cause 
673         // APS server panic KERN-EXEC 0, so let's wait for sometime before
674         // closing the client session.
675         snd_wait(APS_CLOSE_WAIT_TIME);
676
677         iSession.Close();
678         session_opened = EFalse;
679     }
680
681     if (readq_opened) {
682         iReadQ.Close();
683         iReadCommQ.Close();
684         readq_opened = EFalse;
685     }
686
687     if (writeq_opened) {
688         iWriteQ.Close();
689         iWriteCommQ.Close();
690         writeq_opened = EFalse;
691     }
692
693     state_ = STATE_NULL;
694 }
695
696 void CPjAudioEngine::InputStreamInitialized(const TInt aStatus)
697 {
698     TRACE_((THIS_FILE, "Recorder initialized, err=%d", aStatus));
699
700     if (aStatus == KErrNone) {
701         // Don't start the stream since Stop() has been requested. 
702         if (state_ != STATE_PENDING_STOP) {
703             StartStreamL();
704         } else {
705             state_ = STATE_READY;
706         }
707     } else {
708         Deinit();
709     }
710 }
711
712 void CPjAudioEngine::OutputStreamInitialized(const TInt aStatus)
713 {
714     TRACE_((THIS_FILE, "Player initialized, err=%d", aStatus));
715
716     if (aStatus == KErrNone) {
717         if (parentStrm_->param.dir == PJMEDIA_DIR_PLAYBACK) {
718             // Don't start the stream since Stop() has been requested.
719             if (state_ != STATE_PENDING_STOP) {
720                 StartStreamL();
721             } else {
722                 state_ = STATE_READY;
723             }
724         } else
725             InitRecL();
726     } else {
727         Deinit();
728     }
729 }
730
731 void CPjAudioEngine::NotifyError(const TInt aError)
732 {
733     Deinit();
734     snd_perror("Error from CQueueHandler", aError);
735 }
736
737 TInt CPjAudioEngine::ActivateSpeaker(TBool active)
738 {
739     if (state_ == STATE_READY || state_ == STATE_STREAMING) {
740         iSession.ActivateLoudspeaker(active);
741         TRACE_((THIS_FILE, "Loudspeaker turned %s", (active? "on":"off")));
742         return KErrNone;
743     }
744     return KErrNotReady;
745 }
746
747 /****************************************************************************
748  * Internal APS callbacks for PCM format
749  */
750
751 static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
752 {
753     struct aps_stream *strm = (struct aps_stream*) user_data;
754
755     /* Buffer has to contain normal speech. */
756     pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
757
758     /* Detect the recorder G.711 frame size, player frame size will follow
759      * this recorder frame size.
760      */
761     if (aps_g711_frame_len == 0) {
762         aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
763         TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
764                 aps_g711_frame_len));
765     }
766
767     /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
768      * Whenever rec_buf is full, call parent stream callback.
769      */
770     unsigned samples_processed = 0;
771
772     while (samples_processed < aps_g711_frame_len) {
773         unsigned samples_to_process;
774         unsigned samples_req;
775
776         samples_to_process = aps_g711_frame_len - samples_processed;
777         samples_req = (strm->param.samples_per_frame /
778                        strm->param.channel_count /
779                        strm->resample_factor) -
780                       strm->rec_buf_len;
781         if (samples_to_process > samples_req)
782             samples_to_process = samples_req;
783
784         pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
785                             buf.iBuffer.Ptr() + 2 + samples_processed,
786                             samples_to_process);
787
788         strm->rec_buf_len += samples_to_process;
789         samples_processed += samples_to_process;
790
791         /* Buffer is full, time to call parent callback */
792         if (strm->rec_buf_len == strm->param.samples_per_frame / 
793                                  strm->param.channel_count /
794                                  strm->resample_factor) 
795         {
796             pjmedia_frame f;
797
798             /* Need to resample clock rate? */
799             if (strm->rec_resample) {
800                 unsigned resampled = 0;
801                 
802                 while (resampled < strm->rec_buf_len) {
803                     pjmedia_resample_run(strm->rec_resample, 
804                                 &strm->rec_buf[resampled],
805                                 strm->pcm_buf + 
806                                 resampled * strm->resample_factor);
807                     resampled += 80;
808                 }
809                 f.buf = strm->pcm_buf;
810             } else {
811                 f.buf = strm->rec_buf;
812             }
813
814             /* Need to convert channel count? */
815             if (strm->param.channel_count != 1) {
816                 pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
817                                              (pj_int16_t*)f.buf,
818                                              strm->param.channel_count,
819                                              strm->param.samples_per_frame /
820                                              strm->param.channel_count,
821                                              0);
822             }
823
824             /* Call parent callback */
825             f.type = PJMEDIA_FRAME_TYPE_AUDIO;
826             f.size = strm->param.samples_per_frame << 1;
827             strm->rec_cb(strm->user_data, &f);
828             strm->rec_buf_len = 0;
829         }
830     }
831 }
832
833 static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
834 {
835     struct aps_stream *strm = (struct aps_stream*) user_data;
836     unsigned g711_frame_len = aps_g711_frame_len;
837
838     /* Init buffer attributes and header. */
839     buf.iCommand = CQueueHandler::EAPSPlayData;
840     buf.iStatus = 0;
841     buf.iBuffer.Zero();
842     buf.iBuffer.Append(1);
843     buf.iBuffer.Append(0);
844
845     /* Assume frame size is 10ms if frame size hasn't been known. */
846     if (g711_frame_len == 0)
847         g711_frame_len = 80;
848
849     /* Call parent stream callback to get PCM samples to play,
850      * encode the PCM samples into G.711 and put it into APS buffer.
851      */
852     unsigned samples_processed = 0;
853     
854     while (samples_processed < g711_frame_len) {
855         /* Need more samples to play, time to call parent callback */
856         if (strm->play_buf_len == 0) {
857             pjmedia_frame f;
858             unsigned samples_got;
859             
860             f.size = strm->param.samples_per_frame << 1;
861             if (strm->play_resample || strm->param.channel_count != 1)
862                 f.buf = strm->pcm_buf;
863             else
864                 f.buf = strm->play_buf;
865
866             /* Call parent callback */
867             strm->play_cb(strm->user_data, &f);
868             if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
869                 pjmedia_zero_samples((pj_int16_t*)f.buf, 
870                                      strm->param.samples_per_frame);
871             }
872             
873             samples_got = strm->param.samples_per_frame / 
874                           strm->param.channel_count /
875                           strm->resample_factor;
876
877             /* Need to convert channel count? */
878             if (strm->param.channel_count != 1) {
879                 pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
880                                              (pj_int16_t*)f.buf,
881                                              strm->param.channel_count,
882                                              strm->param.samples_per_frame,
883                                              PJ_FALSE,
884                                              0);
885             }
886
887             /* Need to resample clock rate? */
888             if (strm->play_resample) {
889                 unsigned resampled = 0;
890                 
891                 while (resampled < samples_got) 
892                 {
893                     pjmedia_resample_run(strm->play_resample, 
894                                 strm->pcm_buf + 
895                                 resampled * strm->resample_factor,
896                                 &strm->play_buf[resampled]);
897                     resampled += 80;
898                 }
899             }
900             
901             strm->play_buf_len = samples_got;
902             strm->play_buf_start = 0;
903         }
904
905         unsigned tmp;
906
907         tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
908         pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
909                             &strm->play_buf[strm->play_buf_start],
910                             tmp);
911         buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
912         samples_processed += tmp;
913         strm->play_buf_len -= tmp;
914         strm->play_buf_start += tmp;
915     }
916 }
917
918 /****************************************************************************
919  * Internal APS callbacks for non-PCM format
920  */
921
922 static void RecCb(TAPSCommBuffer &buf, void *user_data)
923 {
924     struct aps_stream *strm = (struct aps_stream*) user_data;
925     pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
926     
927     switch(strm->param.ext_fmt.id) {
928     case PJMEDIA_FORMAT_AMR:
929         {
930             const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 1;
931             unsigned len = buf.iBuffer.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 (buf.iBuffer[0] != 0 || buf.iBuffer[1] != 0) {
947                 enum { NORMAL_LEN = 22, SID_LEN = 8 };
948                 TBitStream *bitstream = (TBitStream*)strm->strm_data;
949                 unsigned src_len = buf.iBuffer.Length()- 2;
950                 
951                 pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
952
953                 const TDesC8& p = bitstream->CompressG729Frame(
954                                             buf.iBuffer.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 (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
980                 const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2;
981                 unsigned len = buf.iBuffer.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(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
1005
1006             /* Detect the recorder G.711 frame size, player frame size will 
1007              * follow this recorder frame size.
1008              */
1009             if (aps_g711_frame_len == 0) {
1010                 aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
1011                 TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
1012                         aps_g711_frame_len));
1013             }
1014             
1015             /* Convert APS 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 < aps_g711_frame_len) {
1020                 unsigned tmp;
1021                 const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() +
1022                                        2 + samples_processed;
1023     
1024                 tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
1025                              aps_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
1045 static void PlayCb(TAPSCommBuffer &buf, void *user_data)
1046 {
1047     struct aps_stream *strm = (struct aps_stream*) user_data;
1048     pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
1049
1050     /* Init buffer attributes and header. */
1051     buf.iCommand = CQueueHandler::EAPSPlayData;
1052     buf.iStatus = 0;
1053     buf.iBuffer.Zero();
1054
1055     switch(strm->param.ext_fmt.id) {
1056     case PJMEDIA_FORMAT_AMR:
1057         {
1058             if (frame->samples_cnt == 0) {
1059                 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1060                 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1061                 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1062                           frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1063             }
1064
1065             if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 
1066                 pjmedia_frame_ext_subframe *sf;
1067                 unsigned samples_cnt;
1068                 
1069                 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1070                 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1071                 
1072                 if (sf->data && sf->bitlen) {
1073                     /* AMR header for APS is one byte, the format (may be!):
1074                      * 0xxxxy00, where xxxx:frame type, y:not sure. 
1075                      */
1076                     unsigned len = (sf->bitlen+7)>>3;
1077                     enum {SID_FT = 8 };
1078                     pj_uint8_t amr_header = 4, ft = SID_FT;
1079
1080                     if (len >= pjmedia_codec_amrnb_framelen[0])
1081                         ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
1082                     
1083                     amr_header |= ft << 3;
1084                     buf.iBuffer.Append(amr_header);
1085                     
1086                     buf.iBuffer.Append((TUint8*)sf->data, len);
1087                 } else {
1088                     enum {NO_DATA_FT = 15 };
1089                     pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1090
1091                     buf.iBuffer.Append(amr_header);
1092                 }
1093
1094                 pjmedia_frame_ext_pop_subframes(frame, 1);
1095             
1096             } else { /* PJMEDIA_FRAME_TYPE_NONE */
1097                 enum {NO_DATA_FT = 15 };
1098                 pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
1099
1100                 buf.iBuffer.Append(amr_header);
1101
1102                 frame->samples_cnt = 0;
1103                 frame->subframe_cnt = 0;
1104             }
1105         }
1106         break;
1107         
1108     case PJMEDIA_FORMAT_G729:
1109         {
1110             if (frame->samples_cnt == 0) {
1111                 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1112                 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1113                 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1114                           frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1115             }
1116
1117             if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 
1118                 pjmedia_frame_ext_subframe *sf;
1119                 unsigned samples_cnt;
1120                 
1121                 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1122                 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1123                 
1124                 if (sf->data && sf->bitlen) {
1125                     enum { NORMAL_LEN = 10, SID_LEN = 2 };
1126                     pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
1127                     TBitStream *bitstream = (TBitStream*)strm->strm_data;
1128                     const TPtrC8 src(sf->data, sf->bitlen>>3);
1129                     const TDesC8 &dst = bitstream->ExpandG729Frame(src,
1130                                                                    sid_frame); 
1131                     if (sid_frame) {
1132                         buf.iBuffer.Append(2);
1133                         buf.iBuffer.Append(0);
1134                     } else {
1135                         buf.iBuffer.Append(1);
1136                         buf.iBuffer.Append(0);
1137                     }
1138                     buf.iBuffer.Append(dst);
1139                 } else {
1140                     buf.iBuffer.Append(2);
1141                     buf.iBuffer.Append(0);
1142                     buf.iBuffer.AppendFill(0, 22);
1143                 }
1144
1145                 pjmedia_frame_ext_pop_subframes(frame, 1);
1146             
1147             } else { /* PJMEDIA_FRAME_TYPE_NONE */
1148                 buf.iBuffer.Append(2);
1149                 buf.iBuffer.Append(0);
1150                 buf.iBuffer.AppendFill(0, 22);
1151                 
1152                 frame->samples_cnt = 0;
1153                 frame->subframe_cnt = 0;
1154             }
1155         }
1156         break;
1157         
1158     case PJMEDIA_FORMAT_ILBC:
1159         {
1160             if (frame->samples_cnt == 0) {
1161                 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1162                 strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1163                 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1164                           frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1165             }
1166
1167             if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 
1168                 pjmedia_frame_ext_subframe *sf;
1169                 unsigned samples_cnt;
1170                 
1171                 sf = pjmedia_frame_ext_get_subframe(frame, 0);
1172                 samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1173                 
1174                 pj_assert((strm->param.ext_fmt.bitrate == 15200 && 
1175                            samples_cnt == 160) ||
1176                           (strm->param.ext_fmt.bitrate != 15200 &&
1177                            samples_cnt == 240));
1178                 
1179                 if (sf->data && sf->bitlen) {
1180                     buf.iBuffer.Append(1);
1181                     buf.iBuffer.Append(0);
1182                     buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1183                 } else {
1184                     buf.iBuffer.Append(0);
1185                     buf.iBuffer.Append(0);
1186                 }
1187
1188                 pjmedia_frame_ext_pop_subframes(frame, 1);
1189             
1190             } else { /* PJMEDIA_FRAME_TYPE_NONE */
1191                 buf.iBuffer.Append(0);
1192                 buf.iBuffer.Append(0);
1193                 
1194                 frame->samples_cnt = 0;
1195                 frame->subframe_cnt = 0;
1196             }
1197         }
1198         break;
1199         
1200     case PJMEDIA_FORMAT_PCMU:
1201     case PJMEDIA_FORMAT_PCMA:
1202         {
1203             unsigned samples_ready = 0;
1204             unsigned samples_req = aps_g711_frame_len;
1205             
1206             /* Assume frame size is 10ms if frame size hasn't been known. */
1207             if (samples_req == 0)
1208                 samples_req = 80;
1209             
1210             buf.iBuffer.Append(1);
1211             buf.iBuffer.Append(0);
1212             
1213             /* Call parent stream callback to get samples to play. */
1214             while (samples_ready < samples_req) {
1215                 if (frame->samples_cnt == 0) {
1216                     frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
1217                     strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
1218                     pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
1219                               frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
1220                 }
1221     
1222                 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 
1223                     pjmedia_frame_ext_subframe *sf;
1224                     unsigned samples_cnt;
1225                     
1226                     sf = pjmedia_frame_ext_get_subframe(frame, 0);
1227                     samples_cnt = frame->samples_cnt / frame->subframe_cnt;
1228                     if (sf->data && sf->bitlen) {
1229                         buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
1230                     } else {
1231                         pj_uint8_t silc;
1232                         silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1233                                 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1234                         buf.iBuffer.AppendFill(silc, samples_cnt);
1235                     }
1236                     samples_ready += samples_cnt;
1237                     
1238                     pjmedia_frame_ext_pop_subframes(frame, 1);
1239                 
1240                 } else { /* PJMEDIA_FRAME_TYPE_NONE */
1241                     pj_uint8_t silc;
1242                     
1243                     silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
1244                             pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
1245                     buf.iBuffer.AppendFill(silc, samples_req - samples_ready);
1246
1247                     samples_ready = samples_req;
1248                     frame->samples_cnt = 0;
1249                     frame->subframe_cnt = 0;
1250                 }
1251             }
1252         }
1253         break;
1254         
1255     default:
1256         break;
1257     }
1258 }
1259
1260
1261 /****************************************************************************
1262  * Factory operations
1263  */
1264
1265 /*
1266  * C compatible declaration of APS factory.
1267  */
1268 PJ_BEGIN_DECL
1269 PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf);
1270 PJ_END_DECL
1271
1272 /*
1273  * Init APS audio driver.
1274  */
1275 PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf)
1276 {
1277     struct aps_factory *f;
1278     pj_pool_t *pool;
1279
1280     pool = pj_pool_create(pf, "APS", 1000, 1000, NULL);
1281     f = PJ_POOL_ZALLOC_T(pool, struct aps_factory);
1282     f->pf = pf;
1283     f->pool = pool;
1284     f->base.op = &factory_op;
1285
1286     return &f->base;
1287 }
1288
1289 /* API: init factory */
1290 static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
1291 {
1292     struct aps_factory *af = (struct aps_factory*)f;
1293
1294     pj_ansi_strcpy(af->dev_info.name, "S60 APS");
1295     af->dev_info.default_samples_per_sec = 8000;
1296     af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
1297                         //PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
1298                         PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
1299                         PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
1300                         PJMEDIA_AUD_DEV_CAP_VAD |
1301                         PJMEDIA_AUD_DEV_CAP_CNG;
1302     af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE | 
1303                           PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1304     af->dev_info.input_count = 1;
1305     af->dev_info.output_count = 1;
1306
1307     /* Enumerate codecs by trying to initialize each codec and examining
1308      * the error code. Consider the following:
1309      * - not possible to reinitialize the same APS session with 
1310      *   different settings,
1311      * - closing APS session and trying to immediately reconnect may fail,
1312      *   clients should wait ~5s before attempting to reconnect.
1313      */
1314
1315     unsigned i, fmt_cnt = 0;
1316     pj_bool_t g711_supported = PJ_FALSE;
1317
1318     /* Do not change the order! */
1319     TFourCC fourcc[] = {
1320         TFourCC(KMCPFourCCIdAMRNB),
1321         TFourCC(KMCPFourCCIdG711),
1322         TFourCC(KMCPFourCCIdG729),
1323         TFourCC(KMCPFourCCIdILBC)
1324     };
1325
1326     for (i = 0; i < PJ_ARRAY_SIZE(fourcc); ++i) {
1327         pj_bool_t supported = PJ_FALSE;
1328         unsigned retry_cnt = 0;
1329         enum { MAX_RETRY = 3 }; 
1330
1331 #if (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 0)
1332         /* Codec detection is disabled */
1333         supported = PJ_TRUE;
1334 #elif (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 1)
1335         /* Minimal codec detection, AMR-NB and G.711 only */
1336         if (i > 1) {
1337             /* If G.711 has been checked, skip G.729 and iLBC checks */
1338             retry_cnt = MAX_RETRY;
1339             supported = g711_supported;
1340         }
1341 #endif
1342         
1343         while (!supported && ++retry_cnt <= MAX_RETRY) {
1344             RAPSSession iSession;
1345             TAPSInitSettings iPlaySettings;
1346             TAPSInitSettings iRecSettings;
1347             TInt err;
1348
1349             // Recorder settings
1350             iRecSettings.iGlobal                = APP_UID;
1351             iRecSettings.iPriority              = TMdaPriority(100);
1352             iRecSettings.iPreference            = TMdaPriorityPreference(0x05210001);
1353             iRecSettings.iSettings.iChannels    = EMMFMono;
1354             iRecSettings.iSettings.iSampleRate  = EMMFSampleRate8000Hz;
1355
1356             // Player settings
1357             iPlaySettings.iGlobal               = APP_UID;
1358             iPlaySettings.iPriority             = TMdaPriority(100);
1359             iPlaySettings.iPreference           = TMdaPriorityPreference(0x05220001);
1360             iPlaySettings.iSettings.iChannels   = EMMFMono;
1361             iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
1362
1363             iRecSettings.iFourCC = iPlaySettings.iFourCC = fourcc[i];
1364
1365             err = iSession.Connect();
1366             if (err == KErrNone)
1367                 err = iSession.InitializePlayer(iPlaySettings);
1368             if (err == KErrNone)
1369                 err = iSession.InitializeRecorder(iRecSettings);
1370             
1371             // On some devices, immediate closing causes APS Server panic,
1372             // e.g: N95, so let's just wait for some time before closing.
1373             enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
1374             snd_wait(APS_CLOSE_WAIT_TIME);
1375             
1376             iSession.Close();
1377
1378             if (err == KErrNone) {
1379                 /* All fine, stop retyring */
1380                 supported = PJ_TRUE;
1381             }  else if (err == KErrAlreadyExists && retry_cnt < MAX_RETRY) {
1382                 /* Seems that the previous session is still arround,
1383                  * let's wait before retrying.
1384                  */
1385                 enum { RETRY_WAIT_TIME = 3000 }; /* in msecs */
1386                 snd_wait(RETRY_WAIT_TIME);
1387             } else {
1388                 /* Seems that this format is not supported */
1389                 retry_cnt = MAX_RETRY;
1390             }
1391         }
1392
1393         if (supported) {
1394             switch(i) {
1395             case 0: /* AMRNB */
1396                 af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_AMR;
1397                 af->dev_info.ext_fmt[fmt_cnt].bitrate = 7400;
1398                 af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1399                 ++fmt_cnt;
1400                 break;
1401             case 1: /* G.711 */
1402                 af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
1403                 af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
1404                 af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1405                 ++fmt_cnt;
1406                 af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
1407                 af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
1408                 af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1409                 ++fmt_cnt;
1410                 g711_supported = PJ_TRUE;
1411                 break;
1412             case 2: /* G.729 */
1413                 af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_G729;
1414                 af->dev_info.ext_fmt[fmt_cnt].bitrate = 8000;
1415                 af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
1416                 ++fmt_cnt;
1417                 break;
1418             case 3: /* iLBC */
1419                 af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
1420                 af->dev_info.ext_fmt[fmt_cnt].bitrate = 13333;
1421                 af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
1422                 ++fmt_cnt;
1423                 break;
1424             }
1425         }
1426     }
1427     
1428     af->dev_info.ext_fmt_cnt = fmt_cnt;
1429
1430     PJ_LOG(4, (THIS_FILE, "APS initialized"));
1431
1432     return PJ_SUCCESS;
1433 }
1434
1435 /* API: destroy factory */
1436 static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
1437 {
1438     struct aps_factory *af = (struct aps_factory*)f;
1439     pj_pool_t *pool = af->pool;
1440
1441     af->pool = NULL;
1442     pj_pool_release(pool);
1443
1444     PJ_LOG(4, (THIS_FILE, "APS destroyed"));
1445     
1446     return PJ_SUCCESS;
1447 }
1448
1449 /* API: refresh the device list */
1450 static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
1451 {
1452     PJ_UNUSED_ARG(f);
1453     return PJ_ENOTSUP;
1454 }
1455
1456 /* API: get number of devices */
1457 static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
1458 {
1459     PJ_UNUSED_ARG(f);
1460     return 1;
1461 }
1462
1463 /* API: get device info */
1464 static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, 
1465                                         unsigned index,
1466                                         pjmedia_aud_dev_info *info)
1467 {
1468     struct aps_factory *af = (struct aps_factory*)f;
1469
1470     PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1471
1472     pj_memcpy(info, &af->dev_info, sizeof(*info));
1473
1474     return PJ_SUCCESS;
1475 }
1476
1477 /* API: create default device parameter */
1478 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
1479                                          unsigned index,
1480                                          pjmedia_aud_param *param)
1481 {
1482     struct aps_factory *af = (struct aps_factory*)f;
1483
1484     PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
1485
1486     pj_bzero(param, sizeof(*param));
1487     param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1488     param->rec_id = index;
1489     param->play_id = index;
1490     param->clock_rate = af->dev_info.default_samples_per_sec;
1491     param->channel_count = 1;
1492     param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
1493     param->bits_per_sample = BITS_PER_SAMPLE;
1494     param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1495     param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1496
1497     return PJ_SUCCESS;
1498 }
1499
1500
1501 /* API: create stream */
1502 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1503                                          const pjmedia_aud_param *param,
1504                                          pjmedia_aud_rec_cb rec_cb,
1505                                          pjmedia_aud_play_cb play_cb,
1506                                          void *user_data,
1507                                          pjmedia_aud_stream **p_aud_strm)
1508 {
1509     struct aps_factory *af = (struct aps_factory*)f;
1510     pj_pool_t *pool;
1511     struct aps_stream *strm;
1512
1513     CPjAudioSetting aps_setting;
1514     PjAudioCallback aps_rec_cb;
1515     PjAudioCallback aps_play_cb;
1516
1517     /* Can only support 16bits per sample */
1518     PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
1519
1520     /* Supported clock rates:
1521      * - for non-PCM format: 8kHz  
1522      * - for PCM format: 8kHz and 16kHz  
1523      */
1524     PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
1525                      (param->clock_rate == 16000 && 
1526                       param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1527                      PJ_EINVAL);
1528
1529     /* Supported channels number:
1530      * - for non-PCM format: mono
1531      * - for PCM format: mono and stereo  
1532      */
1533     PJ_ASSERT_RETURN(param->channel_count == 1 || 
1534                      (param->channel_count == 2 &&
1535                       param->ext_fmt.id == PJMEDIA_FORMAT_L16),
1536                      PJ_EINVAL);
1537
1538     /* Create and Initialize stream descriptor */
1539     pool = pj_pool_create(af->pf, "aps-dev", 1000, 1000, NULL);
1540     PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
1541
1542     strm = PJ_POOL_ZALLOC_T(pool, struct aps_stream);
1543     strm->pool = pool;
1544     strm->param = *param;
1545
1546     if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
1547         strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
1548         
1549     /* Set audio engine fourcc. */
1550     switch(strm->param.ext_fmt.id) {
1551     case PJMEDIA_FORMAT_L16:
1552     case PJMEDIA_FORMAT_PCMU:
1553     case PJMEDIA_FORMAT_PCMA:
1554         aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
1555         break;
1556     case PJMEDIA_FORMAT_AMR:
1557         aps_setting.fourcc = TFourCC(KMCPFourCCIdAMRNB);
1558         break;
1559     case PJMEDIA_FORMAT_G729:
1560         aps_setting.fourcc = TFourCC(KMCPFourCCIdG729);
1561         break;
1562     case PJMEDIA_FORMAT_ILBC:
1563         aps_setting.fourcc = TFourCC(KMCPFourCCIdILBC);
1564         break;
1565     default:
1566         aps_setting.fourcc = 0;
1567         break;
1568     }
1569
1570     /* Set audio engine mode. */
1571     if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
1572     {
1573         aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.bitrate;
1574     } 
1575     else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1576              strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1577             (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC  &&
1578              strm->param.ext_fmt.bitrate != 15200))
1579     {
1580         aps_setting.mode = EULawOr30ms;
1581     } 
1582     else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1583             (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
1584              strm->param.ext_fmt.bitrate == 15200))
1585     {
1586         aps_setting.mode = EALawOr20ms;
1587     }
1588
1589     /* Disable VAD on L16, G711, and also G729 (G729's VAD potentially 
1590      * causes noise?).
1591      */
1592     if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
1593         strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
1594         strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
1595         strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
1596     {
1597         aps_setting.vad = EFalse;
1598     } else {
1599         aps_setting.vad = strm->param.ext_fmt.vad;
1600     }
1601     
1602     /* Set other audio engine attributes. */
1603     aps_setting.plc = strm->param.plc_enabled;
1604     aps_setting.cng = aps_setting.vad;
1605     aps_setting.loudspk = 
1606                 strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
1607
1608     /* Set audio engine callbacks. */
1609     if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
1610         aps_play_cb = &PlayCbPcm;
1611         aps_rec_cb  = &RecCbPcm;
1612     } else {
1613         aps_play_cb = &PlayCb;
1614         aps_rec_cb  = &RecCb;
1615     }
1616
1617     strm->rec_cb = rec_cb;
1618     strm->play_cb = play_cb;
1619     strm->user_data = user_data;
1620     strm->resample_factor = strm->param.clock_rate / 8000;
1621
1622     /* play_buf size is samples per frame scaled in to 8kHz mono. */
1623     strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
1624                                         pool, 
1625                                         (strm->param.samples_per_frame / 
1626                                         strm->resample_factor /
1627                                         strm->param.channel_count) << 1);
1628     strm->play_buf_len = 0;
1629     strm->play_buf_start = 0;
1630
1631     /* rec_buf size is samples per frame scaled in to 8kHz mono. */
1632     strm->rec_buf  = (pj_int16_t*)pj_pool_zalloc(
1633                                         pool, 
1634                                         (strm->param.samples_per_frame / 
1635                                         strm->resample_factor /
1636                                         strm->param.channel_count) << 1);
1637     strm->rec_buf_len = 0;
1638
1639     if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1640         TBitStream *g729_bitstream = new TBitStream;
1641         
1642         PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
1643         strm->strm_data = (void*)g729_bitstream;
1644     }
1645         
1646     /* Init resampler when format is PCM and clock rate is not 8kHz */
1647     if (strm->param.clock_rate != 8000 && 
1648         strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
1649     {
1650         pj_status_t status;
1651         
1652         if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1653             /* Create resample for recorder */
1654             status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1, 
1655                                               8000,
1656                                               strm->param.clock_rate,
1657                                               80,
1658                                               &strm->rec_resample);
1659             if (status != PJ_SUCCESS)
1660                 return status;
1661         }
1662     
1663         if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1664             /* Create resample for player */
1665             status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1, 
1666                                               strm->param.clock_rate,
1667                                               8000,
1668                                               80 * strm->resample_factor,
1669                                               &strm->play_resample);
1670             if (status != PJ_SUCCESS)
1671                 return status;
1672         }
1673     }
1674
1675     /* Create PCM buffer, when the clock rate is not 8kHz or not mono */
1676     if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
1677         (strm->resample_factor > 1 || strm->param.channel_count != 1)) 
1678     {
1679         strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool, 
1680                                         strm->param.samples_per_frame << 1);
1681     }
1682
1683     
1684     /* Create the audio engine. */
1685     TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
1686                                                    aps_rec_cb, aps_play_cb,
1687                                                    strm, aps_setting));
1688     if (err != KErrNone) {
1689         pj_pool_release(pool);
1690         return PJ_RETURN_OS_ERROR(err);
1691     }
1692
1693     /* Apply output volume setting if specified */
1694     if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1695         stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 
1696                        &param->output_vol);
1697     }
1698
1699     /* Done */
1700     strm->base.op = &stream_op;
1701     *p_aud_strm = &strm->base;
1702
1703     return PJ_SUCCESS;
1704 }
1705
1706 /* API: Get stream info. */
1707 static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1708                                     pjmedia_aud_param *pi)
1709 {
1710     struct aps_stream *strm = (struct aps_stream*)s;
1711
1712     PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1713
1714     pj_memcpy(pi, &strm->param, sizeof(*pi));
1715
1716     /* Update the output volume setting */
1717     if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1718                        &pi->output_vol) == PJ_SUCCESS)
1719     {
1720         pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1721     }
1722     
1723     return PJ_SUCCESS;
1724 }
1725
1726 /* API: get capability */
1727 static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1728                                   pjmedia_aud_dev_cap cap,
1729                                   void *pval)
1730 {
1731     struct aps_stream *strm = (struct aps_stream*)s;
1732     pj_status_t status = PJ_ENOTSUP;
1733
1734     PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1735
1736     switch (cap) {
1737     case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE: 
1738         if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1739             *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
1740             status = PJ_SUCCESS;
1741         }
1742         break;
1743     
1744     /* There is a case that GetMaxGain() stucks, e.g: in N95. */ 
1745     /*
1746     case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1747         if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1748             PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1749             
1750             TInt max_gain = strm->engine->GetMaxGain();
1751             TInt gain = strm->engine->GetGain();
1752             
1753             if (max_gain > 0 && gain >= 0) {
1754                 *(unsigned*)pval = gain * 100 / max_gain; 
1755                 status = PJ_SUCCESS;
1756             } else {
1757                 status = PJMEDIA_EAUD_NOTREADY;
1758             }
1759         }
1760         break;
1761     */
1762
1763     case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1764         if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1765             PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1766             
1767             TInt max_vol = strm->engine->GetMaxVolume();
1768             TInt vol = strm->engine->GetVolume();
1769             
1770             if (max_vol > 0 && vol >= 0) {
1771                 *(unsigned*)pval = vol * 100 / max_vol; 
1772                 status = PJ_SUCCESS;
1773             } else {
1774                 status = PJMEDIA_EAUD_NOTREADY;
1775             }
1776         }
1777         break;
1778     default:
1779         break;
1780     }
1781     
1782     return status;
1783 }
1784
1785 /* API: set capability */
1786 static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1787                                   pjmedia_aud_dev_cap cap,
1788                                   const void *pval)
1789 {
1790     struct aps_stream *strm = (struct aps_stream*)s;
1791     pj_status_t status = PJ_ENOTSUP;
1792
1793     PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1794
1795     switch (cap) {
1796     case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE: 
1797         if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1798             pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
1799             TInt err;
1800
1801             PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1802             
1803             switch (r) {
1804             case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
1805             case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
1806                 err = strm->engine->ActivateSpeaker(EFalse);
1807                 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1808                 break;
1809             case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
1810                 err = strm->engine->ActivateSpeaker(ETrue);
1811                 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1812                 break;
1813             default:
1814                 status = PJ_EINVAL;
1815                 break;
1816             }
1817             if (status == PJ_SUCCESS)
1818                 strm->param.output_route = r; 
1819         }
1820         break;
1821
1822     /* There is a case that GetMaxGain() stucks, e.g: in N95. */ 
1823     /*
1824     case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1825         if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1826             PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1827             
1828             TInt max_gain = strm->engine->GetMaxGain();
1829             if (max_gain > 0) {
1830                 TInt gain, err;
1831                 
1832                 gain = *(unsigned*)pval * max_gain / 100;
1833                 err = strm->engine->SetGain(gain);
1834                 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1835             } else {
1836                 status = PJMEDIA_EAUD_NOTREADY;
1837             }
1838             if (status == PJ_SUCCESS)
1839                 strm->param.input_vol = *(unsigned*)pval;
1840         }
1841         break;
1842     */
1843
1844     case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1845         if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1846             PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
1847             
1848             TInt max_vol = strm->engine->GetMaxVolume();
1849             if (max_vol > 0) {
1850                 TInt vol, err;
1851                 
1852                 vol = *(unsigned*)pval * max_vol / 100;
1853                 err = strm->engine->SetVolume(vol);
1854                 status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
1855             } else {
1856                 status = PJMEDIA_EAUD_NOTREADY;
1857             }
1858             if (status == PJ_SUCCESS)
1859                 strm->param.output_vol = *(unsigned*)pval;
1860         }
1861         break;
1862     default:
1863         break;
1864     }
1865     
1866     return status;
1867 }
1868
1869 /* API: Start stream. */
1870 static pj_status_t stream_start(pjmedia_aud_stream *strm)
1871 {
1872     struct aps_stream *stream = (struct aps_stream*)strm;
1873
1874     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1875
1876     if (stream->engine) {
1877         TInt err = stream->engine->StartL();
1878         if (err != KErrNone)
1879             return PJ_RETURN_OS_ERROR(err);
1880     }
1881
1882     return PJ_SUCCESS;
1883 }
1884
1885 /* API: Stop stream. */
1886 static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1887 {
1888     struct aps_stream *stream = (struct aps_stream*)strm;
1889
1890     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1891
1892     if (stream->engine) {
1893         stream->engine->Stop();
1894     }
1895
1896     return PJ_SUCCESS;
1897 }
1898
1899
1900 /* API: Destroy stream. */
1901 static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1902 {
1903     struct aps_stream *stream = (struct aps_stream*)strm;
1904
1905     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1906
1907     stream_stop(strm);
1908
1909     delete stream->engine;
1910     stream->engine = NULL;
1911
1912     if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
1913         TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
1914         stream->strm_data = NULL;
1915         delete g729_bitstream;
1916     }
1917
1918     pj_pool_t *pool;
1919     pool = stream->pool;
1920     if (pool) {
1921         stream->pool = NULL;
1922         pj_pool_release(pool);
1923     }
1924
1925     return PJ_SUCCESS;
1926 }
1927
1928 #endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
1929