[WTF] MediaTime should support round-tripping from and to doubles.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Dec 2014 20:51:02 +0000 (20:51 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Dec 2014 20:51:02 +0000 (20:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=139248

Reviewed by Eric Carlson.

Source/WebCore:

Check whether the MediaTime's underlying data is floating point before converting
to a CMTime or QTTime.

* platform/graphics/avfoundation/MediaTimeAVFoundation.cpp:
(WebCore::toCMTime):
* platform/graphics/mac/MediaTimeQTKit.mm:
(WebCore::toQTTime):

Source/WTF:

MediaTimes should be able to return precisely the same double value as was used
when the MediaTime was created, so long as that MediaTime was not modified in a
non-destructive way. This will allow API which accepts floating-point values to
return the exact same value when asked, but still be able to store that value
as a MediaTime.

* wtf/MediaTime.cpp:
(WTF::MediaTime::createWithFloat): Added; store the floating-point value in a union.
(WTF::MediaTime::createWithDouble): Ditto.
(WTF::MediaTime::toFloat): If the value is a double, just return it.
(WTF::MediaTime::toDouble): Ditto.
(WTF::MediaTime::operator+): Special case when one or both sides are doubles.
(WTF::MediaTime::operator-): Ditto.
(WTF::MediaTime::operator*): Ditto.
(WTF::MediaTime::compare): Ditto.
(WTF::abs): Ditto.
(WTF::MediaTime::setTimeScale): Convert the MediaTime from a double.

Tools:

Add API tests for new features of MediaTime. Update the LLDB python provider to correctly display
the MediaTimes after this change.

* TestWebKitAPI/Tests/WTF/MediaTime.cpp:
(WTF::operator<<):
(TestWebKitAPI::TEST):
* lldb/lldb_webkit.py:
(WTFMediaTime_SummaryProvider):
(WTFMediaTimeProvider.timeValueAsDouble):
(WTFMediaTimeProvider.isIndefinite):
(WTFMediaTimeProvider):
(WTFMediaTimeProvider.hasDoubleValue):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@176863 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WTF/ChangeLog
Source/WTF/wtf/MediaTime.cpp
Source/WTF/wtf/MediaTime.h
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/avfoundation/MediaTimeAVFoundation.cpp
Source/WebCore/platform/graphics/mac/MediaTimeQTKit.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp
Tools/lldb/lldb_webkit.py

index ca33912..abe77e8 100644 (file)
@@ -1,3 +1,28 @@
+2014-12-05  Jer Noble  <jer.noble@apple.com>
+
+        [WTF] MediaTime should support round-tripping from and to doubles.
+        https://bugs.webkit.org/show_bug.cgi?id=139248
+
+        Reviewed by Eric Carlson.
+
+        MediaTimes should be able to return precisely the same double value as was used
+        when the MediaTime was created, so long as that MediaTime was not modified in a
+        non-destructive way. This will allow API which accepts floating-point values to
+        return the exact same value when asked, but still be able to store that value
+        as a MediaTime.
+
+        * wtf/MediaTime.cpp:
+        (WTF::MediaTime::createWithFloat): Added; store the floating-point value in a union.
+        (WTF::MediaTime::createWithDouble): Ditto.
+        (WTF::MediaTime::toFloat): If the value is a double, just return it.
+        (WTF::MediaTime::toDouble): Ditto.
+        (WTF::MediaTime::operator+): Special case when one or both sides are doubles.
+        (WTF::MediaTime::operator-): Ditto.
+        (WTF::MediaTime::operator*): Ditto.
+        (WTF::MediaTime::compare): Ditto.
+        (WTF::abs): Ditto.
+        (WTF::MediaTime::setTimeScale): Convert the MediaTime from a double.
+
 2014-12-05  peavo@outlook.com  <peavo@outlook.com>
 
         [WinCairo] WTF project is missing a GStreamer source file.
index dce3ba6..0582c95 100644 (file)
@@ -83,6 +83,22 @@ MediaTime::MediaTime(const MediaTime& rhs)
     *this = rhs;
 }
 
+MediaTime MediaTime::createWithFloat(float floatTime)
+{
+    if (floatTime != floatTime)
+        return invalidTime();
+    if (std::isinf(floatTime))
+        return std::signbit(floatTime) ? negativeInfiniteTime() : positiveInfiniteTime();
+    if (floatTime > std::numeric_limits<int64_t>::max())
+        return positiveInfiniteTime();
+    if (floatTime < std::numeric_limits<int64_t>::min())
+        return negativeInfiniteTime();
+
+    MediaTime value(0, DefaultTimeScale, Valid | DoubleValue);
+    value.m_timeValueAsDouble = floatTime;
+    return value;
+}
+
 MediaTime MediaTime::createWithFloat(float floatTime, int32_t timeScale)
 {
     if (floatTime != floatTime)
@@ -99,6 +115,22 @@ MediaTime MediaTime::createWithFloat(float floatTime, int32_t timeScale)
     return MediaTime(static_cast<int64_t>(floatTime * timeScale), timeScale, Valid);
 }
 
+MediaTime MediaTime::createWithDouble(double doubleTime)
+{
+    if (doubleTime != doubleTime)
+        return invalidTime();
+    if (std::isinf(doubleTime))
+        return std::signbit(doubleTime) ? negativeInfiniteTime() : positiveInfiniteTime();
+    if (doubleTime > std::numeric_limits<int64_t>::max())
+        return positiveInfiniteTime();
+    if (doubleTime < std::numeric_limits<int64_t>::min())
+        return negativeInfiniteTime();
+
+    MediaTime value(0, DefaultTimeScale, Valid | DoubleValue);
+    value.m_timeValueAsDouble = doubleTime;
+    return value;
+}
+
 MediaTime MediaTime::createWithDouble(double doubleTime, int32_t timeScale)
 {
     if (doubleTime != doubleTime)
@@ -123,6 +155,8 @@ float MediaTime::toFloat() const
         return std::numeric_limits<float>::infinity();
     if (isNegativeInfinite())
         return -std::numeric_limits<float>::infinity();
+    if (hasDoubleValue())
+        return m_timeValueAsDouble;
     return static_cast<float>(m_timeValue) / m_timeScale;
 }
 
@@ -134,6 +168,8 @@ double MediaTime::toDouble() const
         return std::numeric_limits<double>::infinity();
     if (isNegativeInfinite())
         return -std::numeric_limits<double>::infinity();
+    if (hasDoubleValue())
+        return m_timeValueAsDouble;
     return static_cast<double>(m_timeValue) / m_timeScale;
 }
 
@@ -165,11 +201,20 @@ MediaTime MediaTime::operator+(const MediaTime& rhs) const
     if (isNegativeInfinite() || rhs.isNegativeInfinite())
         return negativeInfiniteTime();
 
-    int32_t commonTimeScale;
-    if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
-        commonTimeScale = MaximumTimeScale;
+    if (hasDoubleValue() && rhs.hasDoubleValue())
+        return MediaTime::createWithDouble(m_timeValueAsDouble + rhs.m_timeValueAsDouble);
+
     MediaTime a = *this;
     MediaTime b = rhs;
+
+    if (a.hasDoubleValue())
+        a.setTimeScale(DefaultTimeScale);
+    else if (b.hasDoubleValue())
+        b.setTimeScale(DefaultTimeScale);
+
+    int32_t commonTimeScale;
+    if (!leastCommonMultiple(a.m_timeScale, b.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
+        commonTimeScale = MaximumTimeScale;
     a.setTimeScale(commonTimeScale);
     b.setTimeScale(commonTimeScale);
     while (!safeAdd(a.m_timeValue, b.m_timeValue, a.m_timeValue)) {
@@ -202,11 +247,20 @@ MediaTime MediaTime::operator-(const MediaTime& rhs) const
     if (isNegativeInfinite() || rhs.isPositiveInfinite())
         return negativeInfiniteTime();
 
+    if (hasDoubleValue() && rhs.hasDoubleValue())
+        return MediaTime::createWithDouble(m_timeValueAsDouble - rhs.m_timeValueAsDouble);
+
+    MediaTime a = *this;
+    MediaTime b = rhs;
+
+    if (a.hasDoubleValue())
+        a.setTimeScale(DefaultTimeScale);
+    else if (b.hasDoubleValue())
+        b.setTimeScale(DefaultTimeScale);
+
     int32_t commonTimeScale;
     if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
         commonTimeScale = MaximumTimeScale;
-    MediaTime a = *this;
-    MediaTime b = rhs;
     a.setTimeScale(commonTimeScale);
     b.setTimeScale(commonTimeScale);
     while (!safeSub(a.m_timeValue, b.m_timeValue, a.m_timeValue)) {
@@ -234,7 +288,10 @@ MediaTime MediaTime::operator-() const
         return positiveInfiniteTime();
 
     MediaTime negativeTime = *this;
-    negativeTime.m_timeValue = -negativeTime.m_timeValue;
+    if (negativeTime.hasDoubleValue())
+        negativeTime.m_timeValueAsDouble = -negativeTime.m_timeValueAsDouble;
+    else
+        negativeTime.m_timeValue = -negativeTime.m_timeValue;
     return negativeTime;
 }
 
@@ -263,6 +320,11 @@ MediaTime MediaTime::operator*(int32_t rhs) const
 
     MediaTime a = *this;
 
+    if (a.hasDoubleValue()) {
+        a.m_timeValueAsDouble *= rhs;
+        return a;
+    }
+
     while (!safeMultiply(a.m_timeValue, rhs, a.m_timeValue)) {
         if (a.m_timeScale == 1)
             return signum(a.m_timeValue) == signum(rhs) ? positiveInfiniteTime() : negativeInfiniteTime();
@@ -338,17 +400,33 @@ MediaTime::ComparisonFlags MediaTime::compare(const MediaTime& rhs) const
     if (rhs.isIndefinite())
         return LessThan;
 
-    int64_t rhsWhole = rhs.m_timeValue / rhs.m_timeScale;
-    int64_t lhsWhole = m_timeValue / m_timeScale;
+    if (hasDoubleValue() && rhs.hasDoubleValue()) {
+        if (m_timeValueAsDouble == rhs.m_timeValueAsDouble)
+            return EqualTo;
+
+        return m_timeValueAsDouble < rhs.m_timeValueAsDouble ? LessThan : GreaterThan;
+    }
+
+    MediaTime a = *this;
+    MediaTime b = rhs;
+
+    if (a.hasDoubleValue())
+        a.setTimeScale(DefaultTimeScale);
+
+    if (b.hasDoubleValue())
+        b.setTimeScale(DefaultTimeScale);
+
+    int64_t rhsWhole = b.m_timeValue / b.m_timeScale;
+    int64_t lhsWhole = a.m_timeValue / a.m_timeScale;
     if (lhsWhole > rhsWhole)
         return GreaterThan;
     if (lhsWhole < rhsWhole)
         return LessThan;
 
-    int64_t rhsRemain = rhs.m_timeValue % rhs.m_timeScale;
-    int64_t lhsRemain = m_timeValue % m_timeScale;
-    int64_t lhsFactor = lhsRemain * rhs.m_timeScale;
-    int64_t rhsFactor = rhsRemain * m_timeScale;
+    int64_t rhsRemain = b.m_timeValue % b.m_timeScale;
+    int64_t lhsRemain = a.m_timeValue % a.m_timeScale;
+    int64_t lhsFactor = lhsRemain * b.m_timeScale;
+    int64_t rhsFactor = rhsRemain * a.m_timeScale;
 
     if (lhsFactor == rhsFactor)
         return EqualTo;
@@ -387,6 +465,11 @@ const MediaTime& MediaTime::indefiniteTime()
 
 void MediaTime::setTimeScale(int32_t timeScale)
 {
+    if (hasDoubleValue()) {
+        *this = MediaTime::createWithDouble(m_timeValueAsDouble, timeScale);
+        return;
+    }
+
     if (timeScale == m_timeScale)
         return;
     timeScale = std::min(MaximumTimeScale, timeScale);
@@ -415,6 +498,9 @@ MediaTime abs(const MediaTime& rhs)
         return MediaTime::invalidTime();
     if (rhs.isNegativeInfinite() || rhs.isPositiveInfinite())
         return MediaTime::positiveInfiniteTime();
+    if (rhs.hasDoubleValue())
+        return MediaTime::createWithDouble(fabs(rhs.m_timeValueAsDouble));
+
     MediaTime val = rhs;
     val.m_timeValue *= signum(rhs.m_timeScale) * signum(rhs.m_timeValue);
     return val;
index d901c7e..a3feea8 100644 (file)
@@ -49,6 +49,7 @@ public:
         PositiveInfinite = 1 << 2,
         NegativeInfinite = 1 << 3,
         Indefinite = 1 << 4,
+        DoubleValue = 1 << 5,
     };
 
     MediaTime();
@@ -56,8 +57,10 @@ public:
     MediaTime(const MediaTime& rhs);
     ~MediaTime();
 
-    static MediaTime createWithFloat(float floatTime, int32_t timeScale = DefaultTimeScale);
-    static MediaTime createWithDouble(double doubleTime, int32_t timeScale = DefaultTimeScale);
+    static MediaTime createWithFloat(float floatTime);
+    static MediaTime createWithFloat(float floatTime, int32_t timeScale);
+    static MediaTime createWithDouble(double doubleTime);
+    static MediaTime createWithDouble(double doubleTime, int32_t timeScale);
 
     float toFloat() const;
     double toDouble() const;
@@ -92,6 +95,7 @@ public:
     bool isPositiveInfinite() const { return m_timeFlags & PositiveInfinite; }
     bool isNegativeInfinite() const { return m_timeFlags & NegativeInfinite; }
     bool isIndefinite() const { return m_timeFlags & Indefinite; }
+    bool hasDoubleValue() const { return m_timeFlags & DoubleValue; }
 
     static const MediaTime& zeroTime();
     static const MediaTime& invalidTime();
@@ -111,13 +115,17 @@ public:
     MediaTime(int) = delete;
 
     friend WTF_EXPORT_PRIVATE MediaTime abs(const MediaTime& rhs);
-private:
+
     static const int32_t DefaultTimeScale = 10000000;
     static const int32_t MaximumTimeScale;
 
+private:
     void setTimeScale(int32_t);
 
-    int64_t m_timeValue;
+    union {
+        int64_t m_timeValue;
+        double m_timeValueAsDouble;
+    };
     int32_t m_timeScale;
     uint32_t m_timeFlags;
 };
index fc49284..0c90e58 100644 (file)
@@ -1,3 +1,18 @@
+2014-12-05  Jer Noble  <jer.noble@apple.com>
+
+        [WTF] MediaTime should support round-tripping from and to doubles.
+        https://bugs.webkit.org/show_bug.cgi?id=139248
+
+        Reviewed by Eric Carlson.
+
+        Check whether the MediaTime's underlying data is floating point before converting
+        to a CMTime or QTTime.
+
+        * platform/graphics/avfoundation/MediaTimeAVFoundation.cpp:
+        (WebCore::toCMTime):
+        * platform/graphics/mac/MediaTimeQTKit.mm:
+        (WebCore::toQTTime):
+
 2014-12-05  Anders Carlsson  <andersca@apple.com>
 
         Add a private browsing mode to MiniBrowser
index e619ab8..1c972cd 100644 (file)
 
 #if USE(AVFOUNDATION)
 
+#if PLATFORM(WIN)
+#include <CoreMedia/CoreMedia.h>
+#include "CoreMediaSoftLinking.h"
+#else
+#include "SoftLinking.h"
+SOFT_LINK_FRAMEWORK(CoreMedia)
+SOFT_LINK(CoreMedia, CMTimeMakeWithSeconds, CMTime, (Float64 seconds, int32_t preferredTimeScale), (seconds, preferredTimeScale))
+SOFT_LINK(CoreMedia, CMTimeMake, CMTime, (int64_t value, int32_t timescale), (value, timescale))
+#endif
+
 namespace WebCore {
 
 static bool CMTimeHasFlags(const CMTime& cmTime, uint32_t flags)
@@ -54,7 +64,12 @@ MediaTime toMediaTime(const CMTime& cmTime)
 
 CMTime toCMTime(const MediaTime& mediaTime)
 {
-    CMTime time = {mediaTime.timeValue(), mediaTime.timeScale(), 0, 0};
+    CMTime time;
+
+    if (mediaTime.hasDoubleValue())
+        time = CMTimeMakeWithSeconds(mediaTime.toDouble(), mediaTime.timeScale());
+    else
+        time = CMTimeMake(mediaTime.timeValue(), mediaTime.timeScale());
 
     if (mediaTime.isValid())
         time.flags |= kCMTimeFlags_Valid;
index 9415aa4..79e272b 100644 (file)
 #import "SoftLinking.h"
 #import <QTKit/QTTime.h>
 
-SOFT_LINK_FRAMEWORK(QTKit);
-SOFT_LINK_CONSTANT(QTKit, QTIndefiniteTime, QTTime);
-SOFT_LINK_CONSTANT(QTKit, QTZeroTime, QTTime);
-SOFT_LINK(QTKit, QTTimeCompare, NSComparisonResult, (QTTime time, QTTime otherTime), (time, otherTime));
-SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale));
+SOFT_LINK_FRAMEWORK(QTKit)
+SOFT_LINK_CONSTANT(QTKit, QTIndefiniteTime, QTTime)
+SOFT_LINK_CONSTANT(QTKit, QTZeroTime, QTTime)
+SOFT_LINK(QTKit, QTTimeCompare, NSComparisonResult, (QTTime time, QTTime otherTime), (time, otherTime))
+SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale))
+SOFT_LINK(QTKit, QTMakeTimeWithTimeInterval, QTTime, (NSTimeInterval timeInterval), (timeInterval))
 
 namespace WebCore {
 
@@ -55,6 +56,8 @@ QTTime toQTTime(const MediaTime& mediaTime)
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    if (mediaTime.hasDoubleValue())
+        return QTMakeTimeWithTimeInterval(mediaTime.toDouble());
     return QTMakeTime(mediaTime.timeValue(), mediaTime.timeScale());
 #pragma clang diagnostic pop
 }
index 8b8da02..d93b832 100644 (file)
@@ -1,3 +1,23 @@
+2014-12-05  Jer Noble  <jer.noble@apple.com>
+
+        [WTF] MediaTime should support round-tripping from and to doubles.
+        https://bugs.webkit.org/show_bug.cgi?id=139248
+
+        Reviewed by Eric Carlson.
+
+        Add API tests for new features of MediaTime. Update the LLDB python provider to correctly display
+        the MediaTimes after this change.
+
+        * TestWebKitAPI/Tests/WTF/MediaTime.cpp:
+        (WTF::operator<<):
+        (TestWebKitAPI::TEST):
+        * lldb/lldb_webkit.py:
+        (WTFMediaTime_SummaryProvider):
+        (WTFMediaTimeProvider.timeValueAsDouble):
+        (WTFMediaTimeProvider.isIndefinite):
+        (WTFMediaTimeProvider):
+        (WTFMediaTimeProvider.hasDoubleValue):
+
 2014-12-05  Anders Carlsson  <andersca@apple.com>
 
         Add a private browsing mode to MiniBrowser
index cc17d02..248fa33 100644 (file)
@@ -44,6 +44,8 @@ std::ostream& operator<<(std::ostream& out, const MediaTime& val)
         out << "+infinite";
     else if (val.isNegativeInfinite())
         out << "-infinite";
+    else if (val.hasDoubleValue())
+        out << "double: " << val.toDouble();
     else
         out << "value: " << val.timeValue() << ", scale: " << val.timeScale();
     return out << " }";
@@ -177,6 +179,20 @@ TEST(WTF, MediaTime)
     EXPECT_EQ(MediaTime::createWithDouble(-INFINITY), MediaTime::negativeInfiniteTime());
     EXPECT_EQ(MediaTime::createWithDouble(NAN), MediaTime::invalidTime());
 
+    // Floating Point Round Trip
+    EXPECT_EQ(10.0123456789f, MediaTime::createWithFloat(10.0123456789f).toFloat());
+    EXPECT_EQ(10.0123456789, MediaTime::createWithDouble(10.0123456789).toDouble());
+
+    // Floating Point Math
+    EXPECT_EQ(1.5 + 3.3, (MediaTime::createWithDouble(1.5) + MediaTime::createWithDouble(3.3)).toDouble());
+    EXPECT_EQ(1.5 - 3.3, (MediaTime::createWithDouble(1.5) - MediaTime::createWithDouble(3.3)).toDouble());
+    EXPECT_EQ(-3.3, (-MediaTime::createWithDouble(3.3)).toDouble());
+    EXPECT_EQ(3.3 * 2, (MediaTime::createWithDouble(3.3) * 2).toDouble());
+
+    // Floating Point and non-Floating Point math
+    EXPECT_EQ(2.0, (MediaTime::createWithDouble(1.5) + MediaTime(1, 2)).toDouble());
+    EXPECT_EQ(1.0, (MediaTime::createWithDouble(1.5) - MediaTime(1, 2)).toDouble());
+
     // Overflow Behavior
     EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 64.0f)), MediaTime::positiveInfiniteTime());
     EXPECT_EQ(MediaTime::createWithFloat(-pow(2.0f, 64.0f)), MediaTime::negativeInfiniteTime());
index f8a75b1..b99557d 100644 (file)
@@ -83,6 +83,8 @@ def WTFMediaTime_SummaryProvider(valobj, dict):
         return "{ -Infinity }"
     if provider.isIndefinite():
         return "{ Indefinite }"
+    if provider.hasDoubleValue():
+        return "{ %f }" % (provider.timeValueAsDouble())
     return "{ %d/%d, %f }" % (provider.timeValue(), provider.timeScale(), float(provider.timeValue()) / provider.timeScale())
 
 
@@ -407,6 +409,10 @@ class WTFMediaTimeProvider:
     def timeValue(self):
         return self.valobj.GetChildMemberWithName('m_timeValue').GetValueAsSigned(0)
 
+    def timeValueAsDouble(self):
+        error = lldb.SBError()
+        return self.valobj.GetChildMemberWithName('m_timeValueAsDouble').GetData().GetDouble(error, 0)
+
     def timeScale(self):
         return self.valobj.GetChildMemberWithName('m_timeScale').GetValueAsSigned(0)
 
@@ -421,3 +427,6 @@ class WTFMediaTimeProvider:
 
     def isIndefinite(self):
         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 4)
+
+    def hasDoubleValue(self):
+        return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)