Ticket #872: symbian-mda-g711.patch

File symbian-mda-g711.patch, 31.4 KB (added by bennylp, 15 years ago)

Initial work, untested (won't probably even compile)

  • pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp

     
    3838#include <mdaaudiooutputstream.h> 
    3939#include <mdaaudioinputstream.h> 
    4040 
     41#include "s60_g729_bitstream.h" 
    4142 
    4243#define THIS_FILE                       "symb_mda_dev.c" 
    4344#define BITS_PER_SAMPLE                 16 
     
    5051#   define TRACE_(st) 
    5152#endif 
    5253 
     54/* Forward decl */ 
     55class CPjSndUtility; 
    5356 
    5457/* MDA factory */ 
    5558struct mda_factory 
     
    5760    pjmedia_aud_dev_factory      base; 
    5861    pj_pool_t                   *pool; 
    5962    pj_pool_factory             *pf; 
     63    CPjSndUtility               *sndUtility; 
    6064    pjmedia_aud_dev_info         dev_info; 
    6165}; 
    6266 
     
    7478    pj_pool_t           *pool;                  /**< Memory pool.       */ 
    7579 
    7680    // Common settings. 
    77     pjmedia_aud_param param;            /**< Stream param.      */ 
     81    pjmedia_aud_param    param;                 /**< Stream param.      */ 
    7882 
    7983    // Audio engine 
    8084    CPjAudioInputEngine *in_engine;             /**< Record engine.     */ 
    8185    CPjAudioOutputEngine *out_engine;           /**< Playback engine.   */ 
     86     
     87    // Detected G.711 frame size 
     88    unsigned             mda_g711_frame_len; 
    8289}; 
    8390 
    8491 
     
    176183{ 
    177184    PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc)); 
    178185} 
    179   
     186 
     187static TFourCC format_id_to_fourcc(const pjmedia_format *fmt) 
     188{ 
     189    switch (fmt->id) { 
     190    case PJMEDIA_FORMAT_L16: 
     191    case PJMEDIA_FORMAT_PCMA: 
     192        return TFourCC('G', '7', '1', '1'); 
     193    case PJMEDIA_FORMAT_AMR: 
     194        return TFourCC(' ', 'A', 'M', 'R'); 
     195    case PJMEDIA_FORMAT_G729: 
     196        return TFourCC('G', '7', '2', '9'); 
     197    case PJMEDIA_FORMAT_ILBC: 
     198        return TFourCC('i', 'L', 'B', 'C'); 
     199    } 
     200     
     201    pj_assert(!"Unsupported codec"); 
     202    return TFourCC('G', '7', '1', '1'); 
     203} 
     204 
    180205////////////////////////////////////////////////////////////////////////////// 
    181206// 
    182207 
     
    240265 
    241266    // cache variable 
    242267    // to avoid calculating frame length repeatedly 
    243     TInt                     frameLen_; 
     268    TInt                     pcmFrameLen_; 
    244269     
    245270    // sometimes recorded size != requested framesize, so let's 
    246271    // provide a buffer to make sure the rec callback returning 
     
    248273    TUint8                  *frameRecBuf_; 
    249274    TInt                     frameRecBufLen_; 
    250275 
     276    // is the device opened in g711 format? 
     277    bool                     fmt_g711_; 
     278     
     279    // stuffs to process encoded frame 
     280    TBitStream               bitStream_; 
     281     
    251282    CPjAudioInputEngine(struct mda_stream *parent_strm, 
    252283            pjmedia_aud_rec_cb rec_cb, 
    253284                        void *user_data); 
    254285    void ConstructL(); 
    255286    TPtr8 & GetFrame(); 
    256287     
     288    void RecCbPcm(const TDesC8 &aBuffer); 
     289    void RecCbEncoded(const TDesC8 &aBuffer); 
     290     
    257291public: 
    258292    virtual void MaiscOpenComplete(TInt aError); 
    259293    virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer); 
     
    269303      recCb_(rec_cb), userData_(user_data),  
    270304      iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0), 
    271305      lastError_(KErrNone), timeStamp_(0), 
    272       frameLen_(parent_strm->param.samples_per_frame *  
    273                 BYTES_PER_SAMPLE), 
    274       frameRecBuf_(NULL), frameRecBufLen_(0) 
     306      pcmFrameLen_(parent_strm->param.samples_per_frame *  
     307                   BYTES_PER_SAMPLE), 
     308      frameRecBuf_(NULL), frameRecBufLen_(0), 
     309      fmt_g711_(false) 
    275310{ 
    276311} 
    277312 
     
    289324 
    290325void CPjAudioInputEngine::ConstructL() 
    291326{ 
    292     iStreamBuffer_ = HBufC8::NewL(frameLen_); 
     327    iStreamBuffer_ = HBufC8::NewL(pcmFrameLen_); 
    293328    CleanupStack::PushL(iStreamBuffer_); 
    294329 
    295     frameRecBuf_ = new TUint8[frameLen_*2]; 
     330    frameRecBuf_ = new TUint8[pcmFrameLen_*2]; 
    296331    CleanupStack::PushL(frameRecBuf_); 
    297332} 
    298333 
     
    362397    iInputStream_->Open(&iStreamSettings); 
    363398     
    364399    // Success 
    365     PJ_LOG(4,(THIS_FILE, "Sound capture started.")); 
    366400    return PJ_SUCCESS; 
    367401} 
    368402 
     
    390424 
    391425TPtr8 & CPjAudioInputEngine::GetFrame()  
    392426{ 
    393     //iStreamBuffer_->Des().FillZ(frameLen_); 
    394     iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_); 
     427    //iStreamBuffer_->Des().FillZ(pcmFrameLen_); 
     428    iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), pcmFrameLen_, pcmFrameLen_); 
    395429    return iFramePtr_; 
    396430} 
    397431 
     
    403437        return; 
    404438    } 
    405439 
     440    PJ_LOG(4,(THIS_FILE, "Sound capture started.")); 
     441     
    406442    // set stream priority to normal and time sensitive 
    407443    iInputStream_->SetPriority(EPriorityNormal,  
    408                                EMdaPriorityPreferenceTime);                              
    409  
     444                               EMdaPriorityPreferenceTime); 
     445     
     446    // set the stream format 
     447    TFourCC aFmt = format_id_to_fourcc(&parentStrm_->param.ext_fmt); 
     448    TRAPD(fmterr, iInputStream_->SetDataTypeL(aFmt)); 
     449    if (fmterr) { 
     450        if (parentStrm_->param.ext_fmt.id != 0) { 
     451            // This is error since we can't open codec as requested 
     452            snd_perror("Unable to set capture format", aError); 
     453            return; 
     454        } else { 
     455            // This is fine. The stream is opened in PCM but we couldn't 
     456            // set G.711 codec, so just revert back to use PCM. 
     457            fmt_g711_ = false; 
     458        } 
     459    } else { 
     460        if (parentStrm_->param.ext_fmt.id == 0) 
     461            fmt_g711_ = true; 
     462    } 
     463     
    410464    // Read the first frame. 
    411465    TPtr8 & frm = GetFrame(); 
    412466    TRAPD(err2, iInputStream_->ReadL(frm)); 
     
    415469    } 
    416470} 
    417471 
    418 void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,  
    419                                             const TDesC8 &aBuffer) 
     472// Record callback for PCM format. 
     473void CPjAudioInputEngine::RecCbPcm(const TDesC8 &aBuffer) 
    420474{ 
    421     lastError_ = aError; 
    422     if (aError != KErrNone) { 
    423         snd_perror("Error in MaiscBufferCopied()", aError); 
    424         return; 
    425     } 
    426  
    427     if (frameRecBufLen_ || aBuffer.Length() < frameLen_) { 
    428         pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length()); 
     475    if (fmt_g711_) { 
     476        pj_assert(aBuffer.Length() >= 2); 
     477        if (aBuffer[0]==1 && aBuffer[1]==0) { 
     478            /* Normal voice */ 
     479            pjmedia_alaw_decode((short*)(frameRecBuf_ + frameRecBufLen_),  
     480                                ((const pj_uint8_t*)aBuffer.Ptr())+2,  
     481                                aBuffer.Length()-2); 
     482            frameRecBufLen_ += ((aBuffer.Length()-2) << 1); 
     483        } else { 
     484            /* Zeroes or SID */ 
     485            pj_bzero(frameRecBuf_ + frameRecBufLen_, ((aBuffer.Length()-2) << 1)); 
     486            frameRecBufLen_ += ((aBuffer.Length()-2) << 1); 
     487        } 
     488    } else { 
     489        pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(),  
     490                  aBuffer.Length()); 
    429491        frameRecBufLen_ += aBuffer.Length(); 
    430492    } 
    431  
     493         
    432494    if (frameRecBufLen_) { 
    433         while (frameRecBufLen_ >= frameLen_) { 
     495        while (frameRecBufLen_ >= pcmFrameLen_) { 
    434496            pjmedia_frame f; 
    435497             
    436498            f.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    437499            f.buf = frameRecBuf_; 
    438             f.size = frameLen_; 
     500            f.size = pcmFrameLen_; 
    439501            f.timestamp.u32.lo = timeStamp_; 
    440502            f.bit_info = 0; 
    441503             
     
    444506            // Increment timestamp. 
    445507            timeStamp_ += parentStrm_->param.samples_per_frame; 
    446508 
    447             frameRecBufLen_ -= frameLen_; 
    448             pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_); 
     509            frameRecBufLen_ -= pcmFrameLen_; 
     510            pj_memmove(frameRecBuf_, frameRecBuf_+pcmFrameLen_, frameRecBufLen_); 
    449511        } 
    450     } else { 
    451         pjmedia_frame f; 
    452          
    453         f.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    454         f.buf = (void*)aBuffer.Ptr(); 
    455         f.size = aBuffer.Length(); 
    456         f.timestamp.u32.lo = timeStamp_; 
    457         f.bit_info = 0; 
    458          
    459         // Call the callback. 
    460         recCb_(userData_, &f); 
    461          
    462         // Increment timestamp. 
    463         timeStamp_ += parentStrm_->param.samples_per_frame; 
    464512    } 
    465  
     513     
    466514    // Record next frame 
    467515    TPtr8 & frm = GetFrame(); 
    468516    TRAPD(err2, iInputStream_->ReadL(frm)); 
     
    471519    } 
    472520} 
    473521 
     522// Record callback for encoded format. 
     523void CPjAudioInputEngine::RecCbEncoded(const TDesC8 &aBuffer) 
     524{ 
     525    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) frameRecBuf_; 
     526     
     527    switch(parentStrm_->param.ext_fmt.id) { 
     528    case PJMEDIA_FORMAT_AMR: 
     529        { 
     530            const pj_uint8_t *p = (const pj_uint8_t*)aBuffer.Ptr() + 1; 
     531            unsigned len = aBuffer.Length() - 1; 
     532             
     533            pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160); 
     534            if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 
     535                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     536                recCb_(userData_, (pjmedia_frame*)frame); 
     537                frame->samples_cnt = 0; 
     538                frame->subframe_cnt = 0; 
     539            } 
     540        } 
     541        break; 
     542         
     543    case PJMEDIA_FORMAT_G729: 
     544        { 
     545            /* Check if we got a normal or SID frame. */ 
     546            if (aBuffer[0] != 0 || aBuffer[1] != 0) { 
     547                enum { NORMAL_LEN = 22, SID_LEN = 8 }; 
     548                unsigned src_len = aBuffer.Length()- 2; 
     549                 
     550                pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN); 
    474551 
     552                const TDesC8& p = bitStream_.CompressG729Frame( 
     553                                            aBuffer.Right(src_len),  
     554                                            src_len == SID_LEN); 
     555                 
     556                pjmedia_frame_ext_append_subframe(frame, p.Ptr(),  
     557                                                  p.Length() << 3, 80); 
     558            } else { /* We got null frame. */ 
     559                pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80); 
     560            } 
     561             
     562            if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 
     563                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     564                recCb_(userData_, (pjmedia_frame*)frame); 
     565                frame->samples_cnt = 0; 
     566                frame->subframe_cnt = 0; 
     567            } 
     568        } 
     569        break; 
     570 
     571    case PJMEDIA_FORMAT_ILBC: 
     572        { 
     573            unsigned samples_got; 
     574             
     575            samples_got = (parentStrm_->param.ext_fmt.bitrate) == 15200?  
     576                                160 : 240; 
     577             
     578            /* Check if we got a normal frame. */ 
     579            if (aBuffer[0] == 1 && aBuffer[1] == 0) { 
     580                const pj_uint8_t *p = (const pj_uint8_t*)aBuffer.Ptr() + 2; 
     581                unsigned len = aBuffer.Length() - 2; 
     582                 
     583                pjmedia_frame_ext_append_subframe(frame, p, len << 3, 
     584                                                  samples_got); 
     585            } else { /* We got null frame. */ 
     586                pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got); 
     587            } 
     588             
     589            if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 
     590                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     591                recCb_(userData_, (pjmedia_frame*)frame); 
     592                frame->samples_cnt = 0; 
     593                frame->subframe_cnt = 0; 
     594            } 
     595        } 
     596        break; 
     597         
     598    case PJMEDIA_FORMAT_PCMU: 
     599    case PJMEDIA_FORMAT_PCMA: 
     600        { 
     601            unsigned samples_processed = 0; 
     602             
     603            /* Make sure it is normal frame. */ 
     604            pj_assert(aBuffer[0] == 1 && aBuffer[1] == 0); 
     605 
     606            /* Detect the recorder G.711 frame size, player frame size will  
     607             * follow this recorder frame size. 
     608             */ 
     609            if (parentStrm_->mda_g711_frame_len == 0) { 
     610                parentStrm_->mda_g711_frame_len = aBuffer.Length()-2; 
     611                TRACE_((THIS_FILE, "Detected MDA G.711 frame size = %u samples", 
     612                        parentStrm_->mda_g711_frame_len)); 
     613            } 
     614             
     615            /* Convert APS buffer format into pjmedia_frame_ext. Whenever  
     616             * samples count in the frame is equal to stream's samples per  
     617             * frame, call parent stream callback. 
     618             */ 
     619            while (samples_processed < parentStrm_->mda_g711_frame_len) { 
     620                unsigned tmp; 
     621                const pj_uint8_t *pb = (const pj_uint8_t*)aBuffer.Ptr() + 
     622                                       2 + samples_processed; 
     623     
     624                tmp = PJ_MIN(parentStrm_->param.samples_per_frame - frame->samples_cnt, 
     625                             parentStrm_->mda_g711_frame_len - samples_processed); 
     626                 
     627                pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp); 
     628                samples_processed += tmp; 
     629     
     630                if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 
     631                    frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     632                    recCb_(userData_, (pjmedia_frame*)frame); 
     633                    frame->samples_cnt = 0; 
     634                    frame->subframe_cnt = 0; 
     635                } 
     636            } 
     637        } 
     638        break; 
     639         
     640    default: 
     641        break; 
     642    } 
     643 
     644} 
     645 
     646void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,  
     647                                            const TDesC8 &aBuffer) 
     648{ 
     649    lastError_ = aError; 
     650    if (aError != KErrNone) { 
     651        snd_perror("Error in MaiscBufferCopied()", aError); 
     652        return; 
     653    } 
     654     
     655    if (this->parentStrm_->param.ext_fmt.id == 0) 
     656        RecCbPcm(aBuffer); 
     657    else 
     658        RecCbEncoded(aBuffer); 
     659} 
     660 
     661 
    475662void CPjAudioInputEngine::MaiscRecordComplete(TInt aError) 
    476663{ 
    477664    lastError_ = aError; 
     
    540727    pjmedia_aud_play_cb      playCb_; 
    541728    void                    *userData_; 
    542729    CMdaAudioOutputStream   *iOutputStream_; 
    543     TUint8                  *frameBuf_; 
    544     unsigned                 frameBufSize_; 
     730    TUint8                  *frameBuf_;         // buffer for playCb_ 
     731    unsigned                 frameBufSize_;     // left over samples in frameBuf_ 
     732    unsigned                 frameBufCapacity_; // frameBuf_ capacity 
    545733    TPtrC8                   frame_; 
    546734    TInt                     lastError_; 
    547735    unsigned                 timestamp_; 
    548  
     736    bool                     fmt_g711_; 
     737    pj_uint8_t              *tmpEncodedFrame_; 
     738     
    549739    CPjAudioOutputEngine(struct mda_stream *parent_strm, 
    550740                         pjmedia_aud_play_cb play_cb, 
    551741                         void *user_data); 
     
    554744    virtual void MaoscOpenComplete(TInt aError); 
    555745    virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer); 
    556746    virtual void MaoscPlayComplete(TInt aError); 
     747     
     748    bool GetFramePcm(); 
     749    bool PlayFramePcm(); 
     750    bool PlayFrameEncoded(); 
    557751}; 
    558752 
    559753 
     
    561755                                           pjmedia_aud_play_cb play_cb, 
    562756                                           void *user_data)  
    563757: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),  
    564   userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL), 
    565   lastError_(KErrNone), timestamp_(0) 
     758  userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL), frameBufSize_(0), 
     759  lastError_(KErrNone), timestamp_(0), fmt_g711_(false), tmpEncodedFrame_(NULL) 
    566760{ 
    567761} 
    568762 
    569763 
    570764void CPjAudioOutputEngine::ConstructL() 
    571765{ 
    572     frameBufSize_ = parentStrm_->param.samples_per_frame * 
    573                     BYTES_PER_SAMPLE; 
    574     frameBuf_ = new TUint8[frameBufSize_]; 
     766    frameBufCapacity_ = parentStrm_->param.samples_per_frame * 2 * 
     767                        BYTES_PER_SAMPLE; 
     768    frameBuf_ = new TUint8[frameBufCapacity_]; 
     769    tmpEncodedFrame_ = new TUint8[parentStrm_->param.samples_per_frame+2]; 
    575770} 
    576771 
    577772CPjAudioOutputEngine::~CPjAudioOutputEngine() 
    578773{ 
    579774    Stop(); 
    580     delete [] frameBuf_;         
     775    delete [] frameBuf_; 
     776    delete [] tmpEncodedFrame_; 
    581777} 
    582778 
    583779CPjAudioOutputEngine * 
     
    663859    state_ = STATE_INACTIVE; 
    664860} 
    665861 
     862bool CPjAudioOutputEngine::GetFramePcm() 
     863{ 
     864    pjmedia_frame f; 
     865    unsigned pcm_size = parentStrm_->param.samples_per_frame * BYTES_PER_SAMPLE; 
     866    pj_status_t status; 
     867     
     868    f.type = PJMEDIA_FRAME_TYPE_AUDIO; 
     869    f.buf = frameBuf_+frameBufSize_; 
     870    f.size = pcm_size; 
     871    f.timestamp.u32.lo = timestamp_; 
     872    f.bit_info = 0; 
     873     
     874    status = playCb_(this->userData_, &f); 
     875    if (status != PJ_SUCCESS) { 
     876        this->Stop(); 
     877        return false; 
     878    } 
     879 
     880    if (f.type != PJMEDIA_FRAME_TYPE_AUDIO || f.size == 0) { 
     881        pj_bzero(frameBuf_, pcm_size); 
     882        f.size = pcm_size; 
     883    } 
     884     
     885    frameBufSize_ += pcm_size; 
     886     
     887    // Increment timestamp. 
     888    timestamp_ += (pcm_size / BYTES_PER_SAMPLE); 
     889 
     890    return true; 
     891} 
     892 
     893bool CPjAudioOutputEngine::PlayFramePcm() 
     894{ 
     895    if (fmt_g711_) { 
     896        // Here we open MDA in G.711 mode although user asked PCM 
     897        unsigned frame_size = parentStrm_->mda_g711_frame_len; 
     898         
     899        if (frame_size==0) 
     900            frame_size = 320; 
     901         
     902        // If we don't have enough samples in the buffer to fill in one frame, 
     903        // ask user. 
     904        if (frameBufSize_ < frame_size) { 
     905            if (!GetFramePcm()) 
     906                return false; 
     907        } 
     908         
     909        pj_assert(frameBufSize_ >= frame_size); 
     910         
     911        pjmedia_alaw_encode((pj_uint8_t*)tmpEncodedFrame_+2,  
     912                            (const pj_int16_t*)frameBuf_,  
     913                            frame_size >> 1); 
     914        tmpEncodedFrame_[0] = 1; 
     915        tmpEncodedFrame_[1] = 0; 
     916        frame_.Set(tmpEncodedFrame_, (frame_size >> 1) + 2); 
     917        iOutputStream_->WriteL(frame_); 
     918         
     919        frameBufSize_ -= frame_size; 
     920        pj_memmove(frameBuf_, frameBuf_ + frame_size, frameBufSize_); 
     921    } else { 
     922        // Here we open MDA in PCM mode, so just return the frame 
     923        pj_assert(frameBufSize_ == 0); 
     924         
     925        if (!GetFramePcm()) 
     926            return false; 
     927         
     928        frame_.Set(frameBuf_, frameBufSize_); 
     929        iOutputStream_->WriteL(frame_); 
     930         
     931        frameBufSize_ = 0; 
     932    } 
     933     
     934    return true; 
     935} 
     936 
     937bool CPjAudioOutputEngine::PlayFrameEncoded() 
     938{ 
     939    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) frameBuf_; 
     940 
     941    switch(parentStrm_->param.ext_fmt.id) { 
     942    case PJMEDIA_FORMAT_AMR: 
     943        { 
     944            if (frame->samples_cnt == 0) { 
     945                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     946                strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 
     947                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 
     948                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 
     949            } 
     950 
     951            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {  
     952                pjmedia_frame_ext_subframe *sf; 
     953                unsigned samples_cnt; 
     954                 
     955                sf = pjmedia_frame_ext_get_subframe(frame, 0); 
     956                samples_cnt = frame->samples_cnt / frame->subframe_cnt; 
     957                 
     958                if (sf->data && sf->bitlen) { 
     959                    /* AMR header for APS is one byte, the format (may be!): 
     960                     * 0xxxxy00, where xxxx:frame type, y:not sure.  
     961                     */ 
     962                    unsigned len = (sf->bitlen+7)>>3; 
     963                    enum {SID_FT = 8 }; 
     964                    pj_uint8_t amr_header = 4, ft = SID_FT; 
     965 
     966                    if (len >= pjmedia_codec_amrnb_framelen[0]) 
     967                        ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len); 
     968                     
     969                    amr_header |= ft << 3; 
     970                    buf.iBuffer.Append(amr_header); 
     971                     
     972                    buf.iBuffer.Append((TUint8*)sf->data, len); 
     973                } else { 
     974                    buf.iBuffer.Append(0); 
     975                } 
     976 
     977                pjmedia_frame_ext_pop_subframes(frame, 1); 
     978             
     979            } else { /* PJMEDIA_FRAME_TYPE_NONE */ 
     980                buf.iBuffer.Append(0); 
     981                 
     982                frame->samples_cnt = 0; 
     983                frame->subframe_cnt = 0; 
     984            } 
     985        } 
     986        break; 
     987         
     988    case PJMEDIA_FORMAT_G729: 
     989        { 
     990            if (frame->samples_cnt == 0) { 
     991                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     992                strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 
     993                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 
     994                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 
     995            } 
     996 
     997            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {  
     998                pjmedia_frame_ext_subframe *sf; 
     999                unsigned samples_cnt; 
     1000                 
     1001                sf = pjmedia_frame_ext_get_subframe(frame, 0); 
     1002                samples_cnt = frame->samples_cnt / frame->subframe_cnt; 
     1003                 
     1004                if (sf->data && sf->bitlen) { 
     1005                    enum { NORMAL_LEN = 10, SID_LEN = 2 }; 
     1006                    pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN); 
     1007                    TBitStream *bitstream = (TBitStream*)strm->strm_data; 
     1008                    const TPtrC8 src(sf->data, sf->bitlen>>3); 
     1009                    const TDesC8 &dst = bitstream->ExpandG729Frame(src, 
     1010                                                                   sid_frame);  
     1011                    if (sid_frame) { 
     1012                        buf.iBuffer.Append(0); 
     1013                        buf.iBuffer.Append(1); 
     1014                    } else { 
     1015                        buf.iBuffer.Append(1); 
     1016                        buf.iBuffer.Append(0); 
     1017                    } 
     1018                    buf.iBuffer.Append(dst); 
     1019                } else { 
     1020                    buf.iBuffer.Append(0); 
     1021                    buf.iBuffer.Append(0); 
     1022                } 
     1023 
     1024                pjmedia_frame_ext_pop_subframes(frame, 1); 
     1025             
     1026            } else { /* PJMEDIA_FRAME_TYPE_NONE */ 
     1027                buf.iBuffer.Append(0); 
     1028                buf.iBuffer.Append(0); 
     1029                 
     1030                frame->samples_cnt = 0; 
     1031                frame->subframe_cnt = 0; 
     1032            } 
     1033        } 
     1034        break; 
     1035         
     1036    case PJMEDIA_FORMAT_ILBC: 
     1037        { 
     1038            if (frame->samples_cnt == 0) { 
     1039                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     1040                strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 
     1041                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 
     1042                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 
     1043            } 
     1044 
     1045            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {  
     1046                pjmedia_frame_ext_subframe *sf; 
     1047                unsigned samples_cnt; 
     1048                 
     1049                sf = pjmedia_frame_ext_get_subframe(frame, 0); 
     1050                samples_cnt = frame->samples_cnt / frame->subframe_cnt; 
     1051                 
     1052                pj_assert((strm->param.ext_fmt.bitrate == 15200 &&  
     1053                           samples_cnt == 160) || 
     1054                          (strm->param.ext_fmt.bitrate != 15200 && 
     1055                           samples_cnt == 240)); 
     1056                 
     1057                if (sf->data && sf->bitlen) { 
     1058                    buf.iBuffer.Append(1); 
     1059                    buf.iBuffer.Append(0); 
     1060                    buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3); 
     1061                } else { 
     1062                    buf.iBuffer.Append(0); 
     1063                    buf.iBuffer.Append(0); 
     1064                } 
     1065 
     1066                pjmedia_frame_ext_pop_subframes(frame, 1); 
     1067             
     1068            } else { /* PJMEDIA_FRAME_TYPE_NONE */ 
     1069                buf.iBuffer.Append(0); 
     1070                buf.iBuffer.Append(0); 
     1071                 
     1072                frame->samples_cnt = 0; 
     1073                frame->subframe_cnt = 0; 
     1074            } 
     1075        } 
     1076        break; 
     1077         
     1078    case PJMEDIA_FORMAT_PCMU: 
     1079    case PJMEDIA_FORMAT_PCMA: 
     1080        { 
     1081            unsigned samples_ready = 0; 
     1082            unsigned samples_req = aps_g711_frame_len; 
     1083             
     1084            /* Assume frame size is 10ms if frame size hasn't been known. */ 
     1085            if (samples_req == 0) 
     1086                samples_req = 80; 
     1087             
     1088            buf.iBuffer.Append(1); 
     1089            buf.iBuffer.Append(0); 
     1090             
     1091            /* Call parent stream callback to get samples to play. */ 
     1092            while (samples_ready < samples_req) { 
     1093                if (frame->samples_cnt == 0) { 
     1094                    frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 
     1095                    strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 
     1096                    pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 
     1097                              frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 
     1098                } 
     1099     
     1100                if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {  
     1101                    pjmedia_frame_ext_subframe *sf; 
     1102                    unsigned samples_cnt; 
     1103                     
     1104                    sf = pjmedia_frame_ext_get_subframe(frame, 0); 
     1105                    samples_cnt = frame->samples_cnt / frame->subframe_cnt; 
     1106                    if (sf->data && sf->bitlen) { 
     1107                        buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3); 
     1108                    } else { 
     1109                        pj_uint8_t silc; 
     1110                        silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)? 
     1111                                pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0); 
     1112                        buf.iBuffer.AppendFill(silc, samples_cnt); 
     1113                    } 
     1114                    samples_ready += samples_cnt; 
     1115                     
     1116                    pjmedia_frame_ext_pop_subframes(frame, 1); 
     1117                 
     1118                } else { /* PJMEDIA_FRAME_TYPE_NONE */ 
     1119                    pj_uint8_t silc; 
     1120                     
     1121                    silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)? 
     1122                            pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0); 
     1123                    buf.iBuffer.AppendFill(silc, samples_req - samples_ready); 
     1124 
     1125                    samples_ready = samples_req; 
     1126                    frame->samples_cnt = 0; 
     1127                    frame->subframe_cnt = 0; 
     1128                } 
     1129            } 
     1130        } 
     1131        break; 
     1132         
     1133    default: 
     1134        break; 
     1135    } 
     1136} 
     1137 
    6661138void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError) 
    6671139{ 
    6681140    lastError_ = aError; 
     
    6881160        iOutputStream_->SetPriority(EPriorityNormal,  
    6891161                                    EMdaPriorityPreferenceTime);                                 
    6901162 
    691         // Call callback to retrieve frame from upstream. 
    692         pjmedia_frame f; 
    693         pj_status_t status; 
    694          
    695         f.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    696         f.buf = frameBuf_; 
    697         f.size = frameBufSize_; 
    698         f.timestamp.u32.lo = timestamp_; 
    699         f.bit_info = 0; 
    700  
    701         status = playCb_(this->userData_, &f); 
    702         if (status != PJ_SUCCESS) { 
    703             this->Stop(); 
    704             return; 
     1163        // set format 
     1164        TFourCC aFmt = format_id_to_fourcc(&parentStrm_->param.ext_fmt); 
     1165        TRAPD(ferr, iOutputStream_->SetDataTypeL(aFmt)); 
     1166        if (ferr) { 
     1167            if (parentStrm_->param.ext_fmt.id != 0) { 
     1168                // This is error since we can't open codec as requested 
     1169                snd_perror("Unable to set playback format", aError); 
     1170                return; 
     1171            } else { 
     1172                // This is fine. The stream is opened in PCM but we couldn't 
     1173                // set G.711 codec, so just revert back to use PCM. 
     1174                fmt_g711_ = false; 
     1175            } 
     1176        } else { 
     1177            if (parentStrm_->param.ext_fmt.id == 0) 
     1178                fmt_g711_ = true; 
    7051179        } 
    7061180 
    707         if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) 
    708             pj_bzero(frameBuf_, frameBufSize_); 
     1181        // Kick start the first Write 
     1182        TPtrC8 dummy; 
     1183        MaoscBufferCopied(KErrNone, dummy); 
    7091184         
    710         // Increment timestamp. 
    711         timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE); 
    712  
    713         // issue WriteL() to write the first audio data block,  
    714         // subsequent calls to WriteL() will be issued in  
    715         // MMdaAudioOutputStreamCallback::MaoscBufferCopied()  
    716         // until whole data buffer is written. 
    717         frame_.Set(frameBuf_, frameBufSize_); 
    718         iOutputStream_->WriteL(frame_); 
    7191185    } else { 
    7201186        snd_perror("Error in MaoscOpenComplete()", aError); 
    7211187    } 
     
    7281194 
    7291195    if (aError==KErrNone) { 
    7301196        // Buffer successfully written, feed another one. 
     1197        if (parentStrm_->param.ext_fmt.id == 0) 
     1198            PlayFramePcm(); 
     1199        else 
     1200            PlayFrameEncoded(); 
    7311201 
    732         // Call callback to retrieve frame from upstream. 
    733         pjmedia_frame f; 
    734         pj_status_t status; 
    735          
    736         f.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    737         f.buf = frameBuf_; 
    738         f.size = frameBufSize_; 
    739         f.timestamp.u32.lo = timestamp_; 
    740         f.bit_info = 0; 
    741  
    742         status = playCb_(this->userData_, &f); 
    743         if (status != PJ_SUCCESS) { 
    744             this->Stop(); 
    745             return; 
    746         } 
    747  
    748         if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) 
    749             pj_bzero(frameBuf_, frameBufSize_); 
    750          
    751         // Increment timestamp. 
    752         timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE); 
    753  
    754         // Write to playback stream. 
    755         frame_.Set(frameBuf_, frameBufSize_); 
    756         iOutputStream_->WriteL(frame_); 
    757  
    7581202    } else if (aError==KErrAbort) { 
    7591203        // playing was aborted, due to call to CMdaAudioOutputStream::Stop() 
    7601204        state_ = STATE_INACTIVE; 
     
    7761220} 
    7771221 
    7781222/**************************************************************************** 
     1223 * MDA format enumerator 
     1224 */ 
     1225class CPjSndUtility : public CBase, MMdaAudioInputStreamCallback 
     1226{ 
     1227public: 
     1228    CPjSndUtility(); 
     1229    ~CPjSndUtility(); 
     1230     
     1231    bool Busy() const; 
     1232    pj_status_t EnumFormats(pjmedia_format fmt[], unsigned *count); 
     1233     
     1234private: 
     1235    CMdaAudioInputStream  *iInputStream_; 
     1236    bool done_; 
     1237    bool stopped_; 
     1238    pjmedia_format *fmt_; 
     1239    unsigned max_fmt_; 
     1240    unsigned *pcount_; 
     1241    TInt lastError_; 
     1242     
     1243    virtual void MaiscOpenComplete(TInt aError); 
     1244    virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer); 
     1245    virtual void MaiscRecordComplete(TInt aError); 
     1246}; 
     1247 
     1248CPjSndUtility::CPjSndUtility() 
     1249: iInputStream_(NULL) 
     1250{ 
     1251     
     1252} 
     1253 
     1254CPjSndUtility::~CPjSndUtility() 
     1255{ 
     1256    delete iInputStream_; 
     1257} 
     1258 
     1259bool CPjSndUtility::Busy() const 
     1260{ 
     1261    return iInputStream_ != NULL; 
     1262} 
     1263 
     1264pj_status_t CPjSndUtility::EnumFormats(pjmedia_format fmt[], unsigned *count) 
     1265{ 
     1266    unsigned i; 
     1267     
     1268    if (Busy()) { 
     1269        // Already running 
     1270        return PJ_EINVALIDOP; 
     1271    } 
     1272 
     1273    fmt_ = fmt; 
     1274    pcount_ = count; 
     1275    max_fmt_ = *count; 
     1276    done_ = false; 
     1277     
     1278    // Create the stream. 
     1279    TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this)); 
     1280    if (err != KErrNone) 
     1281        return PJ_RETURN_OS_ERROR(err); 
     1282 
     1283    // Initialize settings. 
     1284    TMdaAudioDataSettings iStreamSettings; 
     1285    iStreamSettings.iChannels = get_channel_cap(1); 
     1286    iStreamSettings.iSampleRate = get_clock_rate_cap(8000); 
     1287 
     1288    pj_assert(iStreamSettings.iChannels != 0 &&  
     1289              iStreamSettings.iSampleRate != 0); 
     1290 
     1291    // Open stream. 
     1292    lastError_ = KRequestPending; 
     1293    iInputStream_->Open(&iStreamSettings); 
     1294     
     1295    for (i=0; i<100 && !done_; ++i) 
     1296        pj_symbianos_poll(-1, 50); 
     1297 
     1298    stopped_ = false; 
     1299    iInputStream_->Stop(); 
     1300    for (i=0; i<10 && !stopped_; ++i) 
     1301        pj_symbianos_poll(-1, 50); 
     1302  
     1303    if (stopped_) { 
     1304        delete iInputStream_; 
     1305        iInputStream_ = NULL; 
     1306    } 
     1307 
     1308    if (!done_) 
     1309        return PJ_RETURN_OS_ERROR(KRequestPending); 
     1310     
     1311    return PJ_RETURN_OS_ERROR(lastError_); 
     1312} 
     1313 
     1314void CPjSndUtility::MaiscOpenComplete(TInt aError) 
     1315{ 
     1316    struct fmt_info 
     1317    { 
     1318        TFourCC          fourcc; 
     1319        const char      *name; 
     1320        pjmedia_format   fmt; 
     1321    } fmt[8]; 
     1322    unsigned i, cnt = 0; 
     1323     
     1324    lastError_ = aError; 
     1325    if (aError != KErrNone) { 
     1326        snd_perror("Error in MaiscOpenComplete()", aError); 
     1327        done_ = true; 
     1328        return; 
     1329    } 
     1330 
     1331    pj_bzero(fmt, sizeof(fmt)); 
     1332     
     1333    // G.711 Alaw 
     1334    fmt[cnt].fourcc = TFourCC('G', '7', '1', '1'); 
     1335    fmt[cnt].fmt.id = PJMEDIA_FORMAT_ALAW; 
     1336    fmt[cnt].fmt.bitrate = 64000; 
     1337    fmt[cnt].fmt.vad = PJ_TRUE; 
     1338    cnt++; 
     1339     
     1340    // AMR-NB 
     1341    fmt[cnt].fourcc = TFourCC(' ', 'A', 'M', 'R'); 
     1342    fmt[cnt].fmt.id = PJMEDIA_FORMAT_AMR; 
     1343    fmt[cnt].fmt.bitrate = 7400; 
     1344    fmt[cnt].fmt.vad = PJ_TRUE; 
     1345    cnt++; 
     1346     
     1347    // G.729 
     1348    fmt[cnt].fourcc = TFourCC('G', '7', '2', '9'); 
     1349    fmt[cnt].fmt.id = PJMEDIA_FORMAT_G729; 
     1350    fmt[cnt].fmt.bitrate = 8000; 
     1351    fmt[cnt].fmt.vad = PJ_TRUE; 
     1352    cnt++; 
     1353 
     1354    // iLBC 
     1355    fmt[cnt].fourcc = TFourCC('i', 'L', 'B', 'C'); 
     1356    fmt[cnt].fmt.id = PJMEDIA_FORMAT_ILBC; 
     1357    fmt[cnt].fmt.bitrate = 13333; 
     1358    fmt[cnt].fmt.vad = PJ_TRUE; 
     1359    cnt++; 
     1360     
     1361    // set stream priority to normal and time sensitive 
     1362    iInputStream_->SetPriority(EPriorityNormal,  
     1363                               EMdaPriorityPreferenceTime); 
     1364     
     1365    // Test each formats 
     1366    for (i=0; i<cnt; ++i) { 
     1367        TRAPD(fmterr, iInputStream_->SetDataTypeL(fmt[i].fourcc)); 
     1368        if (!fmterr && *pcount_ < max_fmt_) { 
     1369            fmt_[*pcount_] = fmt[i].fmt; 
     1370            (*pcount_)++; 
     1371        } 
     1372        PJ_LOG(4,(THIS_FILE, "Format %s is %s", 
     1373                  fmt[i].name, (fmterr? "not supported" : "supported"))); 
     1374    } 
     1375     
     1376    done_ = true; 
     1377} 
     1378 
     1379void CPjSndUtility::MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer) 
     1380{ 
     1381    // Should not happen 
     1382    pj_assert(!"Should not be called"); 
     1383} 
     1384 
     1385void CPjSndUtility::MaiscRecordComplete(TInt aError) 
     1386{ 
     1387        stopped_ = true; 
     1388} 
     1389 
     1390/**************************************************************************** 
    7791391 * Factory operations 
    7801392 */ 
    7811393 
     
    8071419static pj_status_t factory_init(pjmedia_aud_dev_factory *f) 
    8081420{ 
    8091421    struct mda_factory *af = (struct mda_factory*)f; 
    810  
     1422    unsigned i; 
     1423    pj_status_t status; 
     1424     
     1425    PJ_LOG(4, (THIS_FILE, "Initializing Symbian MDA sound subsystem..")); 
     1426     
    8111427    pj_ansi_strcpy(af->dev_info.name, "Symbian Audio"); 
    8121428    af->dev_info.default_samples_per_sec = 8000; 
    8131429    af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING | 
     
    8151431    af->dev_info.input_count = 1; 
    8161432    af->dev_info.output_count = 1; 
    8171433 
    818     PJ_LOG(4, (THIS_FILE, "Symb Mda initialized")); 
    819  
     1434    // Enum supported formats 
     1435    af->sndUtility = new CPjSndUtility; 
     1436    af->dev_info.ext_fmt_cnt = PJ_ARRAY_SIZE(af->dev_info.ext_fmt); 
     1437    status = af->sndUtility->EnumFormats(af->dev_info.ext_fmt,  
     1438                                         &af->dev_info.ext_fmt_cnt); 
     1439    if (status != PJ_SUCCESS) { 
     1440        af->dev_info.ext_fmt_cnt = 0; 
     1441    } else { 
     1442        af->dev_info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT; 
     1443    } 
     1444     
     1445    if (!af->sndUtility->Busy()) { 
     1446        delete af->sndUtility; 
     1447        af->sndUtility = NULL; 
     1448    } 
     1449     
     1450    // If G.711 is supported, that should indicate that EC and VAD is being  
     1451    // used. It cannot be turned off by this API though. 
     1452    for (i=0; i<af->dev_info.ext_fmt_cnt; ++i) { 
     1453        if (af->dev_info.ext_fmt[i].id == PJMEDIA_FORMAT_PCMA) 
     1454            break; 
     1455    } 
     1456    if (i != af->dev_info.ext_fmt_cnt) { 
     1457        af->dev_info.caps |= (PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_VAD); 
     1458    } 
     1459     
     1460    PJ_LOG(4, (THIS_FILE, "Symbian MDA sound initialized")); 
    8201461    return PJ_SUCCESS; 
    8211462} 
    8221463 
     
    8261467    struct mda_factory *af = (struct mda_factory*)f; 
    8271468    pj_pool_t *pool = af->pool; 
    8281469 
     1470    delete af->sndUtility; 
     1471    af->sndUtility = NULL; 
     1472         
    8291473    af->pool = NULL; 
    8301474    pj_pool_release(pool); 
    8311475 
     
    8741518    param->bits_per_sample = BITS_PER_SAMPLE; 
    8751519    param->flags = af->dev_info.caps; 
    8761520 
     1521    /* Clear volume settings */ 
     1522    param->flags &= ~(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |  
     1523                      PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING); 
     1524     
    8771525    return PJ_SUCCESS; 
    8781526} 
    8791527 
     
    9001548    PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL); 
    9011549 
    9021550    /* Create and Initialize stream descriptor */ 
    903     pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL); 
     1551    pool = pj_pool_create(mf->pf, "symb_aud_dev", 512, 512, NULL); 
    9041552    PJ_ASSERT_RETURN(pool, PJ_ENOMEM); 
    9051553 
    9061554    strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream); 
     
    9341582    strm->base.op = &stream_op; 
    9351583    *p_aud_strm = &strm->base; 
    9361584 
     1585    /* Apply output volume setting if specified */ 
     1586    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { 
     1587        stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,  
     1588                       &param->output_vol); 
     1589    } 
     1590 
     1591    /* Apply input volume setting if specified */ 
     1592    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) { 
     1593        stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,  
     1594                       &param->input_vol); 
     1595    } 
     1596     
    9371597    return PJ_SUCCESS; 
    9381598} 
    9391599 
     
    9471607 
    9481608    pj_memcpy(pi, &strm->param, sizeof(*pi)); 
    9491609     
     1610    /* Update the output volume setting */ 
     1611    if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 
     1612                       &pi->output_vol) == PJ_SUCCESS) 
     1613    { 
     1614        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; 
     1615    } else { 
     1616        pi->flags &= (~PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING); 
     1617    } 
     1618 
     1619    /* Update the input volume setting */ 
     1620    if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, 
     1621                       &pi->input_vol) == PJ_SUCCESS) 
     1622    { 
     1623        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING; 
     1624    } else { 
     1625        pi->flags &= (~PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING); 
     1626    } 
     1627     
    9501628    return PJ_SUCCESS; 
    9511629} 
    9521630