📄 smlprstime.cpp
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
// system
#include <time.h>
#ifdef _UNIX
#include <ctype.h>
#endif
// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxxml.h"
#include "smiltype.h"
// pncont
#include "hxstring.h"
#include "hxslist.h"
// pnmisc
#include "nptime.h"
#include "smpte.h"
#include "hxwinver.h"
// rnxmllib
#include "hxxmlprs.h"
// rmasmil
#include "smlerror.h"
#include "smlparse.h"
#include "smlelem.h"
#include "smlprstime.h"
// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif
SmilTimeValue::SmilTimeValue(IUnknown* pContext, UINT32 ulStartLine,
CSmilElement* pElement)
: m_pContext(pContext)
, m_pElement(pElement)
, m_ulStartLine(ulStartLine)
, m_type(SmilTimeNone)
, m_position(SMILEventSourceEnd)
, m_uRepeatIteration(0)
, m_bTreatSyncArcAsEvent(FALSE)
, m_lOffset(0)
, m_lOriginalOffset(0)
, m_year(-1)
, m_month(-1)
, m_day(-1)
, m_hour(0)
, m_min(0)
, m_sec(0)
, m_ms(0)
, m_UTCOffsetMin(0)
, m_bRelativeToUTC(FALSE)
, m_pszMarkerName(NULL)
, m_bIsExternalMarker(FALSE)
, m_pszExternalMarkerFileName(NULL)
, m_pszExternalMarkerName(NULL)
, m_ulMarkerTime(0)
, m_bUsedToBeMediaMarker(FALSE)
, m_pEventName(NULL)
, m_bTimeIsResolved(FALSE)
, m_lResolvedToTime(SMILTIME_INFINITY)
, m_lWhenTimeWasResolved(SMILTIME_INFINITY)
, m_lTimeOfPause(SMILTIME_INFINITY)
{
m_pContext->AddRef();
}
SmilTimeValue::~SmilTimeValue()
{
HX_RELEASE(m_pContext); // /fixes mem leak.
HX_VECTOR_DELETE(m_pszMarkerName);
HX_VECTOR_DELETE(m_pszExternalMarkerFileName);
HX_VECTOR_DELETE(m_pszExternalMarkerName);
HX_VECTOR_DELETE(m_pEventName);
}
// /This returns the resolved time, including any positive offset,
// and includes only the part of a negative offset that is after
// the resolved-AT time (which is m_lWhenTimeWasResolved). In other
// words, this returns the time at which the element will actually
// begin or end based on this SmilTimeValue:
//
// Returns HXR_OK if resolved time is returned in lEffectiveResolveTime,
// else returns HXR_FAILED.
HX_RESULT
SmilTimeValue::getEffectiveResolvedTime(REF(INT32) lEffectiveResolvedTime)
{
HX_RESULT retval = HXR_OK;
lEffectiveResolvedTime = SMILTIME_NEGATIVE_INFINITY;
switch (m_type)
{
case SmilTimeSyncBase:
#if defined(ENABLE_SYNC_TO_PREV)
case SmilTimeSyncToPrev:
#endif
case SmilTimeMediaMarker:
case SmilTimeEvent:
{
if (isTimeResolved())
{
// /EH- the following should *always* be true:
HX_ASSERT(m_lResolvedToTime >= m_lWhenTimeWasResolved);
// /Note: resolvedToTime doesn't include offset:
lEffectiveResolvedTime = m_lResolvedToTime;
#define XXXEH_OFFSET_NOT_ALREADY_ACCOUNTED_FOR
#if defined(XXXEH_OFFSET_NOT_ALREADY_ACCOUNTED_FOR)
if (SmilTimeEvent == m_type
// /XXXEH- TODO: handle SmilTimeMediaMarker here, too,
// if and when we get internal markers (event-like
// ones) working:
|| (isSyncBaseTimeVal() && m_bTreatSyncArcAsEvent))
{
INT32 lEffectiveOffset = m_lOffset;
if (lEffectiveOffset < 0)
{
lEffectiveOffset = m_lWhenTimeWasResolved -
m_lResolvedToTime;
if (lEffectiveOffset < m_lOffset)
{
// /Resolved-at time was well before resolved-to
// time minus |offset|, so the entire offset is
// used:
lEffectiveOffset = m_lOffset;
}
}
lEffectiveResolvedTime += lEffectiveOffset;
}
#endif
}
else
{
retval = HXR_FAILED;
}
}
break;
// /XXXEH- TODO: figure out how to handle wallclock; I think
// it should just work the same as clock values:
case SmilTimeWallclock:
case SmilTimeClockValue:
case SmilTimeOffset:
{
if (isTimeResolved())
{
lEffectiveResolvedTime = m_lOffset;
}
else
{
retval = HXR_FAILED;
// /All wallclock, clock, and offset values should have
// been set as resolved at parse time:
HX_ASSERT(isTimeResolved());
}
}
break;
default:
{
retval = HXR_FAILED;
}
break;
}
return retval;
}
HX_RESULT
SmilTimeValue::parseValue(const char* pPos,
SMILSyncAttributeTag nTag,
// /Needed for self-references:
const char* pThisElementID)
{
HX_RESULT ret = HXR_OK;
if (pPos == (const char*)NULL)
{
ret = HXR_FAIL;
}
else if (*pPos == '+' || *pPos == '-')
{
HX_ASSERT(nTag == SMILSyncAttrBegin);
if (nTag == SMILSyncAttrBegin)
{
ret = parseOffset(pPos);
m_type = SmilTimeOffset;
if (SUCCEEDED(ret))
{
m_bTimeIsResolved = TRUE;
}
}
else
{
CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
errHandler.ReportError(SMILErrorBadTimeValue, pPos,
m_ulStartLine);
return ret;
}
}
else if (isdigit(*pPos)
// /Fixes PR 42592:
// "0.5s" was OK but ".5s" was being treated as unresolved:
|| '.' == *pPos)
{
ret = parseOffset(pPos);
if (SUCCEEDED(ret))
{
m_bTimeIsResolved = TRUE;
}
// this is actually a Clock Value if it is an end
// time.
if (nTag == SMILSyncAttrBegin)
{
m_type = SmilTimeOffset;
}
else // nTag == SMILSyncAttrEnd
{
m_type = SmilTimeClockValue;
}
}
#if defined(ENABLE_SYNC_TO_PREV)
else if (strncmp(pPos, "prev", 4) == 0)
{
// syncToPrev-value
ret = parseSyncToPrev(pPos);
}
#endif
else if (strncmp(pPos, "wallclock", 9) == 0)
{
// wallclock-sync-value
ret = parseWallClockValue(pPos);
}
else
{
const char* pDotPos = NULL;
BOOL bEscape = FALSE;
UINT32 len = strlen(pPos);
char* idref = new char [len+1];
char* idrefPos = idref;
*idrefPos = '\0';
char* command = new char [len+1];
char* commandPos = command;
*commandPos = '\0';
const char* pOffset = NULL;
enum { IDREF, ESCAPE, COMMAND, END, OFFSET } state = IDREF;
const char* pBegin = pPos;
while (*pPos)
{
switch (state)
{
case ESCAPE:
*idrefPos++ = *pPos;
state = IDREF;
break;
case IDREF:
if (*pPos == '\\')
{
// /Don't include escape char in idref; go to next char:
state = ESCAPE;
}
else if (*pPos == '.')
{
*idrefPos = '\0';
state = COMMAND;
}
else if (isspace(*pPos))
{
*idrefPos = '\0';
state = END;
}
else if (*pPos == '+' || *pPos == '-')
{
*idrefPos++ = '\0';
pOffset = pPos;
state = OFFSET;
}
else
{
*idrefPos++ = *pPos;
}
break;
case COMMAND:
if (isspace(*pPos))
{
*commandPos = '\0';
state = END;
}
else if (*pPos == '+' || *pPos == '-')
{
pOffset = pPos;
*commandPos = '\0';
state = OFFSET;
}
else
{
*commandPos++ = *pPos;
}
break;
case END:
if (*pPos == '+' || *pPos == '-')
{
pOffset = pPos;
state = OFFSET;
}
break;
case OFFSET:
break;
}
++pPos;
}
if (state == COMMAND)
{
*commandPos = '\0';
}
else if (state == IDREF || state == ESCAPE)
{
*idrefPos = '\0';
}
// XXXMEH - if this is an animation element, then we
// will handle the ".repeat(x)" syntax differently than
// we treat sources. We will treat ".repeat(x)" as an
// event rather than a sync-arc.
BOOL bIsAnimate = FALSE;
if (m_pElement && m_pElement->m_pNode &&
(m_pElement->m_pNode->m_tag == SMILAnimate ||
m_pElement->m_pNode->m_tag == SMILSet ||
m_pElement->m_pNode->m_tag == SMILAnimateMotion ||
m_pElement->m_pNode->m_tag == SMILAnimateColor))
{
bIsAnimate = TRUE;
}
if (*command == '\0')
{
// /The SMIL Boston draft supports the fact that the name of the
// event ("Event-symbol") is required and is what is used when
// no "Eventbase-element." precedes it. Thus, we need to
// treat the idref as the command string and use this
// element's id for the idref:
// /HOWEVER, for PR 69849 (needed for PR 58290) fix, if event is
// "accesskey(...)", then the event is not self-generated and is
// not raised for another element, so give it a fake id here so
// special-case code isn't needed in multiple places, elsewhere:
if (!strncmp(idref, "accesskey(", 10))
{
ret = parseEvent(
// /Use this string (an invalid XML id) as the fake ID:
ACCESSKEY_EVENT_FAKE_ID_STRING, // /fake ID.
idref, // /<==actually the command (i.e., Event-symbol)
pOffset);
}
else
{
ret = parseEvent(pThisElementID,
idref, // /<==actually the command (i.e., Event-symbol)
pOffset);
}
}
else if (strcmp(command, "begin") == 0 ||
#if defined(XXXEH_REPEAT_VALUE_TIMING_SHOULD_BE_EVENT_BASED)
#else
((0 == strncmp(command, "repeat(", 7)) && !bIsAnimate) ||
#endif
strcmp(command, "end") == 0)
{
// base, memeber, offset
ret = parseSyncBase(idref, command, pOffset);
}
else if (strncmp(command, "marker(", 7) == 0)
{
ret = parseMarker(idref, command, pOffset);
}
else // /everything else is an event (i.e., "Event-symbol") name:
{
ret = parseEvent(idref, command, pOffset);
}
HX_VECTOR_DELETE(command);
HX_VECTOR_DELETE(idref);
}
// /We need to make sure we set scheduled time values' resolved-to times
// in case these times are used AFTER the element has already begun
// playing or finished playing:
if (SmilTimeOffset == m_type ||
SmilTimeClockValue == m_type ||
SmilTimeWallclock == m_type)
{
setResolvedToTime(0);
setWhenTimeWasResolved(0);
}
return ret;
}
HX_RESULT
SmilTimeValue::setTimeOffset(time_t tRefTime)
{
HX_RESULT ret = HXR_OK;
if (m_type == SmilTimeWallclock)
{
struct tm ourtime;
ourtime.tm_sec = m_sec;
ourtime.tm_min = m_min;
ourtime.tm_hour = m_hour;
if (m_day != -1 && m_year != -1 && m_month != -1)
{
ourtime.tm_mday = m_day;
// /tm::tm_mon is 0-based, while m_month is 1-based, so we need
// to subtract 1 (and our parser already made sure m_month is
// greater than 0 and less than 13):
ourtime.tm_mon = m_month - 1;
ourtime.tm_year = m_year - 1900;
}
else
{
time_t thetime;
time(&thetime);
struct tm* cur;
cur = localtime(&thetime);
ourtime.tm_mday = cur->tm_mday;
ourtime.tm_mon = cur->tm_mon;
ourtime.tm_year = cur->tm_year;
ourtime.tm_isdst = cur->tm_isdst;
}
time_t thisTime = mktime(&ourtime);
double diff = 0.0;
if (m_bRelativeToUTC)
{
//XXXJHUG need a crossplatform way to get the timezone offset.
#ifdef _WIN32
diff = difftime(thisTime + _timezone, tRefTime + m_UTCOffsetMin);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -