mmserv/voipaudioservices/JitterBufferUtility/src/JitterBuffer.cpp
changeset 0 71ca22bcf22a
child 3 4f62049db6ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/voipaudioservices/JitterBufferUtility/src/JitterBuffer.cpp	Tue Feb 02 01:08:46 2010 +0200
@@ -0,0 +1,423 @@
+/*
+* 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)
+        {
+        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