/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

#include "pvmf_media_clock.h"

OSCL_EXPORT_REF PVMFMediaClock::PVMFMediaClock() : OsclTimerObject(OsclActiveObject::EPriorityNominal, "PVMFMediaClock"),
        iState(STOPPED),
        iClockTimebase(NULL)
{
    iLogger = PVLogger::GetLoggerObject("PVMFMediaClock");
    iLatestRunningClockTime = 0;
    iLatestRunningTimebaseTime = 0;
    iStartTimebaseTickValue = 0;
    iStartClockTime = 0;
    iPauseClockTime = 0;
    iLastAdjustObsTimebaseTime = 0;
    iAdjustmentTimebaseTime = 0;
    iStartNPT = 0;
    iIsNPTPlayBackDirectionBackwards = 0;
    iStartMediaClockTS = 0;
    iClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC;
    iPreviousClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC;
    iActiveTimersCount = 0;
    iTimerIDCount = 1;
    OsclThread::GetId(iOrigThreadID);
    AddToScheduler();
    iMutex = OSCL_NEW(OsclMutex, ());
    iMutex->Create();

    //initialize this to real time
    iLastTimebaseRate = REALTIME_PLAYBACK_RATE;

    iIsTimebaseCountBased = false;
    iHighestLatency = 0;
}

OSCL_EXPORT_REF PVMFMediaClock::~PVMFMediaClock()
{
    Reset();
    iMutex->Close();
    if (iMutex)
        OSCL_DELETE(iMutex);
    RemoveFromScheduler();
}

void PVMFMediaClock::CleanCallbackInfImplObjects()
{
    for (uint32 ii = 0; ii < iMediaClockSetCallbackObjects.size(); ii++)
    {
        if (iMediaClockSetCallbackObjects[ii]->iNotificationInterfaceDestroyedCallback)
        {
            (iMediaClockSetCallbackObjects[ii]->iNotificationInterfaceDestroyedCallback)->NotificationsInterfaceDestroyed();
        }
        OSCL_DELETE(iMediaClockSetCallbackObjects[ii]);
    }
    iMediaClockSetCallbackObjects.clear();
    iHighestLatency = 0;
}

OSCL_EXPORT_REF PVMFStatus
PVMFMediaClock::ConstructMediaClockNotificationsInterface(PVMFMediaClockNotificationsInterface*& aIface,
        PVMFMediaClockNotificationsObsBase& aNotificationInterfaceDestroyedCallback,
        uint32 aLatency)
{

    PVMFMediaClockNotificationsInterfaceImpl* ifaceImpl = NULL;

    if (RUNNING == iState)
    {
        return PVMFErrInvalidState;
    }

    ifaceImpl = OSCL_NEW(PVMFMediaClockNotificationsInterfaceImpl, (this, aLatency, aNotificationInterfaceDestroyedCallback));
    aIface = OSCL_STATIC_CAST(PVMFMediaClockNotificationsInterface*, ifaceImpl);

    if (aIface)
    {
        UpdateHighestLatency(aLatency);
        iMediaClockSetCallbackObjects.push_back(ifaceImpl);
        return PVMFSuccess;
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF void PVMFMediaClock::DestroyMediaClockNotificationsInterface(PVMFMediaClockNotificationsInterface* aInf)
{
    if (!aInf)
    {
        return;
    }

    //Cancel all active callbacks created from this interface object.
    if (!iTimersPriQueue.empty())
    {
        Oscl_Vector<PVMFMediaClockTimerQueueElement, OsclMemAllocator> qVector = iTimersPriQueue.vec();
        for (uint32 ii = 0; ii < qVector.size(); ii++)
        {
            if (qVector[ii].pInterfaceObject == aInf)
            {
                CommonCancelCallback(qVector[ii].callBackID, false, false);
            }
        }
    }

    if (!iIsNPTPlayBackDirectionBackwards)
    {
        if (!iTimersPriQueueNPT.empty())
        {
            Oscl_Vector<PVMFMediaClockTimerQueueElement, OsclMemAllocator> qVector = iTimersPriQueueNPT.vec();
            for (uint32 ii = 0; ii < qVector.size(); ii++)
            {
                if (qVector[ii].pInterfaceObject == aInf)
                {
                    CommonCancelCallback(qVector[ii].callBackID, false, true);
                }
            }
        }
    }
    else
    {
        if (!iTimersPriQueueNPTBackwards.empty())
        {
            Oscl_Vector<PVMFMediaClockTimerQueueElement, OsclMemAllocator> qVector = iTimersPriQueueNPTBackwards.vec();
            for (uint32 ii = 0; ii < qVector.size(); ii++)
            {
                if (qVector[ii].pInterfaceObject == aInf)
                {
                    CommonCancelCallback(qVector[ii].callBackID, false, true);
                }
            }

        }
    }



    //Destroy the interface
    PVMFMediaClockNotificationsInterfaceImpl* ifaceImpl = NULL;

    ifaceImpl = OSCL_STATIC_CAST(PVMFMediaClockNotificationsInterfaceImpl*, aInf);
    for (uint32 ii = 0; ii < iMediaClockSetCallbackObjects.size(); ii++)
    {
        if (iMediaClockSetCallbackObjects[ii] == ifaceImpl)
        {
            if (iMediaClockSetCallbackObjects[ii]->iNotificationInterfaceDestroyedCallback)
            {
                (iMediaClockSetCallbackObjects[ii]->iNotificationInterfaceDestroyedCallback)->NotificationsInterfaceDestroyed();
            }
            OSCL_DELETE(iMediaClockSetCallbackObjects[ii]);
            iMediaClockSetCallbackObjects.erase(&iMediaClockSetCallbackObjects[ii]);
        }
    }

    iHighestLatency = 0;
}

OSCL_EXPORT_REF bool PVMFMediaClock::SetClockTimebase(PVMFTimebase& aTimebase)
{
    // Clock timebase can only be set during stopped or paused states
    if (iState == RUNNING)
    {
        return false;
    }

    // Save the clock timebase object pointer
    iClockTimebase = &aTimebase;

    if ((iClockTimebase->GetRate() != iLastTimebaseRate) &&
            iActiveTimersCount != 0)
    {
        //start scheduling afresh
        AdjustScheduling();
    }

    //Update timebase rate
    iLastTimebaseRate = iClockTimebase->GetRate();

    //If this is a counting timebase, then set the timebase
    //observer to this clock, so that we'll get the count update
    //notices.
    if (aTimebase.GetCountTimebase())
    {
        aTimebase.GetCountTimebase()->SetClockObserver(this);
        iIsTimebaseCountBased = true;
    }
    else
    {
        iIsTimebaseCountBased = false;
    }

    //Notify observers that the timebase is updated.
    ClockTimebaseUpdated();

    return true;
}

void PVMFMediaClock::UpdateHighestLatency(uint32 alatency)
{
    //Negative clock adjustment should be equal to the biggest latency
    // among all the sinks.
    if (iHighestLatency < alatency)
    {
        iHighestLatency = alatency;
    }
}

OSCL_EXPORT_REF bool PVMFMediaClock::Start()
{
    bool overflowFlag = false;
    // Can only start from stopped or paused states
    if (iState == RUNNING)
    {
        return false;
    }


    uint32 tbval = 0;


    // Save the clock timebase value to the appropriate
    // variable and update the iLatest... values.
    if (iState == STOPPED)
    {
        // Push back clock by highest latency value so that all modules
        // with latencies can see a zero
        if (iHighestLatency != 0)
        {
            uint32 currentTime = 0;
            uint32 currentTimebaseTime = 0;
            uint32 adjustedTime = 0;
            bool gOverflowFlag = 0;

            //This is the default value of clock units
            PVMFMediaClock_TimeUnits timeUnits = PVMF_MEDIA_CLOCK_MSEC;

            if (iClockUnit == PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC)
            {
                timeUnits =  PVMF_MEDIA_CLOCK_USEC;
            }

            GetCurrentTime32(currentTime, gOverflowFlag, timeUnits, currentTimebaseTime);
            adjustedTime = currentTime - iHighestLatency;

            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::Start - Pushing back Clock to %d",
                             adjustedTime));

            SetStartTime32(adjustedTime, timeUnits, gOverflowFlag);
        }

        // Retrieve the current time value from the clock timebase
        if (iClockTimebase != NULL)
        {
            iClockTimebase->GetCurrentTick32(iStartTimebaseTickValue, overflowFlag);
        }

        //can reuse overflowFlag as result would be virtually same
        GetScaledTimebaseTickCount(tbval, overflowFlag);
        // Starting from stop
        UpdateLatestTimes(iStartClockTime, tbval);
    }
    else
    {
        GetScaledTimebaseTickCount(tbval, overflowFlag);
        // Resuming from pause
        UpdateLatestTimes(iPauseClockTime, tbval);
    }

    // Change to running state
    SetClockState(RUNNING);

    //Restart callback scheduling
    AdjustScheduling();

    return true;
}

OSCL_EXPORT_REF bool PVMFMediaClock::Pause()
{
    bool overflowFlag = false;
    // Can only pause during running state
    if (iState != RUNNING)
    {
        return false;
    }

    // Save the current time
    uint32 tbval = 0;
    GetCurrentTime32(iPauseClockTime, overflowFlag, PVMF_MEDIA_CLOCK_MSEC, tbval);
    UpdateLatestTimes(iPauseClockTime, tbval);

    // Change to paused state
    SetClockState(PAUSED);

    //Cancel any scheduled Run
    Cancel();
    return true;
}

OSCL_EXPORT_REF bool PVMFMediaClock::Stop()
{
    // Can only stop when running or paused
    if (iState == STOPPED)
    {
        return false;
    }

    // Reset the time values
    uint32 tmp = 0;
    UpdateLatestTimes(tmp, tmp);
    iStartClockTime = tmp;
    iPauseClockTime = tmp;
    iLastAdjustObsTimebaseTime = tmp;
    iAdjustmentTimebaseTime = tmp;
    iStartTimebaseTickValue = tmp;

    // Change to stopped state
    SetClockState(STOPPED);

    //Clear all callback queues
    ClearAllQueues();
    return true;
}

OSCL_EXPORT_REF void PVMFMediaClock::GetStartTime32(uint32& aTime, bool& aOverflow, PVMFMediaClock_TimeUnits aUnits)
{
    aOverflow = false;
    // Convert to the requested time units and return the start time
    FromClockUnit(iStartClockTime, aTime, aUnits, aOverflow);
}

OSCL_EXPORT_REF bool PVMFMediaClock::SetStartTime32(uint32& aTime, PVMFMediaClock_TimeUnits aUnits, bool& aOverFlow)
{
    aOverFlow = false;

    // Can only set start time while stopped
    if (iState != STOPPED)
    {
        return false;
    }

    iPreviousClockUnit = iClockUnit;
    // set clock units to usec if units arg is usec. Otherwise, default is msec
    (PVMF_MEDIA_CLOCK_USEC == aUnits) ? iClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC :
            iClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC;

    if (iPreviousClockUnit != iClockUnit)
    {
        AdjustClockInternalsToNewUnits(aOverFlow);
    }

    // Convert to clock units and set the start time
    bool overflowFlag1 = false;
    ToClockUnit(aTime, aUnits, iStartClockTime, overflowFlag1);

    aOverFlow = aOverFlow | overflowFlag1;

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO, (0,
                    "PVMFMediaClock::SetStartTime32 - Clock Start time set to %d", iStartClockTime));
    //start scheduling afresh
    AdjustScheduling();
    return true;
}

OSCL_EXPORT_REF PVMFMediaClockAdjustTimeStatus PVMFMediaClock::AdjustClockTime32(uint32& aClockTime,
        uint32& aTimebaseTime,
        uint32& aAdjustedTime,
        PVMFMediaClock_TimeUnits aUnits,
        bool& aOverFlow)
{
    aOverFlow = false;

    // Clock can only be adjusted when it is running
    if (iState != RUNNING)
    {
        return PVMF_MEDIA_CLOCK_ADJUST_ERR_INVALID_STATE;
    }

    // Check if the adjustment's observed time is later than the last one
    uint32 temp = 0;
    if (PVTimeComparisonUtils::IsEarlier(aTimebaseTime, iLastAdjustObsTimebaseTime, temp) && (temp != 0))
    {
        // Old data so don't use it for adjustment
        return PVMF_MEDIA_CLOCK_ADJUST_ERR_INVALID_TIMEBASE_TIME;
    }

    iPreviousClockUnit = iClockUnit;

    // set clock units to usec if units arg is usec. Otherwise, default is msec
    (PVMF_MEDIA_CLOCK_USEC == aUnits) ? iClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC :
            iClockUnit = PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC;

    if (iPreviousClockUnit != iClockUnit)
    {
        AdjustClockInternalsToNewUnits(aOverFlow);
    }

    // Convert the observed clock and adjustment time to usec so it can be compared
    uint32 adjusttime, clocktime;
    bool overflowFlag1 = false, overflowFlag2 = false;
    ToClockUnit(aClockTime, aUnits, clocktime, overflowFlag1);
    ToClockUnit(aAdjustedTime, aUnits, adjusttime, overflowFlag2);

    // Make sure the adjustment's clock and timebase times are before current time
    uint32 currenttime = 0;
    uint32 tbval = 0;
    bool overflowFlag3 = false, overflowFlag4 = false;

    // Get the current timebase time
    GetScaledTimebaseTickCount(tbval, overflowFlag4);

    // Get the current clock time in clock units
    GetCurrentTime32(currenttime, overflowFlag3, iClockUnit == PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC ? PVMF_MEDIA_CLOCK_USEC :
                     PVMF_MEDIA_CLOCK_MSEC);

    aOverFlow = aOverFlow | overflowFlag1 | overflowFlag2 | overflowFlag3 | overflowFlag4;

    if (PVTimeComparisonUtils::IsEarlier(tbval, aTimebaseTime, temp) && (temp != 0))
    {
        // Observed timebase time cannot be later than the current timebase time
        return PVMF_MEDIA_CLOCK_ADJUST_ERR_INVALID_TIMEBASE_TIME;
    }

    if (clocktime > currenttime)
    {
        // Observed clock time cannot be later than the current clock time
        return PVMF_MEDIA_CLOCK_ADJUST_ERR_CORRUPT_CLOCK_TIME;
    }

    // Make the adjustment
    return AdjustClock(clocktime, aTimebaseTime, adjusttime, currenttime, tbval);
}

OSCL_EXPORT_REF bool PVMFMediaClock::Reset()
{
    bool tmpFlag = true;

    //stop clock if its not already stopped
    if (STOPPED != iState)
    {
        tmpFlag = Stop();
    }

    // remove all ClockObservers
    iClockObservers.clear();

    // remove interface objects. This will also remove all ClockStateObservers.
    CleanCallbackInfImplObjects();

    return tmpFlag;
}

OSCL_EXPORT_REF void PVMFMediaClock::GetCurrentTick32(uint32& aTimebaseTickCount, bool& aOverflow)
{
    aOverflow = false;

    if (iClockTimebase != NULL)
    {
        iClockTimebase->GetCurrentTick32(aTimebaseTickCount, aOverflow);
    }
    else
    {
        aTimebaseTickCount = 0;
    }
}

OSCL_EXPORT_REF void PVMFMediaClock::GetTimebaseResolution(uint32& aResolution)
{
    if (iClockTimebase)
    {
        // Retrieve the resolution from the timebase being used
        iClockTimebase->GetTimebaseResolution(aResolution);
    }
    else
    {
        // No timebase so set to 0
        aResolution = 0;
    }
}

OSCL_EXPORT_REF int32 PVMFMediaClock::GetRate(void)
{
    if (iClockTimebase)
    {
        // Retrieve the playback rate from the timebase being used
        return iClockTimebase->GetRate();
    }
    else
    {
        // No timebase so return realtime rate
        return REALTIME_PLAYBACK_RATE;
    }
}

OSCL_EXPORT_REF void PVMFMediaClock::GetCurrentTime32(uint32& aClockTime, bool& aOverflow, PVMFMediaClock_TimeUnits aUnits)
{
    uint32 temp = 0;

    GetCurrentTime32(aClockTime, aOverflow, aUnits, temp);
}

OSCL_EXPORT_REF void PVMFMediaClock::GetCurrentTime32(uint32& aClockTime, bool& aOverflow, PVMFMediaClock_TimeUnits aUnits, uint32& aTimebaseTime)
{
    // Get and return the current timebase value

    bool overflowFlag1 = false, overflowFlag2 = false, overflowFlag3 = false;

    aOverflow = false;

    // Get the current timebase time
    GetScaledTimebaseTickCount(aTimebaseTime, aOverflow);

    // Determine and return the current clock time
    if (iState == STOPPED)
    {
        // Return the specified start time
        FromClockUnit(iStartClockTime, aClockTime, aUnits, overflowFlag3);
    }
    else if (iState == PAUSED)
    {
        // Returned the paused time
        FromClockUnit(iPauseClockTime, aClockTime, aUnits, overflowFlag3);
    }
    else
    {
        // Running state
        uint32 currenttime;

        // Determine current clock time including any adjustment
        GetAdjustedRunningClockTime(currenttime, aTimebaseTime);

        // Convert to requested time units
        FromClockUnit(currenttime, aClockTime, aUnits, overflowFlag3);
    }

    aOverflow = aOverflow | overflowFlag1 | overflowFlag2 | overflowFlag3;
}

OSCL_EXPORT_REF bool PVMFMediaClock::QueryInterface(const PVUuid& uuid, PVInterface*& iface)
{
    if (uuid == PVMFMediaClockControlInterfaceUuid)
    {
        PVMFMediaClockControlInterface* myInterface =
            OSCL_STATIC_CAST(PVMFMediaClockControlInterface*, this);
        iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
    }
    else if (uuid == PVMFMediaClockAccessInterfaceUuid)
    {
        PVMFMediaClockAccessInterface* myInterface =
            OSCL_STATIC_CAST(PVMFMediaClockAccessInterface*, this);
        iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
    }
    else if (uuid == PVMFMediaClockNPTClockPositionAccessInterfaceUuid)
    {
        PVMFMediaClockNPTClockPositionAccessInterface* myInterface =
            OSCL_STATIC_CAST(PVMFMediaClockNPTClockPositionAccessInterface*, this);
        iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
    }
    else
    {
        return false;
    }

    return true;
}

OSCL_EXPORT_REF void PVMFMediaClock::SetClockObserver(PVMFMediaClockObserver& aObserver)
{
    iClockObservers.push_back(&aObserver);
}

OSCL_EXPORT_REF void PVMFMediaClock::RemoveClockObserver(PVMFMediaClockObserver& aObserver)
{
    for (uint32 i = 0; i < iClockObservers.size(); i++)
    {
        if (iClockObservers[i] == &aObserver)
            iClockObservers.erase(&iClockObservers[i]);
    }
}

void PVMFMediaClock::SetClockState(PVMFMediaClockState aState)
{
    // Change the clock state
    iState = aState;

    // Notify observers
    if (RUNNING == iState)
    {
        //Send notifications
        for (uint32 ii = 0; ii < iMediaClockSetCallbackObjects.size(); ii++)
        {
            if (iMediaClockSetCallbackObjects[ii]->iClockStateObserver != NULL)
            {
                (iMediaClockSetCallbackObjects[ii]->iClockStateObserver)->ClockStateUpdated();
            }
        }
    }
    else
    {
        for (uint32 ii = 0; ii < iMediaClockSetCallbackObjects.size(); ii++)
        {
            if (iMediaClockSetCallbackObjects[ii]->iClockStateObserver != NULL)
            {
                (iMediaClockSetCallbackObjects[ii]->iClockStateObserver)->ClockStateUpdated();
            }
        }
    }
}

void PVMFMediaClock::GetScaledTimebaseTickCount(uint32& aScaledTickCount, bool& aOverFlow)
{
    uint32 temp = 0;
    aOverFlow = false;

    if (iClockTimebase != NULL)
    {
        iClockTimebase->GetCurrentTick32(temp, aOverFlow);
    }

    PVTimeComparisonUtils::IsEarlier(iStartTimebaseTickValue, temp, aScaledTickCount);
}

void PVMFMediaClock::UpdateLatestTimes(uint32 aTime, uint32 aTimebaseVal)
{
    // Set the latest time values
    iLatestRunningClockTime = aTime;
    iLatestRunningTimebaseTime = aTimebaseVal;
}

void PVMFMediaClock::AdjustClockInternalsToNewUnits(bool& aOverFlow)
{

    uint32 temp = 0;

    aOverFlow = false;

    // Change the units
    if (PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC == iPreviousClockUnit)
    {
        ToClockUnit(iLatestRunningClockTime, PVMF_MEDIA_CLOCK_USEC, temp, aOverFlow);
        iLatestRunningClockTime = temp;

        ToClockUnit(iStartClockTime, PVMF_MEDIA_CLOCK_USEC, temp, aOverFlow);
        iStartClockTime = temp;

        ToClockUnit(iPauseClockTime, PVMF_MEDIA_CLOCK_USEC, temp, aOverFlow);
        iPauseClockTime = temp;
    }
    else if (PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC == iPreviousClockUnit)
    {
        ToClockUnit(iLatestRunningClockTime, PVMF_MEDIA_CLOCK_MSEC, temp, aOverFlow);
        iLatestRunningClockTime = temp;

        ToClockUnit(iStartClockTime, PVMF_MEDIA_CLOCK_MSEC, temp, aOverFlow);
        iStartClockTime = temp;

        ToClockUnit(iPauseClockTime, PVMF_MEDIA_CLOCK_MSEC, temp, aOverFlow);
        iPauseClockTime = temp;
    }
}

#define OSCL_DISABLE_WARNING_CONV_POSSIBLE_LOSS_OF_DATA
#include "osclconfig_compiler_warnings.h"

void PVMFMediaClock::ToClockUnit(uint32& aSrcVal, PVMFMediaClock_TimeUnits aSrcUnits, uint32& aDestVal, bool& aOverFlow)
{
    uint32 multconst = 1;

    aOverFlow = false;

    switch (iClockUnit)
    {
        case PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC:
        {
            if (PVMF_MEDIA_CLOCK_USEC == aSrcUnits)
            {
                aDestVal = aSrcVal / 1000;
            }
            else
            {
                // Determine the multiplier constant for the specified units
                switch (aSrcUnits)
                {
                    case PVMF_MEDIA_CLOCK_SEC:
                        multconst = 1000;
                        break;

                    case PVMF_MEDIA_CLOCK_MIN:
                        multconst = 60000;
                        break;

                    case PVMF_MEDIA_CLOCK_HOUR:
                        multconst = 0x36EE80;
                        break;

                    case PVMF_MEDIA_CLOCK_DAY:
                        multconst = 0x5265C00;
                        break;

                    case PVMF_MEDIA_CLOCK_MSEC:
                    default:
                        break;
                }

                // Convert value to clock units
                uint64 time64 = (uint64)(aSrcVal * multconst);

                //There is a chance that Tickcount did not wrap around but aTime value does
                if (time64 > (uint64)(0xFFFFFFFF))
                {
                    aOverFlow = true;
                }
                aDestVal = Oscl_Int64_Utils::get_uint64_lower32(time64);
            }
        }
        break;

        case PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC:
        {
            // Determine the multiplier constant for the specified units
            switch (aSrcUnits)
            {
                case PVMF_MEDIA_CLOCK_MSEC:
                    multconst = 1000;
                    break;

                case PVMF_MEDIA_CLOCK_SEC:
                    multconst = 1000000;
                    break;

                case PVMF_MEDIA_CLOCK_MIN:
                    multconst = 60000000;
                    break;

                case PVMF_MEDIA_CLOCK_HOUR:
                    multconst = 0xD693A400;
                    break;

                case PVMF_MEDIA_CLOCK_DAY:
                {
                    uint64 temp = UINT64_HILO(0x14, 0x1DD76000);
                    multconst = Oscl_Int64_Utils::get_uint64_lower32(temp);
                }
                break;

                case PVMF_MEDIA_CLOCK_USEC:
                default:
                    break;
            }

            // Convert value to clock units
            uint64 time64 = (uint64)(aSrcVal * multconst);
            //There is a chance that Tickcount did not wrap around but aTime value does
            if (time64 > (uint64)(0xFFFFFFFF))
            {
                aOverFlow = true;
            }
            aDestVal = Oscl_Int64_Utils::get_uint64_lower32(time64);
        }
        break;

        default:
        {
            break;
        }
    }

}

void PVMFMediaClock::FromClockUnit(uint32& aClockUnitVal, uint32& aDstVal,
                                   PVMFMediaClock_TimeUnits aDstUnits, bool& aOverflow)
{

    uint32 divconst = 1;

    aOverflow = false;

    switch (iClockUnit)
    {
        case PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC:
        {
            if (PVMF_MEDIA_CLOCK_USEC == aDstUnits)
            {
                //detect overflow;
                uint64 time64 = (uint64)(aClockUnitVal * 1000);
                if (time64 > (uint64)(0xFFFFFFFF))
                {
                    aOverflow = true;
                }
                aDstVal = Oscl_Int64_Utils::get_uint64_lower32(time64);
            }
            else
            {
                // Determine the multiplier constant for the specified units
                switch (aDstUnits)
                {
                    case PVMF_MEDIA_CLOCK_SEC:
                        divconst = 1000;
                        break;

                    case PVMF_MEDIA_CLOCK_MIN:
                        divconst = 60000;
                        break;

                    case PVMF_MEDIA_CLOCK_HOUR:
                        divconst = 3600000;
                        break;

                    case PVMF_MEDIA_CLOCK_DAY:
                        divconst = 86400000;
                        break;

                    case PVMF_MEDIA_CLOCK_MSEC:
                    default:
                        break;
                }

                // Convert value to desired units
                aDstVal = aClockUnitVal / divconst;
            }
        }
        break;

        case PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC:
        {
            // Determine the multiplier constant for the specified units
            switch (aDstUnits)
            {
                case PVMF_MEDIA_CLOCK_MSEC:
                    divconst = 1000;
                    break;

                case PVMF_MEDIA_CLOCK_SEC:
                    divconst = 1000000;
                    break;

                case PVMF_MEDIA_CLOCK_MIN:
                    divconst = 60000000;
                    break;

                case PVMF_MEDIA_CLOCK_HOUR:
                    divconst = 0xD693A400;
                    break;

                case PVMF_MEDIA_CLOCK_DAY:
                {
                    uint64 temp = UINT64_HILO(0x14, 0x1DD76000);
                    divconst = Oscl_Int64_Utils::get_uint64_lower32(temp);
                }
                break;

                case PVMF_MEDIA_CLOCK_USEC:
                default:
                    break;
            }
            // Convert value to desired units
            aDstVal = aClockUnitVal / divconst;

        }
        break;

        default:
        {
            break;
        }
    }
}

void PVMFMediaClock::ConvertTickcountToClockUnits(uint32 aTickcount, uint32& aTimeValue, bool& aOverflowFlag)
{
    uint32 tbval = aTickcount;

    aOverflowFlag = false;

    //Convert tickCount value to msecs
    uint32 usecPerTick = 0;
    GetTimebaseResolution(usecPerTick);

    if (usecPerTick)
    {
        tbval = tbval * (usecPerTick / 1000);
    }
    else /*timebase is not set*/
    {
        tbval = 0;
    }

    ToClockUnit(tbval, PVMF_MEDIA_CLOCK_MSEC, aTimeValue, aOverflowFlag);
}
void PVMFMediaClock::ToUSec(uint32& aSrcVal, PVMFMediaClock_TimeUnits aSrcUnits, uint32& aUSecVal, bool& aOverFlow)
{
    uint32 multconst = 1;

    aOverFlow = false;


    // Determine the multiplier constant for the specified units
    switch (aSrcUnits)
    {
        case PVMF_MEDIA_CLOCK_MSEC:
            multconst = 1000;
            break;

        case PVMF_MEDIA_CLOCK_SEC:
            multconst = 1000000;
            break;

        case PVMF_MEDIA_CLOCK_MIN:
            multconst = 60000000;
            break;

        case PVMF_MEDIA_CLOCK_HOUR:
            multconst = 0xD693A400;
            break;

        case PVMF_MEDIA_CLOCK_DAY:
        {
            uint64 temp = UINT64_HILO(0x14, 0x1DD76000);
            multconst = Oscl_Int64_Utils::get_uint64_lower32(temp);
        }
        break;

        case PVMF_MEDIA_CLOCK_USEC:
        default:
            break;
    }

    // Convert value to clock units
    uint64 time64 = (uint64)(aSrcVal * multconst);
    //There is a chance that Tickcount did not wrap around but aTime value does
    if (time64 > (uint64)(0xFFFFFFFF))
    {
        aOverFlow = true;
    }

    aUSecVal = Oscl_Int64_Utils::get_uint64_lower32(time64);
}

PVMFMediaClockAdjustTimeStatus PVMFMediaClock::AdjustClock(uint32& aObsTime, uint32& aObsTimebase, uint32& aAdjTime,
        uint32& aCurrentTime, uint32& aCurrentTimebase)
{
    // In this implementation, don't allow adjustments to be made with
    // data older than when the last adjustment was made
    uint32 temp = 0;
    if (PVTimeComparisonUtils::IsEarlier(aObsTimebase, iAdjustmentTimebaseTime, temp) && (temp != 0))
    {
        return PVMF_MEDIA_CLOCK_ADJUST_ERR_INVALID_TIMEBASE_TIME;
    }

    // Make the adjustment
    if (PVTimeComparisonUtils::IsEarlier(aObsTime, aAdjTime, temp) && (temp != 0))
    {
        // Adjusted time is ahead so move ahead

        // Save the observed timebase time of the adjusted time
        iLastAdjustObsTimebaseTime = aObsTimebase;
        UpdateLatestTimes(aAdjTime, aObsTimebase);

        // Set the latest adjustment time as the current timebase time
        iAdjustmentTimebaseTime = aCurrentTimebase;
    }
    else if (PVTimeComparisonUtils::IsEarlier(aAdjTime , aObsTime, temp) && (temp != 0))
    {
        // Adjusted time is before the current time

        // Save the observed timebase time of the adjusted time
        iLastAdjustObsTimebaseTime = aObsTimebase;

        // Set the latest clock time to the current clock time
        // Set the latest timebase time to (current timebase time) + ((observed clock time) - (adjusted time))
        uint32 offsettimebase = 0;
        uint32 usecPerTick = 0;

        GetTimebaseResolution(usecPerTick);

        //calculate ticks in offsettbtime
        if (PVMF_MEDIA_CLOCK_CLOCKUNIT_MSEC == iClockUnit)
        {
            uint32 usecInOffset = (aObsTime - aAdjTime) * 1000;

            //Calculate number of ticks
            offsettimebase = usecInOffset / usecPerTick;
        }
        else if (PVMF_MEDIA_CLOCK_CLOCKUNIT_USEC == iClockUnit)
        {
            offsettimebase = (aObsTime - aAdjTime) / usecPerTick;
        }

        UpdateLatestTimes(aCurrentTime, aCurrentTimebase + offsettimebase);
        iAdjustmentTimebaseTime = aCurrentTimebase;
    }
    else
    {
        // Since there is no adjustment, do nothing
    }

    //Start fresh scheduling
    AdjustScheduling();

    ClockAdjusted();
    return PVMF_MEDIA_CLOCK_ADJUST_SUCCESS;
}

void PVMFMediaClock::GetAdjustedRunningClockTime(uint32& aDstTime, uint32& aTimebaseVal)
{
    uint32 delta = 0;
    // Current time is (latest clock time)+(current timebase time - latest timebase time)
    aDstTime = iLatestRunningClockTime;

    // If backward adjustment occurs, iLatestRunningTimebaseTime might be greater than
    // the current value. To avoid negative values and clock going back, only
    // add the diff if current timebase value is greater. This makes the clock "freeze" until
    // the difference has elapsed
    if (PVTimeComparisonUtils::IsEarlier(iLatestRunningTimebaseTime, aTimebaseVal, delta) && (delta != 0))
    {
        //convert delta to clock units
        uint32 deltaTime = 0;
        bool overflowFlag = false;
        ConvertTickcountToClockUnits(delta, deltaTime, overflowFlag);
        aDstTime += deltaTime;
    }
}

void PVMFMediaClock::ClockCountUpdated()
{
    //Calling Run will fire any ready timers.
    Run();

    //notify all observers that the clock count was updated.
    for (uint32 i = 0; i < iClockObservers.size(); i++)
        iClockObservers[i]->ClockCountUpdated();
}

void PVMFMediaClock::ClockAdjusted()
{
    //notify all observers that the clock was adjusted
    for (uint32 i = 0; i < iClockObservers.size(); i++)
        iClockObservers[i]->ClockAdjusted();
}

void PVMFMediaClock::ClockTimebaseUpdated()
{
    //notify all observers that the clock timebase was updated.
    for (uint32 i = 0; i < iClockObservers.size(); i++)
    {
        PVMFMediaClockObserver* obs = iClockObservers[i];
        obs->ClockTimebaseUpdated();
    }
    //reset timebase history.
    iLastAdjustObsTimebaseTime = 0;
    iAdjustmentTimebaseTime = 0;
    iStartTimebaseTickValue = 0;
}

PVMFStatus PVMFMediaClock::SetCallbackCommon(uint32 aAbsoluteTime,
        uint32 aWindow,
        PVMFMediaClockNotificationsObs* aCallback,
        bool aThreadLock,
        const OsclAny* aContextData,
        uint32& aCallBackID,
        const OsclAny* aInterfaceObject,
        uint32 aCurrentTime, bool aIsNPT)
{
    uint32 delta = 0;

    if (NULL == aCallback)
    {
        return PVMFErrArgument;
    }

    //absolute time should be later than current time.
    //upper limit of 30 min for timer

    if (!aIsNPT || (aIsNPT && !iIsNPTPlayBackDirectionBackwards))
    {
        if (PVTimeComparisonUtils::IsEarlier(aCurrentTime + MSECS_IN_30_MINS, aAbsoluteTime, delta) ||
                PVTimeComparisonUtils::IsEarlier(aAbsoluteTime, aCurrentTime, delta))
        {
            return PVMFErrArgument;
        }
    }
    else    /*If this is NPT and clock direction is backwards. So, conditions will be opposite.*/
    {
        if (PVTimeComparisonUtils::IsEarlier(aAbsoluteTime, aCurrentTime + MSECS_IN_30_MINS, delta) ||
                PVTimeComparisonUtils::IsEarlier(aCurrentTime, aAbsoluteTime, delta))
        {
            return PVMFErrArgument;
        }
    }


    if (aThreadLock)
        iMutex->Lock();

    aCallBackID = iTimerIDCount++;

    //insert the timer in the queue
    //
    PVMFMediaClockTimerQueueElement timerQueueElement;

    timerQueueElement.contextData = aContextData;
    timerQueueElement.pInterfaceObject = aInterfaceObject;
    timerQueueElement.timeOut = aAbsoluteTime;
    timerQueueElement.callBackID = aCallBackID;
    timerQueueElement.isNPTTimer = aIsNPT;
    timerQueueElement.window = aWindow;
    timerQueueElement.obs = aCallback;
    if (!aIsNPT)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                        (0, "PVMFMediaClock::SetCallbackCommon Setting regular callback for time %d at time %d",
                         aAbsoluteTime, aCurrentTime));

        iTimersPriQueue.push(timerQueueElement);

        //Adjust scheduling if the element inserted is the topmost element
        if ((iTimersPriQueue.top()).callBackID == (iTimerIDCount - 1))
        {
            AdjustScheduling(false, aCurrentTime);
        }
    }
    else
    {
        if (!iIsNPTPlayBackDirectionBackwards)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::SetCallbackCommon Setting NPT callback for time %d at time %d",
                             aAbsoluteTime, aCurrentTime));

            iTimersPriQueueNPT.push(timerQueueElement);
            if ((iTimersPriQueueNPT.top()).callBackID == (iTimerIDCount - 1))
            {
                AdjustScheduling(true, aCurrentTime);
            }
        }
        else
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::SetCallbackCommon Setting backwards NPT callback for time %d at time %d",
                             aAbsoluteTime, aCurrentTime));

            iTimersPriQueueNPTBackwards.push(timerQueueElement);
            if ((iTimersPriQueueNPT.top()).callBackID == (iTimerIDCount - 1))
            {
                AdjustScheduling(true, aCurrentTime);
            }
        }

    }
    iActiveTimersCount++;

    if (aThreadLock)
        iMutex->Unlock();

    return PVMFSuccess;
}

PVMFStatus PVMFMediaClock::SetCallbackAbsoluteTime(
    uint32 aAbsoluteTime,
    uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID,
    /*IN*/  const OsclAny* aInterfaceObject
)
{
    uint32 currentTime = 0;
    bool overFlowFlag = false;

    GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);

    return SetCallbackCommon(aAbsoluteTime, aWindow, aCallback,
                             aThreadLock, aContextData, aCallBackID, aInterfaceObject, currentTime, false);

}

void PVMFMediaClock::CalculateRunLTimerValue(bool aIsNPT, uint32 aCurrentTime, int32& aDelta)
{
    int32 nptDelta = 0;
    int32 delta = 0;

    if (iTimersPriQueueNPT.size() ||
            iTimersPriQueueNPTBackwards.size())
    {
        uint32 temp = 0;
        GetNPTClockPosition(temp);

        if (!iIsNPTPlayBackDirectionBackwards)
        {
            if (iTimersPriQueueNPT.size())
            {
                nptDelta = iTimersPriQueueNPT.top().timeOut - temp;
            }
        }
        else
        {
            if (iTimersPriQueueNPTBackwards.size())
            {
                nptDelta = temp - iTimersPriQueueNPT.top().timeOut;
            }
        }

        if (!iTimersPriQueue.size())
        {
            aDelta = nptDelta;
            return;
        }
    }

    if (iTimersPriQueue.size())
    {
        bool overFlowFlag = false;

        uint32 currentTime = 0;

        if (!aIsNPT)
        {
            currentTime = aCurrentTime;
        }
        else
        {
            GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);
        }


        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                        (0, "PVMFMediaClock::CalculateRunLTimerValue currtime %d top.timeOut %d", currentTime, iTimersPriQueue.top().timeOut));

        delta = iTimersPriQueue.top().timeOut - currentTime;

        if (!iTimersPriQueueNPT.size()
                && !iTimersPriQueueNPTBackwards.size())
        {
            aDelta = delta;
            return;
        }
    }

    aDelta = delta < nptDelta ? delta : nptDelta;
}

void PVMFMediaClock::AdjustScheduling(bool aIsNPT, uint32 aCurrentTime)
{

    //If timebase is count-based, no need for scheduling.
    if (iIsTimebaseCountBased)
    {
        return;
    }
    //Make sure current thread context is same on which AddToScheduler() was called

    TOsclThreadId tempThreadID;
    OsclThread::GetId(tempThreadID);

    if (!OsclThread::CompareId(tempThreadID, iOrigThreadID))
    {
        OsclError::Leave(OsclErrThreadContextIncorrect);
    }

    uint32 currentTime = 0;
    bool overFlowFlag = false;

    // A fresh RunIfInactive() will be called
    Cancel();

    //get current time if argument aCurrentTime is 0
    if (!aIsNPT)
    {
        if (aCurrentTime)
        {
            currentTime = aCurrentTime;
        }
        else
        {
            GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);
        }
    }
    else
    {
        aCurrentTime ? currentTime = aCurrentTime : GetNPTClockPosition(currentTime);
    }

    int32 deltaTime = 1;

    //if queues are empty, no need to schedule
    if (iTimersPriQueue.size() != 0 ||
            iTimersPriQueueNPT.size() != 0 ||
            iTimersPriQueueNPTBackwards.size() != 0)
    {
        CalculateRunLTimerValue(aIsNPT, currentTime, deltaTime);
        if (deltaTime >= 0)
        {
            //Adjust for rate
            if ((iClockTimebase != NULL) && (iClockTimebase->GetRate() != 0)
                    && (REALTIME_PLAYBACK_RATE != iClockTimebase->GetRate()))
            {
                //Support rate upto 0.1 resolution
                uint32 convNumerator = 10;
                uint32 conversionNumber =
                    (iClockTimebase->GetRate() * convNumerator) / REALTIME_PLAYBACK_RATE;

                if (conversionNumber != 0)
                {
                    deltaTime = (deltaTime * convNumerator) / conversionNumber ;
                }
                else
                {
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                    (0, "PVMFMediaClock::AdjustScheduling ERROR: Timebase rate corrupted"));
                }
            }

            RunIfNotReady((uint32)deltaTime*1000);

            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::AdjustScheduling Timer set for %d msecs wall clock time", deltaTime));
        }
        else
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::AdjustScheduling Late callback detected", deltaTime));
            //this means callback is already late. fire asap
            RunIfNotReady(0);
        }
    }
}

PVMFStatus PVMFMediaClock::SetCallbackDeltaTime(
    /*IN*/  uint32 aDeltaTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID,
    /*IN*/  const OsclAny* aInterfaceObject)
{
    uint32 currentTime = 0;
    bool overFlowFlag = false;

    GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);

    return SetCallbackCommon(currentTime + aDeltaTime, aWindow, aCallback,
                             aThreadLock, aContextData, aCallBackID, aInterfaceObject, currentTime, false);
}

PVMFStatus PVMFMediaClock::CommonCancelCallback(uint32 aCallbackID, bool aThreadLock, bool aIsNPT)
{
    bool flag = 0;
    int elementRemoved = 0;
    PVMFStatus retVal;

    if (aThreadLock)
        iMutex->Lock();

    if (!aIsNPT)
    {
        if (!iTimersPriQueue.empty())
        {
            if ((iTimersPriQueue.top()).callBackID == (aCallbackID))
            {
                flag = 1;
            }

            //Remove element from queue by ID
            PVMFMediaClockTimerQueueElement timerQueueElement;
            timerQueueElement.callBackID = aCallbackID;

            elementRemoved = iTimersPriQueue.remove(timerQueueElement);

            if (elementRemoved && flag)
            {
                AdjustScheduling();
            }
        }
    }
    else
    {
        if (!iIsNPTPlayBackDirectionBackwards)
        {
            if (!iTimersPriQueueNPT.empty())
            {
                if ((iTimersPriQueueNPT.top()).callBackID == (aCallbackID))
                {
                    flag = 1;
                }

                //Remove element from queue by ID
                PVMFMediaClockTimerQueueElement timerQueueElement;
                timerQueueElement.callBackID = aCallbackID;

                elementRemoved = iTimersPriQueueNPT.remove(timerQueueElement);
            }
        }
        else
        {
            if (!iTimersPriQueueNPTBackwards.empty())
            {
                if ((iTimersPriQueueNPTBackwards.top()).callBackID == (aCallbackID))
                {
                    flag = 1;
                }

                //Remove element from queue by ID
                PVMFMediaClockTimerQueueElement timerQueueElement;
                timerQueueElement.callBackID = aCallbackID;

                elementRemoved = iTimersPriQueueNPTBackwards.remove(timerQueueElement);
            }
        }

        if (elementRemoved && flag)
        {
            /*When scheduling is for NPT, make sure to pass isNPT flag i.e. 'true' */
            AdjustScheduling(true);
        }

    }

    if (elementRemoved)
    {
        iActiveTimersCount--;
        retVal = PVMFSuccess;
    }
    else
    {
        retVal = PVMFErrBadHandle;
    }
    if (aThreadLock)
        iMutex->Unlock();

    return retVal;
}

PVMFStatus PVMFMediaClock::CancelCallback(
    /*IN*/  uint32 aCallbackID, bool aThreadLock)
{
    return CommonCancelCallback(aCallbackID, aThreadLock, false);
}

PVMFStatus PVMFMediaClock::SetNPTCallbackAbsoluteTime(
    /*IN*/  uint32 aAbsoluteTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID,
    /*IN*/  const OsclAny* aInterfaceObject)
{

    uint32 currentTime = 0;

    GetNPTClockPosition(currentTime);

    return SetCallbackCommon(aAbsoluteTime, aWindow, aCallback,
                             aThreadLock, aContextData, aCallBackID, aInterfaceObject, currentTime, true);

}

PVMFStatus PVMFMediaClock::SetNPTCallbackDeltaTime(
    /*IN*/  uint32 aDeltaTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID,
    /*IN*/  const OsclAny* aInterfaceObject)
{
    uint32 currentTime = 0;

    GetNPTClockPosition(currentTime);

    return SetCallbackCommon(currentTime + aDeltaTime, aWindow, aCallback,
                             aThreadLock, aContextData, aCallBackID, aInterfaceObject, currentTime, true);

}

PVMFStatus PVMFMediaClock::CancelNPTCallback(
    /*IN*/  uint32 aCallbackID, bool aThreadLock)
{
    return CommonCancelCallback(aCallbackID, aThreadLock, true);
}

void PVMFMediaClock::ClearAllQueues()
{
    PVMFMediaClockTimerQueueElement topTimerElement;

    while (iTimersPriQueue.size() != 0)
    {
        topTimerElement = iTimersPriQueue.top();
        iTimersPriQueue.pop();
        iActiveTimersCount--;
        topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID,
                                             PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW, 0,
                                             topTimerElement.contextData, PVMFErrCallbackClockStopped);
    }

    if (!iIsNPTPlayBackDirectionBackwards)
    {
        while (iTimersPriQueueNPT.size() != 0)
        {
            topTimerElement = iTimersPriQueueNPT.top();
            iTimersPriQueueNPT.pop();
            iActiveTimersCount--;
            topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID,
                                                 PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW, 0,
                                                 topTimerElement.contextData, PVMFErrCallbackClockStopped);
        }
    }
    else
    {
        while (iTimersPriQueueNPTBackwards.size() != 0)
        {
            topTimerElement = iTimersPriQueueNPTBackwards.top();
            iTimersPriQueueNPTBackwards.pop();
            iActiveTimersCount--;
            topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID,
                                                 PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW, 0,
                                                 topTimerElement.contextData, PVMFErrCallbackClockStopped);
        }
    }

}

void PVMFMediaClock::ClearPresentNPTQueue()
{
    PVMFMediaClockTimerQueueElement topTimerElement;

    if (!iIsNPTPlayBackDirectionBackwards)
    {
        while (iTimersPriQueueNPT.size() != 0)
        {
            topTimerElement = iTimersPriQueueNPT.top();
            iTimersPriQueueNPT.pop();
            iActiveTimersCount--;
            topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID,
                                                 PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW, 0,
                                                 topTimerElement.contextData, PVMFErrCallbackHasBecomeInvalid);
        }
    }
    else
    {
        while (iTimersPriQueueNPTBackwards.size() != 0)
        {
            topTimerElement = iTimersPriQueueNPTBackwards.top();
            iTimersPriQueueNPTBackwards.pop();
            iActiveTimersCount--;
            topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID,
                                                 PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW, 0,
                                                 topTimerElement.contextData, PVMFErrCallbackHasBecomeInvalid);
        }
    }
}

OSCL_EXPORT_REF void PVMFMediaClock::UpdateNPTClockPosition(
    /*IN*/  uint32 aStartNPT,
    /*IN*/  bool aIsPlayBackDirectionBackwards)
{
    bool overflow = false;

    iStartNPT = aStartNPT;

    GetCurrentTime32(iStartMediaClockTS, overflow, PVMF_MEDIA_CLOCK_MSEC);

    if (iIsNPTPlayBackDirectionBackwards != aIsPlayBackDirectionBackwards)
    {
        //NPT clock direction has changed. So invalidate all existing timers
        ClearPresentNPTQueue();
    }

    iIsNPTPlayBackDirectionBackwards = aIsPlayBackDirectionBackwards;

    /*reschedule. Send argument as true so that this function knows that
     scheduling is being done for NPT.*/
    AdjustScheduling(true);
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClock::GetNPTClockPosition(
    /*OUT*/ uint32& aCurrentPosition)
{
    uint32 currentTime = 0;
    bool overflow = false;

    GetCurrentTime32(currentTime, overflow, PVMF_MEDIA_CLOCK_MSEC);

    if (overflow)
    {
        return PVMFErrOverflow;
    }

    int32 currentposition;
    if (iIsNPTPlayBackDirectionBackwards)
    {
        currentposition = iStartNPT - (currentTime - iStartMediaClockTS);
    }
    else
    {
        currentposition = iStartNPT + (currentTime - iStartMediaClockTS);
    }
    aCurrentPosition = (currentposition > 0) ? (uint32)currentposition : 0;

    return PVMFSuccess;
}

void PVMFMediaClock::ClearNPTClockPosition()
{
    iStartNPT = 0;
    iStartMediaClockTS = 0;
    iIsNPTPlayBackDirectionBackwards = 0;
}

void PVMFMediaClock::QueueClockStartNotificationEvent(uint32 aDelta, PVMFMediaClockStateObserver *aClockStateObserver)
{
    PVMFMediaClockStartNotificationEventElement element;
    uint32 queuedEventID;
    PVMFStatus status = PVMFFailure;

    element.clockStateObserver = aClockStateObserver;

    //Set a callback on the clock to set this event
    status = SetCallbackDeltaTime(aDelta, 0, (PVMFMediaClockNotificationsObs*)this, false,
                                  &iClockStartNotificationEventQueue, queuedEventID, this);

    element.eventID = queuedEventID;
    if (PVMFSuccess == status)
    {
        iClockStartNotificationEventQueue.push_back(element);
    }

}

PVMFStatus PVMFMediaClock::QueueNPTClockTransitionEvent(uint32 aMediaClockPosition, uint32 aStartNPT,
        bool aIsPlayBackDirectionBackwards, uint32 aWindow, uint32& aClockTransitionID)
{
    PVMFMediaClockNPTTransitionEventElement element;
    PVMFStatus status = PVMFFailure;

    element.isPlayBackDirectionBackwards = aIsPlayBackDirectionBackwards;
    element.mediaClockPosition = aMediaClockPosition;
    element.startNPT = aStartNPT;
    element.window = aWindow;

    //Set a callback on the clock to set this event
    status = SetCallbackAbsoluteTime(element.mediaClockPosition, element.window, (PVMFMediaClockNotificationsObs*)this, false,
                                     &iNPTTransitionEventQueue, aClockTransitionID, this);

    element.eventID = aClockTransitionID;
    if (PVMFSuccess == status)
    {
        iNPTTransitionEventQueue.push_back(element);
    }

    return status;
}

PVMFStatus PVMFMediaClock::CancelNPTClockTransitionEvent(uint32 aClockTransitionEventID)
{
    PVMFStatus status;

    status = CancelCallback(aClockTransitionEventID, false);

    if (PVMFSuccess != status)
    {
        return status;
    }

    for (uint32 ii = 0; ii < iNPTTransitionEventQueue.size(); ii++)
    {
        if (iNPTTransitionEventQueue[ii].eventID == aClockTransitionEventID)
            iNPTTransitionEventQueue.erase(&iNPTTransitionEventQueue[ii]);
    }
    return PVMFSuccess;
}

void PVMFMediaClock::ProcessCallBack(uint32 aCallBackID, PVTimeComparisonUtils::MediaTimeStatus aTimerAccuracy, uint32 delta,
                                     const OsclAny* aContextData, PVMFStatus aStatus)
{

    OSCL_UNUSED_ARG(aTimerAccuracy);
    OSCL_UNUSED_ARG(delta);
    if (aStatus != PVMFSuccess)
    {
        /*This means the clock was stopped*/
        return;
    }

    //if event is NPT transition
    if (aContextData == (void*)&iNPTTransitionEventQueue)
    {
        for (uint32 ii = 0; ii < iNPTTransitionEventQueue.size(); ii++)
        {
            if (iNPTTransitionEventQueue[ii].eventID == aCallBackID)
            {
                UpdateNPTClockPosition(iNPTTransitionEventQueue[ii].startNPT,
                                       iNPTTransitionEventQueue[ii].isPlayBackDirectionBackwards);

                iNPTTransitionEventQueue.erase(&iNPTTransitionEventQueue[ii]);
            }
        }
    }
    //if event is clock-start notification
    else if (aContextData == (void*)&iClockStartNotificationEventQueue)
    {
        for (uint32 ii = 0; ii < iClockStartNotificationEventQueue.size(); ii++)
        {
            if (iClockStartNotificationEventQueue[ii].eventID == aCallBackID)
            {
                (iClockStartNotificationEventQueue[ii].clockStateObserver)->ClockStateUpdated();
                iClockStartNotificationEventQueue.erase(&iClockStartNotificationEventQueue[ii]);
            }
        }
    }

}

void PVMFMediaClock::NotificationsInterfaceDestroyed()
{
    //do nothing
}

void PVMFMediaClock::Run()
{
    uint32 currentTime = 0;
    bool overFlowFlag = false;
    uint32 delta = 0;

    PVMFMediaClockTimerQueueElement topTimerElement;
    PVTimeComparisonUtils::MediaTimeStatus status;

    /*Caution: Both loops below should always be similar. Any update needed for regular callback handling
    loop should also be checked for NPT loop ..and vice versa*/

    if (iTimersPriQueue.size())
    {
        topTimerElement = iTimersPriQueue.top();

        GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);

        status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                 topTimerElement.window, delta);

        while (iTimersPriQueue.size() &&
                status != PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                            (0, "PVMFMediaClock::Run Timer for regular callback currentTime - %d callbackTime - %d callbackMargin - %d queue size - %d status - %d", currentTime,
                             topTimerElement.timeOut, topTimerElement.window, iTimersPriQueue.size(), status));

            switch (status)
            {
                case PVTimeComparisonUtils::MEDIA_EARLY_WITHIN_WINDOW:
                case PVTimeComparisonUtils::MEDIA_ONTIME_WITHIN_WINDOW:
                case PVTimeComparisonUtils::MEDIA_LATE_WITHIN_WINDOW:
                case PVTimeComparisonUtils::MEDIA_LATE_OUTSIDE_WINDOW:
                {
                    iTimersPriQueue.pop();
                    iActiveTimersCount--;
                    topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID, status, delta,
                                                         topTimerElement.contextData, PVMFSuccess);
                }
                break;

                case PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW:
                    //If callback is early, just schedule fresh
                    break;

                default:
                {
                    //@@error
                }
            }

            //check if more timers fall within the window.
            topTimerElement = iTimersPriQueue.top();

            GetCurrentTime32(currentTime, overFlowFlag, PVMF_MEDIA_CLOCK_MSEC);

            status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                     topTimerElement.window, delta);
        }

        AdjustScheduling(false, currentTime);
    }

    // Check NPT timers now

    //normal NPT clock
    if (!iIsNPTPlayBackDirectionBackwards)
    {
        if (iTimersPriQueueNPT.size())
        {
            topTimerElement = iTimersPriQueueNPT.top();

            GetNPTClockPosition(currentTime);

            status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                     topTimerElement.window, delta);

            while (iTimersPriQueueNPT.size() &&
                    status != PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                                (0, "PVMFMediaClock::Run Timer for NPT callback currentTime - %d callbackTime - %d callbackMargin - %d queue size - %d status - %d", currentTime,
                                 topTimerElement.timeOut, topTimerElement.window, iTimersPriQueueNPT.size(), status));

                switch (status)
                {
                    case PVTimeComparisonUtils::MEDIA_EARLY_WITHIN_WINDOW:
                    case PVTimeComparisonUtils::MEDIA_ONTIME_WITHIN_WINDOW:
                    case PVTimeComparisonUtils::MEDIA_LATE_WITHIN_WINDOW:
                    case PVTimeComparisonUtils::MEDIA_LATE_OUTSIDE_WINDOW:
                    {
                        iTimersPriQueueNPT.pop();
                        iActiveTimersCount--;
                        topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID, status, delta,
                                                             topTimerElement.contextData, PVMFSuccess);
                    }
                    break;

                    case PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW:
                        //If callback is early, just schedule fresh
                        break;

                    default:
                    {
                        //@@error
                    }
                }

                //check if more timers fall within the window.
                topTimerElement = iTimersPriQueueNPT.top();

                GetNPTClockPosition(currentTime);

                status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                         topTimerElement.window, delta);
            }

            AdjustScheduling(true, currentTime);
        }
    }
    else  /*When direction of NPT clock is backwards. Just use the other queue and reverse calculations*/
    {
        if (iTimersPriQueueNPTBackwards.size())
        {
            topTimerElement = iTimersPriQueueNPTBackwards.top();

            GetNPTClockPosition(currentTime);

            status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                     topTimerElement.window, delta);

            while (iTimersPriQueueNPTBackwards.size() &&
                    status != PVTimeComparisonUtils::MEDIA_LATE_OUTSIDE_WINDOW)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_INFO,
                                (0, "PVMFMediaClock::Run Timer for Backwards NPT callback currentTime - %d callbackTime - %d callbackMargin - %d queue size - %d status - %d", currentTime,
                                 topTimerElement.timeOut, topTimerElement.window, iTimersPriQueueNPTBackwards.size(), status));

                switch (status)
                {
                    case PVTimeComparisonUtils::MEDIA_EARLY_WITHIN_WINDOW:
                    {
                        iTimersPriQueueNPT.pop();
                        iActiveTimersCount--;
                        topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID, PVTimeComparisonUtils::MEDIA_LATE_WITHIN_WINDOW, delta,
                                                             topTimerElement.contextData, PVMFSuccess);
                    }
                    break;

                    case PVTimeComparisonUtils::MEDIA_ONTIME_WITHIN_WINDOW:
                    case PVTimeComparisonUtils::MEDIA_LATE_WITHIN_WINDOW:
                    {
                        iTimersPriQueueNPT.pop();
                        iActiveTimersCount--;
                        topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID, PVTimeComparisonUtils::MEDIA_EARLY_WITHIN_WINDOW, delta,
                                                             topTimerElement.contextData, PVMFSuccess);
                    }
                    break;
                    case PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW:
                    {
                        iTimersPriQueueNPT.pop();
                        iActiveTimersCount--;
                        topTimerElement.obs->ProcessCallBack(topTimerElement.callBackID, PVTimeComparisonUtils::MEDIA_LATE_OUTSIDE_WINDOW, delta,
                                                             topTimerElement.contextData, PVMFSuccess);
                    }
                    break;

                    default:
                    {
                        //@@error
                    }
                }

                //check if more timers fall within the window.
                topTimerElement = iTimersPriQueueNPTBackwards.top();

                GetNPTClockPosition(currentTime);

                status = PVTimeComparisonUtils::CheckTimeWindow(topTimerElement.timeOut, currentTime, topTimerElement.window,
                         topTimerElement.window, delta);
            }

            AdjustScheduling(true, currentTime);
        }
    }
}

OSCL_EXPORT_REF PVMFMediaClockNotificationsInterfaceImpl::PVMFMediaClockNotificationsInterfaceImpl(PVMFMediaClock *aClock,
        uint32 aLatency,
        PVMFMediaClockNotificationsObsBase& aNotificationInterfaceDestroyedCallback)
{
    iContainer = aClock;
    iLatency = aLatency;
    iNotificationInterfaceDestroyedCallback = &aNotificationInterfaceDestroyedCallback;
    iClockStateObserver = NULL;
}

OSCL_EXPORT_REF PVMFMediaClockNotificationsInterfaceImpl::~PVMFMediaClockNotificationsInterfaceImpl()
{
    //check if vectors need to be destroyed
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::SetLatency(
    /*IN*/ uint32 aLatency)
{
    iLatency = aLatency;
    if (iContainer)
    {
        iContainer->UpdateHighestLatency(iLatency);
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::SetCallbackAbsoluteTime(
    /*IN*/  uint32 aAbsoluteTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID)
{
    if (iContainer)
    {
        return iContainer->SetCallbackAbsoluteTime(aAbsoluteTime - iLatency, aWindow, aCallback, aThreadLock, aContextData,
                aCallBackID, this);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::SetCallbackDeltaTime(
    /*IN*/  uint32 aDeltaTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID)
{
    if (iContainer)
    {
        return iContainer->SetCallbackDeltaTime(aDeltaTime, aWindow, aCallback, aThreadLock, aContextData,
                                                aCallBackID, this);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::CancelCallback(
    /*IN*/  uint32 aCallbackID, bool aThreadLock)
{
    if (iContainer)
    {
        return iContainer->CancelCallback(aCallbackID, aThreadLock);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::SetNPTCallbackAbsoluteTime(
    /*IN*/  uint32 aAbsoluteTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID)
{
    if (iContainer)
    {
        return iContainer->SetNPTCallbackAbsoluteTime(aAbsoluteTime - iLatency, aWindow, aCallback, aThreadLock, aContextData,
                aCallBackID, this);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::SetNPTCallbackDeltaTime(
    /*IN*/  uint32 aDeltaTime,
    /*IN*/  uint32 aWindow,
    /*IN*/  PVMFMediaClockNotificationsObs* aCallback,
    /*IN*/  bool aThreadLock,
    /*IN*/  const OsclAny* aContextData,
    /*OUT*/ uint32& aCallBackID)
{
    if (iContainer)
    {
        return iContainer->SetNPTCallbackDeltaTime(aDeltaTime , aWindow, aCallback, aThreadLock, aContextData,
                aCallBackID, this);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFMediaClockNotificationsInterfaceImpl::CancelNPTCallback(
    /*IN*/  uint32 aCallbackID, bool aThreadLock)
{
    if (iContainer)
    {
        return iContainer->CancelNPTCallback(aCallbackID, aThreadLock);
    }
    else
    {
        return PVMFFailure;
    }
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::SetClockObserver(PVMFMediaClockObserver& aObserver)
{
    if (iContainer)
    {
        iContainer->SetClockObserver(aObserver);
    }
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::RemoveClockObserver(PVMFMediaClockObserver& aObserver)
{
    if (iContainer)
    {
        iContainer->RemoveClockObserver(aObserver);
    }
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::SetClockStateObserver(PVMFMediaClockStateObserver& aObserver)
{
    iClockStateObserver = &aObserver;
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::RemoveClockStateObserver(PVMFMediaClockStateObserver& aObserver)
{
    OSCL_UNUSED_ARG(aObserver);
    iClockStateObserver = NULL;
}

OSCL_EXPORT_REF PVMFMediaClockCheckTimeWindowStatus PVMFMediaClockNotificationsInterfaceImpl::CheckTimeWindow(
    PVMFMediaClockCheckTimeWindowArgs &aArgsStruct)
{
    uint32 currTime;
    bool overflowFlag = false;
    PVMFMediaClockCheckTimeWindowStatus mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_ERROR;

    //Compare timestamp with latency adjusted current time
    GetLatencyAdjustedCurrentTime32(currTime, overflowFlag, aArgsStruct.aUnits);

    PVTimeComparisonUtils::MediaTimeStatus status;
    status = PVTimeComparisonUtils::CheckTimeWindow(aArgsStruct.aTimeStampToBeChecked,
             currTime, aArgsStruct.aWindowEarlyMargin,
             aArgsStruct.aWindowLateMargin, aArgsStruct.aDelta);

    switch (status)
    {
        case PVTimeComparisonUtils::MEDIA_EARLY_OUTSIDE_WINDOW:
        {
            PVMFStatus cbStatus = SetCallbackDeltaTime(aArgsStruct.aDelta, aArgsStruct.aCallbackToleranceWindow,
                                  aArgsStruct.aCallbackObserver, aArgsStruct.aThreadLock, aArgsStruct.aContextData,
                                  aArgsStruct.aCallBackID);

            if (PVMFSuccess == cbStatus)
            {
                mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_EARLY_OUTSIDE_WINDOW_CALLBACK_SET;
            }
            else
            {
                mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_ERROR;
            }
        }
        break;

        case PVTimeComparisonUtils::MEDIA_EARLY_WITHIN_WINDOW:
        {
            mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_EARLY_WITHIN_WINDOW;
        }
        break;

        case PVTimeComparisonUtils::MEDIA_ONTIME_WITHIN_WINDOW:
        {
            mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_ONTIME_WITHIN_WINDOW;
        }
        break;

        case PVTimeComparisonUtils::MEDIA_LATE_WITHIN_WINDOW:
        {
            mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_LATE_WITHIN_WINDOW;
        }
        break;

        case PVTimeComparisonUtils::MEDIA_LATE_OUTSIDE_WINDOW:
        {
            mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_LATE_OUTSIDE_WINDOW;
        }
        break;

        default:
        {
            mediaStatus = PVMF_MEDIA_CLOCK_MEDIA_ERROR;
        }
    }

    return mediaStatus;
}

OSCL_EXPORT_REF void PVMFMediaClockNotificationsInterfaceImpl::GetLatencyAdjustedCurrentTime32(
    uint32& aClockTime,
    bool& aOverflow,
    PVMFMediaClock_TimeUnits aUnits)
{
    if (iContainer)
    {
        iContainer->GetCurrentTime32(aClockTime, aOverflow, aUnits);
        aClockTime += iLatency;
    }
}

OSCL_EXPORT_REF PVMFTimebase_Tickcount::PVMFTimebase_Tickcount()
{
    iMicrosecPerTick = OsclTickCount::TickCountPeriod();
    iPrevTickcount = 0;
}

OSCL_EXPORT_REF PVMFTimebase_Tickcount::~PVMFTimebase_Tickcount()
{
}

OSCL_EXPORT_REF void PVMFTimebase_Tickcount::GetCurrentTick32(uint32& aTimebaseTickCount, bool& aOverflow)
{
    uint32 currenttickcount = OsclTickCount::TickCount();

    aOverflow = false;

    // Check to see if the tickcount wrapped around
    if (iPrevTickcount > currenttickcount)
    {
        aOverflow = true;
    }

    aTimebaseTickCount = currenttickcount;

    // Save the current tickcount for next comparison
    iPrevTickcount = currenttickcount;
}

OSCL_EXPORT_REF void PVMFTimebase_Tickcount::GetCurrentTime32(uint32& aTime, bool& aOverflow, PVMFMediaClock_TimeUnits aUnits)
{
    uint32 currenttickcount = OsclTickCount::TickCount();

    aOverflow = false;

    // Check to see if the tickcount wrapped around
    if (iPrevTickcount > currenttickcount)
    {
        aOverflow = true;
    }

    if (PVMF_MEDIA_CLOCK_USEC == aUnits)
    {
        uint64 time64 = (uint64)(currenttickcount * iMicrosecPerTick);
        //There is a chance that Tickcount did not wrap around but aTime value does
        if (time64 > (uint64)(0xFFFFFFFF))
        {
            aOverflow = true;
        }
        aTime = Oscl_Int64_Utils::get_uint64_lower32(time64);
    }
    else                                  /*convert time to millsecs*/
    {
        aTime = OsclTickCount::TicksToMsec(currenttickcount);
        uint32 divConst = 1;

        switch (aUnits)
        {
            case PVMF_MEDIA_CLOCK_SEC:
                divConst = 1000;
                break;

            case PVMF_MEDIA_CLOCK_MIN:
                divConst = 60000;
                break;

            case PVMF_MEDIA_CLOCK_HOUR:
                divConst = 3600000;
                break;

            case PVMF_MEDIA_CLOCK_DAY:
                divConst = 86400000;
                break;

            case PVMF_MEDIA_CLOCK_MSEC:
            default:
                break;
        }

        aTime = aTime / divConst;
    }

    // Save the current tickcount for next comparison
    iPrevTickcount = currenttickcount;
}
