/*
 * Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description:  VOIP Audio Services  Downlink stream APIs.
 *
 */

#include <e32base.h>
#include <mmffourcc.h>
#include <voipdatabuffer.h>
#include "debugtracemacros.h"
#include "VoIPAudioSession.h"
#include "VoIPAudioClientServer.h"
#include "VoIPFormatIntfcImpl.h"
#include "VoIPBaseCodecIntfcImpl.h"
#include "VoIPILBCDecoderIntfcImpl.h"
#include "VoIPG711DecoderIntfcImpl.h"
#include "VoIPG729DecoderIntfcImpl.h"
#include "VoIPJitterBufferIntfcImpl.h"
#include "VoIPSharedData.h"
#include "VoIPDownlinkStreamImpl.h"

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::NewL
// ---------------------------------------------------------------------------
//
CVoIPAudioDownlinkStreamImpl*
CVoIPAudioDownlinkStreamImpl::NewL(const TMMFPrioritySettings aPriority)
    {
    CVoIPAudioDownlinkStreamImpl* self =
            new (ELeave) CVoIPAudioDownlinkStreamImpl();
    CleanupStack::PushL(self);
    self->ConstructL(aPriority);
    CleanupStack::Pop(self);
    return self;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::~CVoIPAudioDownlinkStreamImpl
// ---------------------------------------------------------------------------
//
CVoIPAudioDownlinkStreamImpl::~CVoIPAudioDownlinkStreamImpl()
    {
    Close();

    if (iMsgQHandler)
        {
        iMsgQHandler->Cancel();
        delete iMsgQHandler;
        }

    if (iMsgQComHandler)
        {
        iMsgQComHandler->Cancel();
        delete iMsgQComHandler;
        }

    if (iMsgQueue.Handle() > 0)
        {
        iMsgQueue.Close();
        }

    if (iMsgComQueue.Handle() > 0)
        {
        iMsgComQueue.Close();
        }

    if (iVoIPAudioSession.Handle() > 0)
        {
        iVoIPAudioSession.Close();
        }
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::CVoIPAudioDownlinkStreamImpl
// ---------------------------------------------------------------------------
//
CVoIPAudioDownlinkStreamImpl::CVoIPAudioDownlinkStreamImpl() :
    iBufferPtr(0, 0, 0)
    {
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::ConstructL
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::ConstructL(
        const TMMFPrioritySettings aPriority)
    {
    // Pass ownership to the parent class
    CVoIPAudioDownlinkStream::ConstructL(this);
    iPriority = aPriority;
    iG711FrameSize = KVoIPG711FrameLen20ms;
    iFormat = EG711;
    TInt err = iVoIPAudioSession.Connect();
    User::LeaveIfError(err);
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::Open
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::Open(MVoIPDownlinkObserver& aObserver)
    {
    iObserver = &aObserver;

    TInt err = CreateQueue(KDnLinkQueue, KVoIPMsgQSlots, EMsgBufQueue);

    if (err == KErrNone)
        {
        err = CreateQueue(KDnLinkComQueue, KVoIPMsgComQSlots, EMsgComQueue);
        }

    if (err == KErrNone)
        {
        err = iVoIPAudioSession.OpenDownlink(iPriority);
        }

    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetVersion
// Returns version of this component.
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::GetVersion(TVersion& aVersion)
    {
    aVersion = TVersion(1, 0, 0);
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetMaxVolume
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::GetMaxVolume(TInt& aVolume)
    {
    aVolume = iVoIPAudioSession.GetMaxVolume();
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::SetVolume
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::SetVolume(TInt aVolume)
    {
    TInt err = iVoIPAudioSession.SetVolume(aVolume);
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetVolume
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::GetVolume(TInt& aVolume)
    {
    aVolume = iVoIPAudioSession.GetVolume();
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetSupportedFormatsL
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::GetSupportedFormatsL(
        const TMMFPrioritySettings aPriority,
        RArray<TVoIPCodecFormat>& aFormats)
    {
    if (!iCodecFormats || aFormats.Count() <= 0)
        {
        RArray<TUint32> codecIDs;
        CleanupClosePushL(codecIDs);
        codecIDs.Reset();
        iVoIPAudioSession.GetSupportedDecoders(aPriority, codecIDs,
                iG711FrameSize);

        TUint32 codec = 0;
        TInt count = codecIDs.Count();
        TVoIPCodecFormat format;
        aFormats.Reset();

        for (TInt i = 0; i < count; i++)
            {
            codec = codecIDs[i];

#ifdef _DEBUG
            TInt8 a = codec;
            TInt8 b = codec >> 8;
            TInt8 c = codec >> 16;
            TInt8 d = codec >> 24;
            RDebug::Print(_L("%c%c%c%c"), a, b, c, d);
#endif
            format = ConvertFourCC(codec);
            if (format != ENULL)
                {
                aFormats.Append(format);
                }
            }

        iCodecFormats = &aFormats;
        CleanupStack::PopAndDestroy(&codecIDs);
        }
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::SetFormatL
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::SetFormatL(TVoIPCodecFormat aFormat,
        CVoIPFormatIntfc*& aIntfc)
    {
    // Check if requested codec is on the list of supported codecs
    if (!IsCodecSupportedL(aFormat))
        {
        iFormat = ENULL;
        }

    // TODO: reuse when switching codecs on the fly and same codec is selected
    delete aIntfc;
    aIntfc = NULL;

    switch (iFormat)
        {
        case EG711:
        case EG711_10MS:
            {
            aIntfc = CVoIPG711DecoderIntfcImpl::NewL(this);
            iBufferLen = iG711FrameSize;
            break;
            }
        case EG729:
            {
            aIntfc = CVoIPG729DecoderIntfcImpl::NewL(this);
            iBufferLen = KVoIPG729FrameLen;
            break;
            }
        case EILBC:
            {
            aIntfc = CVoIPILBCDecoderIntfcImpl::NewL(this);
            iBufferLen = KVoIPILBCFrameLen;
            break;
            }
        case EAMR_NB:
            {
            aIntfc = CVoIPBaseCodecIntfcImpl::NewL(this);
            iBufferLen = KVoIPAMRNBFrameLen;
            break;
            }
        case EPCM16:
            {
            aIntfc = CVoIPBaseCodecIntfcImpl::NewL(this);
            iBufferLen = KVoIPPCM16FrameLen;
            break;
            }
        default:
            {
            iFormat = ENULL;
            iBufferLen = 0;
            User::Leave(KErrNotSupported);
            }
        }

    TUint32 codecFourCC = CodecFourCC(iFormat);
    iVoIPAudioSession.SetDecoder(codecFourCC);
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetFormat
// ---------------------------------------------------------------------------
//
TVoIPCodecFormat CVoIPAudioDownlinkStreamImpl::GetFormat()
    {
    return iFormat;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::SetAudioDevice
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::SetAudioDevice(
        const TVoIPOutputDevice aDevice)
    {
    TInt err = iVoIPAudioSession.SetAudioDevice(aDevice);
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetAudioDevice
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::GetAudioDevice(TVoIPOutputDevice& aDevice)
    {
    TInt err = iVoIPAudioSession.GetAudioDevice(aDevice);
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::Start
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::Start()
    {
    TInt err = iVoIPAudioSession.StartDownlink();
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::Stop
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::Stop()
    {
    TInt err = iVoIPAudioSession.StopDownlink();
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::Close
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::Close()
    {
    iVoIPAudioSession.CloseDownlink();
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::BufferFilled
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::BufferFilled(CVoIPDataBuffer* aBuffer)
    {
    TUint bufSeq = 0;
    aBuffer->GetPayloadPtr(iBufferPtr);
    CVoIPDataBuffer::TVoIPBufferType type;
    aBuffer->GetBufferType(type);

    if (type == CVoIPDataBuffer::EJitterBuffer)
        {
        static_cast<CVoIPJBDataBuffer*> (aBuffer)->GetBufferSequence(bufSeq);
        }

    TInt err = iVoIPAudioSession.BufferFilled(iBufferPtr, bufSeq);
    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::CreateJitterBufferIntfcL
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::CreateJitterBufferIntfcL(
        CVoIPJitterBufferIntfc*& aJBIntfc)
    {
    aJBIntfc = CVoIPJitterBufferIntfcImpl::NewL(&iVoIPAudioSession);
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::GetSession
// ---------------------------------------------------------------------------
//
RVoIPAudioSession* CVoIPAudioDownlinkStreamImpl::GetSession()
    {
    return &iVoIPAudioSession;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioStream::CreateQueue
// ---------------------------------------------------------------------------
//
TInt CVoIPAudioDownlinkStreamImpl::CreateQueue(const TDesC& aQueueName,
        const TInt aNumSlots, const TQueueType aQueueType)
    {
    TInt err = KErrNone;

    if (aQueueType == EMsgBufQueue)
        {
        if (iMsgQueue.Handle() <= 0)
            {
            err = iMsgQueue.CreateGlobal(aQueueName, aNumSlots);
            if (err != KErrNone)
                {
                return err;
                }
            }

        TRAP(err, ReceiveMsgQHandlerEventsL());
        }
    else if (aQueueType == EMsgComQueue)
        {
        if (iMsgComQueue.Handle() <= 0)
            {
            err = iMsgComQueue.CreateGlobal(aQueueName, aNumSlots);
            if (err != KErrNone)
                {
                return err;
                }
            }

        TRAP(err, ReceiveMsgQComHandlerEventsL());
        }

    return err;
    }

// ---------------------------------------------------------------------------
// CVoIPAudioStream::ReceiveMsgQHandlerEventsL
// Starts message queue handler (A/O) to monitor server side events
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::ReceiveMsgQHandlerEventsL()
    {
    if (iMsgQHandler)
        {
        iMsgQHandler->Cancel();
        }
    else
        {
        iMsgQHandler = CQueueHandler::NewL(this, &iMsgQueue, iBufferLen);
        }

    iMsgQHandler->Start();
    }

// ---------------------------------------------------------------------------
// CVoIPAudioStream::ReceiveMsgQComHandlerEventsL
// Starts message queue handler (A/O) to monitor server side events
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::ReceiveMsgQComHandlerEventsL()
    {
    if (iMsgQComHandler)
        {
        iMsgQComHandler->Cancel();
        }
    else
        {
        iMsgQComHandler = CQueueHandler::NewL(this, &iMsgComQueue);
        }

    iMsgQComHandler->Start();
    }

// ---------------------------------------------------------------------------
// CVoIPAudioStream::CodecFourCC
// ---------------------------------------------------------------------------
//
TUint32 CVoIPAudioDownlinkStreamImpl::CodecFourCC(TVoIPCodecFormat aFormat)
    {
    TUint32 codecFourCC;

    switch (aFormat)
        {
        case EG711_10MS:
        case EG711:
            {
            codecFourCC = KMccFourCCIdG711;
            break;
            }
        case EG729:
            {
            codecFourCC = KMccFourCCIdG729;
            break;
            }
        case EILBC:
            {
            codecFourCC = KMccFourCCIdILBC;
            break;
            }
        case EAMR_NB:
            {
            codecFourCC = KMccFourCCIdAMRNB;
            break;
            }
        case EPCM16:
            {
            codecFourCC = KMMFFourCCCodePCM16;
            break;
            }
        default:
            {
            codecFourCC = KFourCCNULL;
            }
        }

    return codecFourCC;
    }

// -----------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::ConvertFourCC
// -----------------------------------------------------------------------------
//
TVoIPCodecFormat CVoIPAudioDownlinkStreamImpl::ConvertFourCC(TUint32 aCodec)
    {
    TVoIPCodecFormat format;

    switch (aCodec)
        {
        case KMccFourCCIdG711:
            {
            if (iG711FrameSize == KVoIPG711FrameLen10ms)
                {
                format = EG711_10MS;
                TRACE_PRN_N(_L("VoIP->DNL: G711 10ms frame rate detected"));
                }
            else
                {
                format = EG711;
                }
            break;
            }
        case KMccFourCCIdG729:
            format = EG729;
            break;
        case KMccFourCCIdILBC:
            format = EILBC;
            break;
        case KMccFourCCIdAMRNB:
            format = EAMR_NB;
            break;
        case KMMFFourCCCodePCM16:
            format = EPCM16;
            break;
        default:
            format = ENULL;
            break;
        }

    return format;
    }

// -----------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::IsCodecSupportedL
// -----------------------------------------------------------------------------
//
TBool CVoIPAudioDownlinkStreamImpl::IsCodecSupportedL(
        TVoIPCodecFormat aFormat)
    {
    TBool status = EFalse;

    if (!iCodecFormats)
        {
        // Client hasn't called GetSupportedFormatsL
        RArray<TVoIPCodecFormat> codecs;
        CleanupClosePushL(codecs);
        codecs.Reset();
        GetSupportedFormatsL(iPriority, codecs); //sets iCodecFormats
        status = FindFormat(aFormat);
        CleanupStack::PopAndDestroy(&codecs);
        iCodecFormats = NULL;
        }
    else
        {
        status = FindFormat(aFormat);
        }

    return status;
    }

// -----------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::FindFormat
// -----------------------------------------------------------------------------
//
TBool CVoIPAudioDownlinkStreamImpl::FindFormat(TVoIPCodecFormat aFormat)
    {
    TBool found = EFalse;

    if (iCodecFormats)
        {
        if (iCodecFormats->Count() > 0)
            {
            if (iCodecFormats->Find(aFormat) == KErrNotFound)
                {
                // For backward compatibility with VAS 1.0
                if (aFormat == EG711)
                    {
                    if (iCodecFormats->Find(EG711_10MS) != KErrNotFound)
                        {
                        iFormat = EG711_10MS;
                        found = ETrue;
                        }
                    }
                }
            else
                {
                iFormat = aFormat;
                found = ETrue;
                }
            }
        }

    return found;
    }

// ======== CALLBACK FUNCTIONS ========

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::FillBuffer
// From MQueueHandlerObserver
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::FillBuffer(CVoIPDataBuffer* aBuffer)
    {
    iObserver->FillBuffer(*this, aBuffer);
    }

// ---------------------------------------------------------------------------
// CVoIPAudioDownlinkStreamImpl::Event
// From MQueueHandlerObserver
// ---------------------------------------------------------------------------
//
void CVoIPAudioDownlinkStreamImpl::Event(TInt aEventType, TInt aError)
    {
    iObserver->Event(*this, aEventType, aError);
    }

// End of file
