/*
* Copyright (c) 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:  JitterBuffer component capable to audioframe buffering.
*
*/


// INCLUDE FILES
#include <e32base.h>
#include <mmcccodecinformation.h> //FourCC codes
#include "debugtracemacros.h"
#include "InternalDef.h"
#include "JitterBufferObserver.h"
#include "JitterBufferImpl.h"
#include "JitterBuffer.h"


// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::CVoIPJitterBuffer
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CVoIPJitterBuffer::CVoIPJitterBuffer(MJitterBufferObserver* aObserver) :
    CActive(EPriorityNormal)
    {
    iObserver = aObserver;
    iState = EJBufStopped;
    iTmCurrentEmptyBuffer = TTime(0);
    iTmPreviousEmptyBuffer = TTime(0);
    }

// -----------------------------------------------------------------------------
// CVoIPDownlinkThread::NewL
// Static constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CVoIPJitterBuffer* CVoIPJitterBuffer::NewL(
                                               MJitterBufferObserver* aObserver)
    {
    CVoIPJitterBuffer* self = new (ELeave) CVoIPJitterBuffer(aObserver);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::ConstructL
// Symbian two-phase constructor
// -----------------------------------------------------------------------------
//
void CVoIPJitterBuffer::ConstructL()
    {
    CActiveScheduler::Add(this);
    iTimer.CreateLocal();
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::~CVoIPJitterBuffer
// Destructor deallocate memory.
// -----------------------------------------------------------------------------
//
CVoIPJitterBuffer::~CVoIPJitterBuffer()
    {
    TRACE_PRN_FN_ENT;

    if (IsActive())
        {
        Cancel();
        }

    delete iJitterBufferImpl;
    iTimer.Close();

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::SetupL
// Setup Jitterbuffer
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::SetupL(const TFourCC aCodec,
                                        const TVoIPJBConfig& aJBConfig)
    {
    TRACE_PRN_FN_ENT;

    iCodec = aCodec;

    if (!iJitterBufferImpl)
        {
        iJitterBufferImpl = CVoIPJitterBufferImpl::NewL(iObserver);
        }

    if (iJitterBufferImpl)
        {
        iJitterBufferImpl->SetupL(aCodec, aJBConfig);
        iSampleInterval =
                aJBConfig.iSampleInterval * KJBMillisecondsToMicroseconds;

        // Cannot use with G711 as it asks buffers in uncontrolled manner
        // TODO: change the logic so that it inspects what happens in long
        // run and if it fails, send error and pause itself.
        if (iCodec != KMccFourCCIdG711)
            {
            // Not too strict with delaying, threshold cannot be bigger than
            // frame time
            iEmptyBufferDelayThreshold = iSampleInterval / KJBEmptyBufferDelay;
            }

        TRACE_PRN_N1(_L("JB-> DelayThreshold [%d]"),iEmptyBufferDelayThreshold);
        }

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::ResetBuffer
// Reset Jitterbuffer
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::ResetBuffer(TBool aPlayTone)
    {
    TRACE_PRN_FN_ENT;

    if (iJitterBufferImpl)
        {
        iJitterBufferImpl->ResetBuffer(aPlayTone);
        }

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::DelayDownL
// Delay Down
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::DelayDown()
    {
    if (iJitterBufferImpl)
        {
        TRAPD(err, iJitterBufferImpl->DelayDownL());
        if (err != KErrNone)
            {
            TRACE_PRN_IF_ERR(err);
            iObserver->EventJB(MJitterBufferObserver::EGeneralError, err);
            }
        }
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::DelayUpL
// Delay Up
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::DelayUp()
    {
    if (iJitterBufferImpl)
        {
        TRAPD(err, iJitterBufferImpl->DelayUpL());
        if (err != KErrNone)
            {
            TRACE_PRN_IF_ERR(err);
            iObserver->EventJB(MJitterBufferObserver::EGeneralError, err);
            }
        }
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::Play
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::Play()
    {
    TRACE_PRN_FN_ENT;

    iState = EJBufPlaying;
    iRequestSize = 0;

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::Pause
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::Pause()
    {
    iState = EJBufPaused;
    ResetBuffer();
    Cancel();
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::Stop
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CVoIPJitterBuffer::Stop()
    {
    iState = EJBufStopped;
    ResetBuffer();
    Cancel();
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::FillBufferL
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CVoIPJitterBuffer::FillBuffer(CMMFBuffer* aBuffer)
    {
//    TRACE_PRN_FN_ENT;

    TInt err = KErrNone;

    if (iJitterBufferImpl && iJitterBufferImpl->BufferLength())
        {
        TRACE_PRN_N(_L("CVoIPJitterBuffer::FillBufferL NORMAL"));

        TUint32 emptyBufferDelay = 0;

        if (iCodec != KMccFourCCIdG729)
            {
            // This can cause JB overflow on G729 (it does in the loopback)
            DetermineEmptyBufferDelay();
            }

        iPlayBuffer = aBuffer;
        iJitterBufferImpl->GetDataFrame(iPlayBuffer);
        TransitionState(EEmptyData, emptyBufferDelay);
        }
    else
        {
        err = KErrNotReady;
        TRACE_PRN_IF_ERR(err);
        iObserver->EventJB(MJitterBufferObserver::EGeneralError, err);
        }

//    TRACE_PRN_FN_EXT;
    return err;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::EmptyBuffer
// Buffer with data comes in and is passed to JB queue.
//
// It was callback based; now DNL-THR->BufferFilled will call it directly
// upon data receiving. The supplier is now the DNL thread.
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CVoIPJitterBuffer::EmptyBuffer(CMMFBuffer* aBuffer)
    {
//    TRACE_PRN_FN_ENT;
    TInt err = KErrNotReady;

    if (iJitterBufferImpl && (iState == EJBufPlaying))
        {
        if (iJitterBufferImpl->BufferLength() == 0)
            {
            return err;
            }

//        TRACE_PRN_N1(_L("JB-> BUF Size: [%d]"), aBuffer->BufferSize());
//        TRACE_PRN_N1(_L("JB-> REQ Size: [%d]"), aBuffer->RequestSize());

        // Adaptation control will be done based on played frames
        err = iJitterBufferImpl->AddDataFrame(aBuffer);
        }

    iObserver->EventJB(MJitterBufferObserver::EBufferConsumed);

    TRACE_PRN_IF_ERR(err);
//    TRACE_PRN_FN_EXT;
    return err;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::PlayBuffer
//
// -----------------------------------------------------------------------------
//
void CVoIPJitterBuffer::PlayBuffer()
    {
//    TRACE_PRN_FN_ENT;

    // iDataSink has valid data - send for playback
    iObserver->EventJB(MJitterBufferObserver::EBufferReadyToPlay);

    // Go to wait state
//    TransitionState(EWait);

//    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::DoCancel
// From CActive
// -----------------------------------------------------------------------------
//
void CVoIPJitterBuffer::DoCancel()
    {
    iTimer.Cancel();
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::RunL
// From CActive
// -----------------------------------------------------------------------------
//
void CVoIPJitterBuffer::RunL()
    {
    TRACE_PRN_FN_ENT;

    switch (iState)
        {
        case EJBufStopped:
            break;
        case EJBufPaused:
            break;
        case EJBufPlaying:
            {
            switch (iTransitionState)
                {
                case EWait:
                    TRACE_PRN_N(_L("CVoIPJitterBuffer::RunL EWait"));
                    break;
                case EEmptyData:
                    TRACE_PRN_N(_L("CVoIPJitterBuffer::RunL EEmptyData"));
                    PlayBuffer();
                    break;
                default:
                    User::Leave(KErrArgument);
                    break;
                }
            break;
            }
        default:
            User::Leave(KErrArgument);
            break;
        }

    TRACE_PRN_FN_EXT;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::RunError
//
// -----------------------------------------------------------------------------
//
TInt CVoIPJitterBuffer::RunError(TInt aError)
    {
    iObserver->EventJB(MJitterBufferObserver::EGeneralError, aError);
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::TransitionState
//
// -----------------------------------------------------------------------------
//
void CVoIPJitterBuffer::TransitionState(TJBTransitionState aTransitionState,
                                        TUint32 aStateChangeDelay)
    {
    TRequestStatus* stat = &iStatus;

    iTransitionState = aTransitionState;
    Cancel();

    if (aStateChangeDelay)
        {
        iStatus = KRequestPending;
        iTimer.After(iStatus, TTimeIntervalMicroSeconds32(aStateChangeDelay));
        }
    else
        {
        User::RequestComplete(stat, KErrNone);
        }

    SetActive();
    }

// -----------------------------------------------------------------------------
// CVoIPJitterBuffer::DetermineEmptyBufferDelay
// -----------------------------------------------------------------------------
//
TUint32 CVoIPJitterBuffer::DetermineEmptyBufferDelay()
    {
    TUint32 emptyBufferDelay(0);
    iTmCurrentEmptyBuffer.HomeTime();

    if (iTmPreviousEmptyBuffer.Int64())
        {
        TTimeIntervalMicroSeconds difference =
            iTmCurrentEmptyBuffer.MicroSecondsFrom(iTmPreviousEmptyBuffer);

        if (difference.Int64() < iEmptyBufferDelayThreshold)
            {
            emptyBufferDelay = iSampleInterval - difference.Int64();
            }
        }

    iTmPreviousEmptyBuffer = iTmCurrentEmptyBuffer;
    return emptyBufferDelay;
    }


//  End of File
