blob: edf71753cf6d05efc5e962bb96c8e7bbcf5243e8 [file] [log] [blame] [edit]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 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;
}
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.erase(&iMediaClockSetCallbackObjects[ii]);
}
}
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)
{
iMediaClockSetCallbackObjects.push_back(ifaceImpl);
AdjustLatenciesOfSinks();
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]);
}
}
}
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::AdjustLatenciesOfSinks()
{
uint32 size = iMediaClockSetCallbackObjects.size();
uint32 ii = 0;
if (!size)
return;
//find the minimum and maximum latencies
uint32 minLatency = iMediaClockSetCallbackObjects[0]->iLatency;
uint32 maxLatency = iMediaClockSetCallbackObjects[0]->iLatency;
for (ii = 0; ii < size - 1; ii++)
{
if (iMediaClockSetCallbackObjects[ii+1]->iLatency > iMediaClockSetCallbackObjects[ii]->iLatency)
{
minLatency = iMediaClockSetCallbackObjects[ii]->iLatency;
}
else
{
minLatency = iMediaClockSetCallbackObjects[ii+1]->iLatency;
}
if (iMediaClockSetCallbackObjects[ii+1]->iLatency > iMediaClockSetCallbackObjects[ii]->iLatency)
{
maxLatency = iMediaClockSetCallbackObjects[ii+1]->iLatency;
}
else
{
maxLatency = iMediaClockSetCallbackObjects[ii]->iLatency;
}
}
//set adjusted-latencies and latency-delays
for (ii = 0; ii < size; ii++)
{
iMediaClockSetCallbackObjects[ii]->iAdjustedLatency =
iMediaClockSetCallbackObjects[ii]->iLatency - minLatency;
iMediaClockSetCallbackObjects[ii]->iLatencyDelayForClockStartNotification =
maxLatency - iMediaClockSetCallbackObjects[ii]->iLatency;
}
}
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)
{
// 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;
//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 this is clock start, we need to send start notification after adjusting latency
if (RUNNING == iState)
{
for (uint32 ii = 0; ii < iMediaClockSetCallbackObjects.size(); ii++)
{
if (iMediaClockSetCallbackObjects[ii]->iClockStateObserver != NULL)
{
if (0 == iMediaClockSetCallbackObjects[ii]->iLatencyDelayForClockStartNotification)
{
(iMediaClockSetCallbackObjects[ii]->iClockStateObserver)->ClockStateUpdated();
}
else
{
//Queue notification
QueueClockStartNotificationEvent(iMediaClockSetCallbackObjects[ii]->iLatencyDelayForClockStartNotification,
iMediaClockSetCallbackObjects[ii]->iClockStateObserver);
}
}
}
}
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 (aAdjTime > aObsTime)
{
// 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 (aAdjTime < aObsTime)
{
// 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);
}
}
}
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);
}
PVMFStatus PVMFMediaClock::GetNPTClockPosition(
/*OUT*/ uint32& aCurrentPosition)
{
uint32 currentTime = 0;
bool overflow = false;
GetCurrentTime32(currentTime, overflow, PVMF_MEDIA_CLOCK_MSEC);
if (overflow)
{
return PVMFErrOverflow;
}
if (iIsNPTPlayBackDirectionBackwards)
{
aCurrentPosition = iStartNPT - (currentTime - iStartMediaClockTS);
}
else
{
aCurrentPosition = iStartNPT + (currentTime - iStartMediaClockTS);
}
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;
iAdjustedLatency = 0;
iClockStateObserver = NULL;
iLatencyDelayForClockStartNotification = 0;
}
OSCL_EXPORT_REF PVMFMediaClockNotificationsInterfaceImpl::~PVMFMediaClockNotificationsInterfaceImpl()
{
//check if vectors need to be destroyed
}
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 - iAdjustedLatency, 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 - iAdjustedLatency, 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 - iAdjustedLatency, 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 - iAdjustedLatency, 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 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;
}