Title: [211746] trunk
Revision
211746
Author
jer.no...@apple.com
Date
2017-02-06 13:49:24 -0800 (Mon, 06 Feb 2017)

Log Message

Playback stalls when a SourceBuffer append causes frame eviction
https://bugs.webkit.org/show_bug.cgi?id=167834

Reviewed by Eric Carlson.

PerformanceTests:

Add an in-page performance test measuring the amount of time required
to append a large amount of media data to a SourceBuffer, and then to
completely remove that data 30s at a time.

Add a microbenchmark for MediaTime which measures the amount of time
required to create a 1M entry std::map and traverse the map 1M times.

* Media/MSERemoveCodedFrames.html: Added.
* Media/media-source-loader.js:
(MediaSourceLoader.prototype.get duration):
* MediaTime/Configurations/Base.xcconfig: Added.
* MediaTime/Configurations/DebugRelease.xcconfig: Added.
* MediaTime/Makefile: Added.
* MediaTime/MediaTime.xcodeproj/project.pbxproj: Added.
* MediaTime/main.cpp: Added.
(performTest):
(test):
(main):
* Skipped:

Source/WebCore:

Test: PerformanceTests/Media/MSERemoveCodedFrames.html

Optimize searching through SampleMap by presentationTime.

Many of the methods exposed by PresentationOrderSampleMap used the bare  std::equal_range,
lower_bound, or upper_bound methods. Unlike those methods exposed on std::map, the bare
search methods perform a linear O(n) search, rather than a the binary O(log(n)) search used
by std::map. Rewrite those methods using the bare methods in terms of the std::map search
methods.

Drive-by fix: rename findSampleOnOrAfterPresentationTime to
findSampleStartingOnOrAfterPresentationTime to make the behavior of the method more
explicit.

* Modules/mediasource/SampleMap.cpp:
(WebCore::PresentationOrderSampleMap::findSampleContainingPresentationTime):
(WebCore::PresentationOrderSampleMap::findSampleStartingOnOrAfterPresentationTime):
(WebCore::PresentationOrderSampleMap::reverseFindSampleBeforePresentationTime):
(WebCore::DecodeOrderSampleMap::findSyncSampleAfterPresentationTime):
(WebCore::PresentationOrderSampleMap::findSamplesBetweenPresentationTimes):
(WebCore::PresentationOrderSampleMap::findSamplesWithinPresentationRange):
(WebCore::PresentationOrderSampleMap::findSampleOnOrAfterPresentationTime): Deleted.
* Modules/mediasource/SampleMap.h:
(WebCore::PresentationOrderSampleMap::begin):
(WebCore::PresentationOrderSampleMap::end):
(WebCore::PresentationOrderSampleMap::rbegin):
(WebCore::PresentationOrderSampleMap::rend):
(WebCore::DecodeOrderSampleMap::begin):
(WebCore::DecodeOrderSampleMap::end):
(WebCore::DecodeOrderSampleMap::rbegin):
(WebCore::DecodeOrderSampleMap::rend):
(WebCore::SampleMap::SampleMap):
(WebCore::SampleMap::sizeInBytes):
(WebCore::SampleMap::decodeOrder):
(WebCore::SampleMap::presentationOrder):
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::removeSamplesFromTrackBuffer):
(WebCore::SourceBuffer::removeCodedFrames):
(WebCore::SourceBuffer::reenqueueMediaForTime):
* WebCore.xcodeproj/project.pbxproj:

Source/WTF:

Optimize the MediaTime class; specifically the compare() method. The class only
needs 6 bits to store the TimeFlags, so make that a uint8_t rather than uint32_t.
The implementation is slightly simpler if the TimeScale is unsigned, so make that
a uint32_t rather than int32_t. Inline the comparison operators. Optimize the equality
comparison by bitwise-and'ing the flags together and masking the result. Optimize for
common comparison scenarios (equal timeScales, equal timeValues(), etc.). Attempt the
mathematically simpler simpler method for comparing ratios, and only fall back to the
complex method if the results of multiplying the timeScale by the timeValue overflows.

* wtf/MediaTime.cpp:
(WTF::greatestCommonDivisor):
(WTF::leastCommonMultiple):
(WTF::signum):
(WTF::MediaTime::MediaTime):
(WTF::MediaTime::createWithFloat):
(WTF::MediaTime::createWithDouble):
(WTF::MediaTime::operator+):
(WTF::MediaTime::operator-):
(WTF::MediaTime::operator!):
(WTF::MediaTime::operator bool):
(WTF::MediaTime::compare):
(WTF::MediaTime::setTimeScale):
(WTF::abs):
(WTF::MediaTime::operator<): Deleted.
(WTF::MediaTime::operator>): Deleted.
(WTF::MediaTime::operator!=): Deleted.
(WTF::MediaTime::operator==): Deleted.
(WTF::MediaTime::operator>=): Deleted.
(WTF::MediaTime::operator<=): Deleted.
* wtf/MediaTime.h:

Tools:

Add new correctness tests for the Webcore::SampleMap class. Add additional subtests
for the WTF::MediaTime class.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/MediaTime.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/SampleMap.cpp: Added.
(WTF::operator<<):
(TestWebKitAPI::TestSample::create):
(TestWebKitAPI::TestSample::TestSample):
(TestWebKitAPI::TEST_F):

Modified Paths

Added Paths

Diff

Modified: trunk/PerformanceTests/ChangeLog (211745 => 211746)


--- trunk/PerformanceTests/ChangeLog	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/PerformanceTests/ChangeLog	2017-02-06 21:49:24 UTC (rev 211746)
@@ -1,3 +1,30 @@
+2017-02-06  Jer Noble  <jer.no...@apple.com>
+
+        Playback stalls when a SourceBuffer append causes frame eviction
+        https://bugs.webkit.org/show_bug.cgi?id=167834
+
+        Reviewed by Eric Carlson.
+
+        Add an in-page performance test measuring the amount of time required
+        to append a large amount of media data to a SourceBuffer, and then to
+        completely remove that data 30s at a time.
+
+        Add a microbenchmark for MediaTime which measures the amount of time
+        required to create a 1M entry std::map and traverse the map 1M times.
+
+        * Media/MSERemoveCodedFrames.html: Added.
+        * Media/media-source-loader.js:
+        (MediaSourceLoader.prototype.get duration):
+        * MediaTime/Configurations/Base.xcconfig: Added.
+        * MediaTime/Configurations/DebugRelease.xcconfig: Added.
+        * MediaTime/Makefile: Added.
+        * MediaTime/MediaTime.xcodeproj/project.pbxproj: Added.
+        * MediaTime/main.cpp: Added.
+        (performTest):
+        (test):
+        (main):
+        * Skipped:
+
 2017-02-06  Saam Barati  <sbar...@apple.com>
 
         Make ARES-6 work from the CLI again

Added: trunk/PerformanceTests/Media/MSERemoveCodedFrames.html (0 => 211746)


--- trunk/PerformanceTests/Media/MSERemoveCodedFrames.html	                        (rev 0)
+++ trunk/PerformanceTests/Media/MSERemoveCodedFrames.html	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<script>
+var loader;
+var video;
+var longMediaSegment;
+
+function concatArrayBuffers() {
+    var byteLength = 0;
+    Array.prototype.forEach.call(arguments, arrayBuffer => {
+        if (!arrayBuffer.byteLength)
+            throw "Not an ArrayBuffer!";
+        byteLength += arrayBuffer.byteLength;
+    });
+
+
+    var view = new Uint8Array(byteLength);
+    var offset = 0;
+    Array.prototype.forEach.call(arguments, arrayBuffer => {
+        view.set(new Uint8Array(arrayBuffer), offset);
+        offset += arrayBuffer.byteLength;
+    });
+    return view.buffer;
+}
+
+function concatMediaData() {
+    return new Promise((resolve, reject) => {
+        var segments = new Array(100);
+        segments.fill(loader.everyMediaSegment);
+        longMediaSegment = concatArrayBuffers.apply(this, segments);
+        resolve(longMediaSegment);
+    });
+}
+
+window.addEventListener('load', () => {
+    PerfTestRunner.prepareToMeasureValuesAsync({
+        unit: 'ms',
+        done: function () {
+            if (video) {
+                video.src = ""
+                video.load();
+            }
+        }
+    });
+
+    loader = new MediaSourceLoader('test-fragmented-video.json');
+    loader.loadMediaData().then(concatMediaData).then(runTest);
+});
+
+function remove30SecondsAtATimeTillEmpty(sourceBuffer) {
+    return new Promise(resolve => {
+        var removeNext30Seconds = () => {
+            var start = sourceBuffer.buffered.start(0);
+            sourceBuffer.remove(start, start + 30)
+        }
+        sourceBuffer._onupdate_ = () => {
+            if (sourceBuffer.buffered.length == 0 || sourceBuffer.buffered.start(0) - sourceBuffer.buffered.end(0) == 0) {
+                sourceBuffer._onupdate_ = null;
+                resolve();
+                return;
+            }
+            
+            removeNext30Seconds();
+        };
+        removeNext30Seconds();
+    });
+}
+
+function runTest() {   
+    video =  document.createElement('video');
+
+    loadMediaDataIntoVideo(video).then(sourceBuffer => {
+        startTime = PerfTestRunner.now();
+        return remove30SecondsAtATimeTillEmpty(sourceBuffer);
+    }).then(() => {
+        if (PerfTestRunner.measureValueAsync(PerfTestRunner.now() - startTime))
+            setTimeout(runTest, 0);
+    });
+}
+
+function loadMediaDataIntoVideo(video, segmentCount) {
+    return new Promise((resolve, reject) => {
+        var source = new MediaSource();
+        source._onsourceopen_ = () => {
+            source._onsourceopen_ = null;
+            source.duration = loader.duration * 100; 
+            var currentMediaSegment = 0;
+            var sourceBuffer = source.addSourceBuffer(loader.type);
+            sourceBuffer.mode = 'sequence';
+            sourceBuffer.appendBuffer(loader.initSegment);
+
+            var appendedMediaSegment = false;
+            sourceBuffer._onupdate_ = () => {
+
+                if (appendedMediaSegment) {
+                    if (source.readyState !== 'ended') {
+                        source.endOfStream();
+                        sourceBuffer._onupdate_ = null;
+                        sourceBuffer._onerror_ = null;
+                        resolve(sourceBuffer);
+                    }
+                    return;
+                }
+
+                sourceBuffer.appendBuffer(longMediaSegment);
+                appendedMediaSegment = true;
+            };
+            sourceBuffer._onerror_ = error => {
+                reject(error);
+            };
+        };
+        video.src = ""
+    });
+}
+</script>
+</head>
+<body>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/PerformanceTests/Media/media-source-loader.js (211745 => 211746)


--- trunk/PerformanceTests/Media/media-source-loader.js	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/PerformanceTests/Media/media-source-loader.js	2017-02-06 21:49:24 UTC (rev 211746)
@@ -87,7 +87,9 @@
 
     get duration()
     {
-        return this._manifest ? this._manifest.duration : 0
+        if (!this._manifest)
+            return 0;
+        return this._manifest.media.reduce((duration, media) => { return duration + media.duration }, 0);
     }
 
     get initSegment()

Added: trunk/PerformanceTests/MediaTime/Configurations/Base.xcconfig (0 => 211746)


--- trunk/PerformanceTests/MediaTime/Configurations/Base.xcconfig	                        (rev 0)
+++ trunk/PerformanceTests/MediaTime/Configurations/Base.xcconfig	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,119 @@
+// Copyright (C) 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+#include "../../../../Internal/Configurations/HaveInternalSDK.xcconfig"
+
+USE_INTERNAL_SDK = $(USE_INTERNAL_SDK_$(CONFIGURATION));
+USE_INTERNAL_SDK_Production = YES;
+USE_INTERNAL_SDK_Debug = $(HAVE_INTERNAL_SDK);
+USE_INTERNAL_SDK_Release = $(HAVE_INTERNAL_SDK);
+
+CLANG_CXX_LANGUAGE_STANDARD = gnu++14;
+CLANG_CXX_LIBRARY = libc++;
+CLANG_WARN_BOOL_CONVERSION = YES;
+CLANG_WARN_CONSTANT_CONVERSION = YES;
+CLANG_WARN_CXX0X_EXTENSIONS = NO;
+CLANG_WARN_EMPTY_BODY = YES;
+CLANG_WARN_ENUM_CONVERSION = YES;
+CLANG_WARN_INFINITE_RECURSION = YES;
+CLANG_WARN_INT_CONVERSION = YES;
+CLANG_WARN_SUSPICIOUS_MOVE = YES;
+CLANG_WARN_UNREACHABLE_CODE = YES;
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+COMBINE_HIDPI_IMAGES = NO;
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym;
+ENABLE_STRICT_OBJC_MSGSEND = YES;
+GCC_C_LANGUAGE_STANDARD = gnu99;
+GCC_DEBUGGING_SYMBOLS = default;
+GCC_DYNAMIC_NO_PIC = NO;
+GCC_ENABLE_CPP_EXCEPTIONS = NO;
+GCC_ENABLE_CPP_RTTI = NO;
+GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+GCC_ENABLE_SYMBOL_SEPARATION = NO;
+GCC_FAST_OBJC_DISPATCH = YES;
+GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+GCC_NO_COMMON_BLOCKS = YES;
+GCC_OBJC_CALL_CXX_CDTORS = YES;
+GCC_PRECOMPILE_PREFIX_HEADER = YES;
+GCC_PREPROCESSOR_DEFINITIONS = $(DEBUG_DEFINES) $(inherited);
+GCC_STRICT_ALIASING = YES;
+GCC_THREADSAFE_STATICS = NO;
+GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+// FIXME: <http://webkit.org/b/107093> WTF should build with -Wshorten-64-to-32
+GCC_WARN_64_TO_32_BIT_CONVERSION = $(GCC_WARN_64_TO_32_BIT_CONVERSION_$(CURRENT_ARCH));
+GCC_WARN_64_TO_32_BIT_CONVERSION_ = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION_armv7 = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION_armv7k = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION_armv7s = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION_arm64 = NO;
+GCC_WARN_64_TO_32_BIT_CONVERSION_i386 = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION_x86_64 = NO;
+GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+GCC_WARN_ABOUT_RETURN_TYPE = YES;
+GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+GCC_WARN_SIGN_COMPARE = YES;
+GCC_WARN_UNDECLARED_SELECTOR = YES;
+GCC_WARN_UNINITIALIZED_AUTOS = YES;
+GCC_WARN_UNUSED_FUNCTION = YES;
+GCC_WARN_UNUSED_VARIABLE = YES;
+PREBINDING = NO;
+WARNING_CFLAGS = -Wall -Wextra -Wcast-qual -Wchar-subscripts -Wextra-tokens -Wformat=2 -Winit-self -Wmissing-format-attribute -Wmissing-noreturn -Wpacked -Wpointer-arith -Wredundant-decls -Wundef -Wwrite-strings -Wexit-time-destructors -Wglobal-constructors -Wtautological-compare -Wimplicit-fallthrough;
+HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/usr/local/include $(DSTROOT)/usr/local/include icu $(HEADER_SEARCH_PATHS);
+
+TARGET_MAC_OS_X_VERSION_MAJOR = $(TARGET_MAC_OS_X_VERSION_MAJOR$(MACOSX_DEPLOYMENT_TARGET:suffix:identifier));
+TARGET_MAC_OS_X_VERSION_MAJOR_10 = 101000;
+TARGET_MAC_OS_X_VERSION_MAJOR_11 = 101100;
+TARGET_MAC_OS_X_VERSION_MAJOR_12 = 101200;
+TARGET_MAC_OS_X_VERSION_MAJOR_13 = 101300;
+
+SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx appletvos appletvsimulator watchos watchsimulator;
+
+_javascript_CORE_FRAMEWORKS_DIR = $(SYSTEM_LIBRARY_DIR)/Frameworks;
+
+// DEBUG_DEFINES, GCC_OPTIMIZATION_LEVEL, STRIP_INSTALLED_PRODUCT and DEAD_CODE_STRIPPING vary between the debug and normal variants.
+// We set up the values for each variant here, and have the Debug configuration in the Xcode project use the _debug variant.
+DEBUG_DEFINES_debug = ;
+DEBUG_DEFINES_normal = NDEBUG;
+DEBUG_DEFINES = $(DEBUG_DEFINES_$(CURRENT_VARIANT));
+
+GCC_OPTIMIZATION_LEVEL = $(GCC_OPTIMIZATION_LEVEL_$(CURRENT_VARIANT));
+GCC_OPTIMIZATION_LEVEL_normal = 3;
+GCC_OPTIMIZATION_LEVEL_debug = 0;
+
+STRIP_INSTALLED_PRODUCT = $(STRIP_INSTALLED_PRODUCT_$(CURRENT_VARIANT));
+STRIP_INSTALLED_PRODUCT_normal = YES;
+STRIP_INSTALLED_PRODUCT_debug = NO;
+
+DEAD_CODE_STRIPPING_debug = NO;
+DEAD_CODE_STRIPPING_normal = YES;
+DEAD_CODE_STRIPPING = $(DEAD_CODE_STRIPPING_$(CURRENT_VARIANT));
+
+SDKROOT = macosx.internal;
+
+OTHER_CFLAGS = $(ASAN_OTHER_CFLAGS);
+OTHER_CPLUSPLUSFLAGS = $(ASAN_OTHER_CPLUSPLUSFLAGS);
+OTHER_LDFLAGS = $(ASAN_OTHER_LDFLAGS);

Added: trunk/PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig (0 => 211746)


--- trunk/PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig	                        (rev 0)
+++ trunk/PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,42 @@
+// Copyright (C) 2009, 2010, 2013 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+#include "Base.xcconfig"
+
+ARCHS = $(ARCHS_STANDARD_32_64_BIT);
+_ONLY_ACTIVE_ARCH_ = YES;
+
+TARGET_MAC_OS_X_VERSION_MAJOR = $(MAC_OS_X_VERSION_MAJOR);
+
+MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(PLATFORM_NAME)_$(TARGET_MAC_OS_X_VERSION_MAJOR));
+MACOSX_DEPLOYMENT_TARGET_macosx_101000 = 10.10;
+MACOSX_DEPLOYMENT_TARGET_macosx_101100 = 10.11;
+MACOSX_DEPLOYMENT_TARGET_macosx_101200 = 10.12;
+MACOSX_DEPLOYMENT_TARGET_macosx_101300 = 10.13;
+
+GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
+DEBUG_INFORMATION_FORMAT = dwarf;
+
+SDKROOT = $(SDKROOT_$(USE_INTERNAL_SDK));
+SDKROOT_ = macosx;
+SDKROOT_YES = macosx.internal;

Added: trunk/PerformanceTests/MediaTime/Makefile (0 => 211746)


--- trunk/PerformanceTests/MediaTime/Makefile	                        (rev 0)
+++ trunk/PerformanceTests/MediaTime/Makefile	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,2 @@
+SCRIPTS_PATH ?= ../../Tools/Scripts
+include ../../Makefile.shared

Added: trunk/PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj (0 => 211746)


--- trunk/PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj	                        (rev 0)
+++ trunk/PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,256 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CDB099E11E4308470039E198 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB099E01E4308470039E198 /* main.cpp */; };
+		CDB099E91E4308700039E198 /* libWTF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB099E81E4308700039E198 /* libWTF.a */; };
+		CDB099EB1E430B250039E198 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB099EA1E430B250039E198 /* CoreFoundation.framework */; };
+		CDB099ED1E430B440039E198 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB099EC1E430B440039E198 /* Foundation.framework */; };
+		CDB099EF1E430B550039E198 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB099EE1E430B550039E198 /* libicucore.dylib */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CDB099DB1E4308470039E198 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		CD836CFE1E43BDB4009F8091 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
+		CD836D001E43BDB4009F8091 /* DebugRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DebugRelease.xcconfig; sourceTree = "<group>"; };
+		CDB099DD1E4308470039E198 /* MediaTime */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MediaTime; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDB099E01E4308470039E198 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../../../../Testcases/MediaTimeTestcase/MediaTime/main.cpp; sourceTree = "<group>"; };
+		CDB099E81E4308700039E198 /* libWTF.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libWTF.a; path = ../../WebKit.git/OpenSource/WebKitBuild/Debug/libWTF.a; sourceTree = "<group>"; };
+		CDB099EA1E430B250039E198 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+		CDB099EC1E430B440039E198 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		CDB099EE1E430B550039E198 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CDB099DA1E4308470039E198 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB099EF1E430B550039E198 /* libicucore.dylib in Frameworks */,
+				CDB099ED1E430B440039E198 /* Foundation.framework in Frameworks */,
+				CDB099EB1E430B250039E198 /* CoreFoundation.framework in Frameworks */,
+				CDB099E91E4308700039E198 /* libWTF.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CD836CFD1E43BDB4009F8091 /* Configurations */ = {
+			isa = PBXGroup;
+			children = (
+				CD836CFE1E43BDB4009F8091 /* Base.xcconfig */,
+				CD836D001E43BDB4009F8091 /* DebugRelease.xcconfig */,
+			);
+			path = Configurations;
+			sourceTree = "<group>";
+		};
+		CDB099D41E4308470039E198 = {
+			isa = PBXGroup;
+			children = (
+				CDB099E01E4308470039E198 /* main.cpp */,
+				CD836CFD1E43BDB4009F8091 /* Configurations */,
+				CDB099DE1E4308470039E198 /* Products */,
+				CDB099E71E43086F0039E198 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		CDB099DE1E4308470039E198 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDB099DD1E4308470039E198 /* MediaTime */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CDB099E71E43086F0039E198 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				CDB099EE1E430B550039E198 /* libicucore.dylib */,
+				CDB099EC1E430B440039E198 /* Foundation.framework */,
+				CDB099EA1E430B250039E198 /* CoreFoundation.framework */,
+				CDB099E81E4308700039E198 /* libWTF.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		CDB099DC1E4308470039E198 /* MediaTime */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDB099E41E4308470039E198 /* Build configuration list for PBXNativeTarget "MediaTime" */;
+			buildPhases = (
+				CDB099D91E4308470039E198 /* Sources */,
+				CDB099DA1E4308470039E198 /* Frameworks */,
+				CDB099DB1E4308470039E198 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = MediaTime;
+			productName = MediaTimeTestcase;
+			productReference = CDB099DD1E4308470039E198 /* MediaTime */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CDB099D51E4308470039E198 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0830;
+				ORGANIZATIONNAME = "Jeremy Noble";
+				TargetAttributes = {
+					CDB099DC1E4308470039E198 = {
+						CreatedOnToolsVersion = 8.3;
+						DevelopmentTeam = G5UYP5CS7K;
+						ProvisioningStyle = Automatic;
+					};
+				};
+			};
+			buildConfigurationList = CDB099D81E4308470039E198 /* Build configuration list for PBXProject "MediaTime" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CDB099D41E4308470039E198;
+			productRefGroup = CDB099DE1E4308470039E198 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CDB099DC1E4308470039E198 /* MediaTime */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CDB099D91E4308470039E198 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB099E11E4308470039E198 /* main.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CDB099E21E4308470039E198 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = CD836D001E43BDB4009F8091 /* DebugRelease.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				_ONLY_ACTIVE_ARCH_ = YES;
+			};
+			name = Debug;
+		};
+		CDB099E31E4308470039E198 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = CD836D001E43BDB4009F8091 /* DebugRelease.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_NO_COMMON_BLOCKS = YES;
+			};
+			name = Release;
+		};
+		CDB099E51E4308470039E198 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+				DEVELOPMENT_TEAM = G5UYP5CS7K;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CDB099E61E4308470039E198 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+				DEVELOPMENT_TEAM = G5UYP5CS7K;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		CDB099F01E4394540039E198 /* Control */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_NO_COMMON_BLOCKS = YES;
+				HEADER_SEARCH_PATHS = /usr/local/include;
+				LIBRARY_SEARCH_PATHS = /usr/local/lib;
+			};
+			name = Control;
+		};
+		CDB099F11E4394540039E198 /* Control */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+				DEVELOPMENT_TEAM = G5UYP5CS7K;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Control;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CDB099D81E4308470039E198 /* Build configuration list for PBXProject "MediaTime" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB099E21E4308470039E198 /* Debug */,
+				CDB099E31E4308470039E198 /* Release */,
+				CDB099F01E4394540039E198 /* Control */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CDB099E41E4308470039E198 /* Build configuration list for PBXNativeTarget "MediaTime" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB099E51E4308470039E198 /* Debug */,
+				CDB099E61E4308470039E198 /* Release */,
+				CDB099F11E4394540039E198 /* Control */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CDB099D51E4308470039E198 /* Project object */;
+}

Added: trunk/PerformanceTests/MediaTime/main.cpp (0 => 211746)


--- trunk/PerformanceTests/MediaTime/main.cpp	                        (rev 0)
+++ trunk/PerformanceTests/MediaTime/main.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <chrono>
+#include <random>
+#include <set>
+#include <wtf/MediaTime.h>
+
+using namespace std;
+using namespace std::chrono;
+
+static const size_t setSize = 100000;
+
+void performTest(const char* name, function<void()> test)
+{
+    vector<double> runtimes(21);
+    for (auto& runtime : runtimes) {
+        auto start = steady_clock::now();
+        test();
+        runtime = duration_cast<milliseconds>(steady_clock::now() - start).count();
+    }
+    sort(runtimes.begin(), runtimes.end());
+    double sum = std::accumulate(runtimes.begin(), runtimes.end(), 0);
+    double mean = sum / runtimes.size();
+    double median = runtimes[(runtimes.size() + 1) / 2];
+    double min = runtimes.front();
+    double max = runtimes.back();
+    double sqSum = std::inner_product(runtimes.begin(), runtimes.end(), runtimes.begin(), 0);
+    double stdev = std::sqrt(sqSum / runtimes.size() - mean * mean);
+
+    printf("RESULT %s: Time= %g ms", name, sum);
+    printf("median= %g ms, stdev= %g ms, min= %g ms, max = %g ms", median, stdev, min, max);
+}
+
+void test(int32_t count, function<MediaTime(int32_t)> generator)
+{
+    set<MediaTime> times;
+
+    for (int32_t i = 0; i < count; ++i)
+        times.insert(generator(i));
+
+    for (int32_t i = 0; i < count; ++i)
+        times.upper_bound(generator(i));
+}
+
+int main(int argc, const char * argv[])
+{
+    performTest("Equal TimeScales", [] { test(setSize, [] (int32_t i) { return MediaTime(i, 1); }); });
+    performTest("Equal TimeValues", [] { test(setSize, [] (int32_t i) { return MediaTime(1, i + 1); }); });
+    performTest("Disparate TimeValues & TimeScales", [] { test(setSize, [] (int32_t i) { return MediaTime(i, i + 1); }); });
+    performTest("Non-uniform", [] {
+        test(setSize, [] (int32_t i) {
+            switch (i % 6) {
+            case 0:
+                return MediaTime::invalidTime();
+            case 1:
+                return MediaTime::positiveInfiniteTime();
+            case 2:
+                return MediaTime::negativeInfiniteTime();
+            case 3:
+                return MediaTime::indefiniteTime();
+            case 4:
+                return MediaTime(i, 1);
+            case 5:
+            default:
+                return MediaTime(i, i + 1);
+            }
+        });
+    });
+
+    return 0;
+}

Modified: trunk/PerformanceTests/Skipped (211745 => 211746)


--- trunk/PerformanceTests/Skipped	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/PerformanceTests/Skipped	2017-02-06 21:49:24 UTC (rev 211746)
@@ -107,6 +107,7 @@
 # Media tests take too long to run; require MSE, HLS which are not supported on all ports;
 # and require a webserver (run-webkit-httpd) which is not part of normal performance testing.
 Media/
+MediaTime/
 
 # Bugs 167622, 167637, 167638 and 167640 - Some IndexedDB test deadlock on the GTK+ perf bot.
 [GTK] IndexedDB/index-get.html

Modified: trunk/Source/WTF/ChangeLog (211745 => 211746)


--- trunk/Source/WTF/ChangeLog	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WTF/ChangeLog	2017-02-06 21:49:24 UTC (rev 211746)
@@ -1,3 +1,41 @@
+2017-02-06  Jer Noble  <jer.no...@apple.com>
+
+        Playback stalls when a SourceBuffer append causes frame eviction
+        https://bugs.webkit.org/show_bug.cgi?id=167834
+
+        Reviewed by Eric Carlson.
+
+        Optimize the MediaTime class; specifically the compare() method. The class only
+        needs 6 bits to store the TimeFlags, so make that a uint8_t rather than uint32_t.
+        The implementation is slightly simpler if the TimeScale is unsigned, so make that
+        a uint32_t rather than int32_t. Inline the comparison operators. Optimize the equality
+        comparison by bitwise-and'ing the flags together and masking the result. Optimize for
+        common comparison scenarios (equal timeScales, equal timeValues(), etc.). Attempt the
+        mathematically simpler simpler method for comparing ratios, and only fall back to the
+        complex method if the results of multiplying the timeScale by the timeValue overflows.
+
+        * wtf/MediaTime.cpp:
+        (WTF::greatestCommonDivisor):
+        (WTF::leastCommonMultiple):
+        (WTF::signum):
+        (WTF::MediaTime::MediaTime):
+        (WTF::MediaTime::createWithFloat):
+        (WTF::MediaTime::createWithDouble):
+        (WTF::MediaTime::operator+):
+        (WTF::MediaTime::operator-):
+        (WTF::MediaTime::operator!):
+        (WTF::MediaTime::operator bool):
+        (WTF::MediaTime::compare):
+        (WTF::MediaTime::setTimeScale):
+        (WTF::abs):
+        (WTF::MediaTime::operator<): Deleted.
+        (WTF::MediaTime::operator>): Deleted.
+        (WTF::MediaTime::operator!=): Deleted.
+        (WTF::MediaTime::operator==): Deleted.
+        (WTF::MediaTime::operator>=): Deleted.
+        (WTF::MediaTime::operator<=): Deleted.
+        * wtf/MediaTime.h:
+
 2017-02-04  Michael Catanzaro  <mcatanz...@igalia.com>
 
         [GTK] Fix huge ENABLE_RESOURCE_USAGE warning spam

Modified: trunk/Source/WTF/wtf/MediaTime.cpp (211745 => 211746)


--- trunk/Source/WTF/wtf/MediaTime.cpp	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WTF/wtf/MediaTime.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -30,6 +30,7 @@
 #include "MediaTime.h"
 
 #include <algorithm>
+#include <cstdlib>
 #include <wtf/CheckedArithmetic.h>
 #include <wtf/MathExtras.h>
 #include <wtf/PrintStream.h>
@@ -36,10 +37,10 @@
 
 namespace WTF {
 
-static int32_t greatestCommonDivisor(int32_t a, int32_t b)
+static uint32_t greatestCommonDivisor(uint32_t a, uint32_t b)
 {
     // Euclid's Algorithm
-    int32_t temp = 0;
+    uint32_t temp = 0;
     while (b) {
         temp = b;
         b = a % b;
@@ -48,17 +49,17 @@
     return a;
 }
 
-static int32_t leastCommonMultiple(int32_t a, int32_t b, int32_t &result)
+static uint32_t leastCommonMultiple(uint32_t a, uint32_t b, uint32_t &result)
 {
     return safeMultiply(a, b / greatestCommonDivisor(a, b), result);
 }
 
-static int32_t signum(int64_t val)
+static int64_t signum(int64_t val)
 {
     return (0 < val) - (val < 0);
 }
 
-const int32_t MediaTime::MaximumTimeScale = 0x7fffffffL;
+const uint32_t MediaTime::MaximumTimeScale = 0x7fffffffL;
 
 MediaTime::MediaTime()
     : m_timeValue(0)
@@ -67,7 +68,7 @@
 {
 }
 
-MediaTime::MediaTime(int64_t value, int32_t scale, uint32_t flags)
+MediaTime::MediaTime(int64_t value, uint32_t scale, uint8_t flags)
     : m_timeValue(value)
     , m_timeScale(scale)
     , m_timeFlags(flags)
@@ -99,7 +100,7 @@
     return value;
 }
 
-MediaTime MediaTime::createWithFloat(float floatTime, int32_t timeScale)
+MediaTime MediaTime::createWithFloat(float floatTime, uint32_t timeScale)
 {
     if (floatTime != floatTime)
         return invalidTime();
@@ -131,7 +132,7 @@
     return value;
 }
 
-MediaTime MediaTime::createWithDouble(double doubleTime, int32_t timeScale)
+MediaTime MediaTime::createWithDouble(double doubleTime, uint32_t timeScale)
 {
     if (doubleTime != doubleTime)
         return invalidTime();
@@ -212,7 +213,7 @@
     else if (b.hasDoubleValue())
         b.setTimeScale(DefaultTimeScale);
 
-    int32_t commonTimeScale;
+    uint32_t commonTimeScale;
     if (!leastCommonMultiple(a.m_timeScale, b.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
         commonTimeScale = MaximumTimeScale;
     a.setTimeScale(commonTimeScale);
@@ -258,7 +259,7 @@
     else if (b.hasDoubleValue())
         b.setTimeScale(DefaultTimeScale);
 
-    int32_t commonTimeScale;
+    uint32_t commonTimeScale;
     if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
         commonTimeScale = MaximumTimeScale;
     a.setTimeScale(commonTimeScale);
@@ -334,73 +335,41 @@
     return a;
 }
 
-bool MediaTime::operator<(const MediaTime& rhs) const
-{
-    return compare(rhs) == LessThan;
-}
-
-bool MediaTime::operator>(const MediaTime& rhs) const
-{
-    return compare(rhs) == GreaterThan;
-}
-
-bool MediaTime::operator!=(const MediaTime& rhs) const
-{
-    return compare(rhs) != EqualTo;
-}
-
-bool MediaTime::operator==(const MediaTime& rhs) const
-{
-    return compare(rhs) == EqualTo;
-}
-
-bool MediaTime::operator>=(const MediaTime& rhs) const
-{
-    return compare(rhs) >= EqualTo;
-}
-
-bool MediaTime::operator<=(const MediaTime& rhs) const
-{
-    return compare(rhs) <= EqualTo;
-}
-
 bool MediaTime::operator!() const
 {
-    return compare(zeroTime()) == EqualTo;
+    return (m_timeFlags == Valid && !m_timeValue)
+        || (m_timeFlags == (Valid | DoubleValue) && !m_timeValueAsDouble);
 }
 
 MediaTime::operator bool() const
 {
-    return compare(zeroTime()) != EqualTo;
+    return !(m_timeFlags == Valid && !m_timeValue)
+        && !(m_timeFlags == (Valid | DoubleValue) && !m_timeValueAsDouble);
 }
 
 MediaTime::ComparisonFlags MediaTime::compare(const MediaTime& rhs) const
 {
-    if ((isPositiveInfinite() && rhs.isPositiveInfinite())
-        || (isNegativeInfinite() && rhs.isNegativeInfinite())
-        || (isInvalid() && rhs.isInvalid())
-        || (isIndefinite() && rhs.isIndefinite()))
+    auto andFlags = m_timeFlags & rhs.m_timeFlags;
+    if (andFlags & (PositiveInfinite | NegativeInfinite | Indefinite))
         return EqualTo;
 
-    if (isInvalid())
-        return GreaterThan;
+    auto orFlags = m_timeFlags | rhs.m_timeFlags;
+    if (!(orFlags & Valid))
+        return EqualTo;
 
-    if (rhs.isInvalid())
-        return LessThan;
+    if (!(andFlags & Valid))
+        return isInvalid() ? GreaterThan : LessThan;
 
-    if (rhs.isNegativeInfinite() || isPositiveInfinite())
-        return GreaterThan;
+    if (orFlags & NegativeInfinite)
+        return isNegativeInfinite() ? LessThan : GreaterThan;
 
-    if (rhs.isPositiveInfinite() || isNegativeInfinite())
-        return LessThan;
+    if (orFlags & PositiveInfinite)
+        return isPositiveInfinite() ? GreaterThan : LessThan;
 
-    if (isIndefinite())
-        return GreaterThan;
+    if (orFlags & Indefinite)
+        return isIndefinite() ? GreaterThan : LessThan;
 
-    if (rhs.isIndefinite())
-        return LessThan;
-
-    if (hasDoubleValue() && rhs.hasDoubleValue()) {
+    if (andFlags & DoubleValue) {
         if (m_timeValueAsDouble == rhs.m_timeValueAsDouble)
             return EqualTo;
 
@@ -407,7 +376,7 @@
         return m_timeValueAsDouble < rhs.m_timeValueAsDouble ? LessThan : GreaterThan;
     }
 
-    if (hasDoubleValue() || rhs.hasDoubleValue()) {
+    if (orFlags & DoubleValue) {
         double a = toDouble();
         double b = rhs.toDouble();
         if (a > b)
@@ -417,20 +386,47 @@
         return EqualTo;
     }
 
-    MediaTime a = *this;
-    MediaTime b = rhs;
+    if ((m_timeValue < 0) != (rhs.m_timeValue < 0))
+        return m_timeValue < 0 ? LessThan : GreaterThan;
 
-    int64_t rhsWhole = b.m_timeValue / b.m_timeScale;
-    int64_t lhsWhole = a.m_timeValue / a.m_timeScale;
+    if (!m_timeValue && !rhs.m_timeValue)
+        return EqualTo;
+
+    if (m_timeScale == rhs.m_timeScale) {
+        if (m_timeValue == rhs.m_timeValue)
+            return EqualTo;
+        return m_timeValue < rhs.m_timeValue ? LessThan : GreaterThan;
+    }
+
+    if (m_timeValue == rhs.m_timeValue)
+        return m_timeScale < rhs.m_timeScale ? GreaterThan : LessThan;
+
+    if (m_timeValue < rhs.m_timeValue && m_timeScale > rhs.m_timeScale)
+        return LessThan;
+
+    if (m_timeValue > rhs.m_timeValue && m_timeScale < rhs.m_timeScale)
+        return GreaterThan;
+
+    int64_t lhsFactor;
+    int64_t rhsFactor;
+    if (safeMultiply(m_timeValue, static_cast<int64_t>(rhs.m_timeScale), lhsFactor)
+        && safeMultiply(rhs.m_timeValue, static_cast<int64_t>(m_timeScale), rhsFactor)) {
+        if (lhsFactor == rhsFactor)
+            return EqualTo;
+        return lhsFactor < rhsFactor ? LessThan : GreaterThan;
+    }
+
+    int64_t rhsWhole = rhs.m_timeValue / rhs.m_timeScale;
+    int64_t lhsWhole = m_timeValue / m_timeScale;
     if (lhsWhole > rhsWhole)
         return GreaterThan;
     if (lhsWhole < rhsWhole)
         return LessThan;
 
-    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;
+    int64_t rhsRemain = rhs.m_timeValue % rhs.m_timeScale;
+    int64_t lhsRemain = m_timeValue % m_timeScale;
+    lhsFactor = lhsRemain * rhs.m_timeScale;
+    rhsFactor = rhsRemain * m_timeScale;
 
     if (lhsFactor == rhsFactor)
         return EqualTo;
@@ -474,7 +470,7 @@
     return *time;
 }
 
-void MediaTime::setTimeScale(int32_t timeScale)
+void MediaTime::setTimeScale(uint32_t timeScale)
 {
     if (hasDoubleValue()) {
         *this = MediaTime::createWithDouble(m_timeValueAsDouble, timeScale);
@@ -490,7 +486,7 @@
     // timescale by two until the number will fit, and round the
     // result.
     int64_t newWholePart;
-    while (!safeMultiply(wholePart, timeScale, newWholePart))
+    while (!safeMultiply(wholePart, static_cast<int64_t>(timeScale), newWholePart))
         timeScale /= 2;
 
     int64_t remainder = m_timeValue % m_timeScale;
@@ -513,7 +509,7 @@
         return MediaTime::createWithDouble(fabs(rhs.m_timeValueAsDouble));
 
     MediaTime val = rhs;
-    val.m_timeValue *= signum(rhs.m_timeScale) * signum(rhs.m_timeValue);
+    val.m_timeValue = std::abs(rhs.m_timeValue);
     return val;
 }
 

Modified: trunk/Source/WTF/wtf/MediaTime.h (211745 => 211746)


--- trunk/Source/WTF/wtf/MediaTime.h	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WTF/wtf/MediaTime.h	2017-02-06 21:49:24 UTC (rev 211746)
@@ -53,14 +53,14 @@
     };
 
     MediaTime();
-    MediaTime(int64_t value, int32_t scale, uint32_t flags = Valid);
+    MediaTime(int64_t value, uint32_t scale, uint8_t flags = Valid);
     MediaTime(const MediaTime& rhs);
     ~MediaTime();
 
     static MediaTime createWithFloat(float floatTime);
-    static MediaTime createWithFloat(float floatTime, int32_t timeScale);
+    static MediaTime createWithFloat(float floatTime, uint32_t timeScale);
     static MediaTime createWithDouble(double doubleTime);
-    static MediaTime createWithDouble(double doubleTime, int32_t timeScale);
+    static MediaTime createWithDouble(double doubleTime, uint32_t timeScale);
 
     float toFloat() const;
     double toDouble() const;
@@ -72,12 +72,12 @@
     MediaTime operator-(const MediaTime& rhs) const;
     MediaTime operator-() const;
     MediaTime operator*(int32_t) const;
-    bool operator<(const MediaTime& rhs) const;
-    bool operator>(const MediaTime& rhs) const;
-    bool operator!=(const MediaTime& rhs) const;
-    bool operator==(const MediaTime& rhs) const;
-    bool operator>=(const MediaTime& rhs) const;
-    bool operator<=(const MediaTime& rhs) const;
+    bool operator<(const MediaTime& rhs) const { return compare(rhs) == LessThan; }
+    bool operator>(const MediaTime& rhs) const { return compare(rhs) == GreaterThan; }
+    bool operator!=(const MediaTime& rhs) const { return compare(rhs) != EqualTo; }
+    bool operator==(const MediaTime& rhs) const { return compare(rhs) == EqualTo; }
+    bool operator>=(const MediaTime& rhs) const { return compare(rhs) >= EqualTo; }
+    bool operator<=(const MediaTime& rhs) const { return compare(rhs) <= EqualTo; }
     bool operator!() const;
     explicit operator bool() const;
 
@@ -105,7 +105,7 @@
     static const MediaTime& indefiniteTime();
 
     const int64_t& timeValue() const { return m_timeValue; }
-    const int32_t& timeScale() const { return m_timeScale; }
+    const uint32_t& timeScale() const { return m_timeScale; }
 
     void dump(PrintStream& out) const;
 
@@ -117,18 +117,18 @@
 
     friend WTF_EXPORT_PRIVATE MediaTime abs(const MediaTime& rhs);
 
-    static const int32_t DefaultTimeScale = 10000000;
-    static const int32_t MaximumTimeScale;
+    static const uint32_t DefaultTimeScale = 10000000;
+    static const uint32_t MaximumTimeScale;
 
 private:
-    void setTimeScale(int32_t);
+    void setTimeScale(uint32_t);
 
     union {
         int64_t m_timeValue;
         double m_timeValueAsDouble;
     };
-    int32_t m_timeScale;
-    uint32_t m_timeFlags;
+    uint32_t m_timeScale;
+    uint8_t m_timeFlags;
 };
 
 inline MediaTime operator*(int32_t lhs, const MediaTime& rhs) { return rhs.operator*(lhs); }

Modified: trunk/Source/WebCore/ChangeLog (211745 => 211746)


--- trunk/Source/WebCore/ChangeLog	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WebCore/ChangeLog	2017-02-06 21:49:24 UTC (rev 211746)
@@ -1,3 +1,51 @@
+2017-02-06  Jer Noble  <jer.no...@apple.com>
+
+        Playback stalls when a SourceBuffer append causes frame eviction
+        https://bugs.webkit.org/show_bug.cgi?id=167834
+
+        Reviewed by Eric Carlson.
+
+        Test: PerformanceTests/Media/MSERemoveCodedFrames.html
+
+        Optimize searching through SampleMap by presentationTime.
+
+        Many of the methods exposed by PresentationOrderSampleMap used the bare  std::equal_range,
+        lower_bound, or upper_bound methods. Unlike those methods exposed on std::map, the bare
+        search methods perform a linear O(n) search, rather than a the binary O(log(n)) search used
+        by std::map. Rewrite those methods using the bare methods in terms of the std::map search
+        methods.
+
+        Drive-by fix: rename findSampleOnOrAfterPresentationTime to
+        findSampleStartingOnOrAfterPresentationTime to make the behavior of the method more
+        explicit.
+
+        * Modules/mediasource/SampleMap.cpp:
+        (WebCore::PresentationOrderSampleMap::findSampleContainingPresentationTime):
+        (WebCore::PresentationOrderSampleMap::findSampleStartingOnOrAfterPresentationTime):
+        (WebCore::PresentationOrderSampleMap::reverseFindSampleBeforePresentationTime):
+        (WebCore::DecodeOrderSampleMap::findSyncSampleAfterPresentationTime):
+        (WebCore::PresentationOrderSampleMap::findSamplesBetweenPresentationTimes):
+        (WebCore::PresentationOrderSampleMap::findSamplesWithinPresentationRange):
+        (WebCore::PresentationOrderSampleMap::findSampleOnOrAfterPresentationTime): Deleted.
+        * Modules/mediasource/SampleMap.h:
+        (WebCore::PresentationOrderSampleMap::begin):
+        (WebCore::PresentationOrderSampleMap::end):
+        (WebCore::PresentationOrderSampleMap::rbegin):
+        (WebCore::PresentationOrderSampleMap::rend):
+        (WebCore::DecodeOrderSampleMap::begin):
+        (WebCore::DecodeOrderSampleMap::end):
+        (WebCore::DecodeOrderSampleMap::rbegin):
+        (WebCore::DecodeOrderSampleMap::rend):
+        (WebCore::SampleMap::SampleMap):
+        (WebCore::SampleMap::sizeInBytes):
+        (WebCore::SampleMap::decodeOrder):
+        (WebCore::SampleMap::presentationOrder):
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::removeSamplesFromTrackBuffer):
+        (WebCore::SourceBuffer::removeCodedFrames):
+        (WebCore::SourceBuffer::reenqueueMediaForTime):
+        * WebCore.xcodeproj/project.pbxproj:
+
 2017-02-06  Said Abou-Hallawa  <sabouhall...@apple.com>
 
         Rename AnimationController to CSSAnimationController

Modified: trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp (211745 => 211746)


--- trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -142,13 +142,21 @@
 
 PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleContainingPresentationTime(const MediaTime& time)
 {
-    auto range = std::equal_range(begin(), end(), time, SampleIsLessThanMediaTimeComparator<MapType>());
-    if (range.first == range.second)
+    // upper_bound will return the first sample whose presentation start time is greater than the search time.
+    // If this is the first sample, that means no sample in the map contains the requested time.
+    auto iter = m_samples.upper_bound(time);
+    if (iter == begin())
         return end();
-    return range.first;
+
+    // Look at the previous sample; does it contain the requested time?
+    --iter;
+    MediaSample& sample = *iter->second;
+    if (sample.presentationTime() + sample.duration() > time)
+        return iter;
+    return end();
 }
 
-PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleOnOrAfterPresentationTime(const MediaTime& time)
+PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleStartingOnOrAfterPresentationTime(const MediaTime& time)
 {
     return m_samples.lower_bound(time);
 }
@@ -168,7 +176,22 @@
 
 PresentationOrderSampleMap::reverse_iterator PresentationOrderSampleMap::reverseFindSampleBeforePresentationTime(const MediaTime& time)
 {
-    return std::lower_bound(rbegin(), rend(), time, SampleIsGreaterThanMediaTimeComparator<MapType>());
+    if (m_samples.empty())
+        return rend();
+
+    // upper_bound will return the first sample whose presentation start time is greater than the search time.
+    auto found = m_samples.upper_bound(time);
+
+    // If no sample was found with a time greater than the search time, return the last sample.
+    if (found == end())
+        return rbegin();
+
+    // If the first sample has a time grater than the search time, no samples will have a presentation time before the search time.
+    if (found == begin())
+        return rend();
+
+    // Otherwise, return the sample immediately previous to the one found.
+    return --reverse_iterator(--found);
 }
 
 DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::reverseFindSampleWithDecodeKey(const KeyType& key)
@@ -203,7 +226,7 @@
 
 DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSyncSampleAfterPresentationTime(const MediaTime& time, const MediaTime& threshold)
 {
-    PresentationOrderSampleMap::iterator currentSamplePTS = m_presentationOrder.findSampleOnOrAfterPresentationTime(time);
+    PresentationOrderSampleMap::iterator currentSamplePTS = m_presentationOrder.findSampleStartingOnOrAfterPresentationTime(time);
     if (currentSamplePTS == m_presentationOrder.end())
         return end();
 
@@ -228,14 +251,24 @@
 
 PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesBetweenPresentationTimes(const MediaTime& beginTime, const MediaTime& endTime)
 {
-    std::pair<MediaTime, MediaTime> range(beginTime, endTime);
-    return std::equal_range(begin(), end(), range, SamplePresentationTimeIsInsideRangeComparator());
+    // startTime is inclusive, so use lower_bound to include samples wich start exactly at startTime.
+    // endTime is not inclusive, so use lower_bound to exclude samples which start exactly at endTime.
+    auto lower_bound = m_samples.lower_bound(beginTime);
+    auto upper_bound = m_samples.lower_bound(endTime);
+    if (lower_bound == upper_bound)
+        return { end(), end() };
+    return { lower_bound, upper_bound };
 }
 
 PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesWithinPresentationRange(const MediaTime& beginTime, const MediaTime& endTime)
 {
-    std::pair<MediaTime, MediaTime> range(beginTime, endTime);
-    return std::equal_range(begin(), end(), range, SamplePresentationTimeIsWithinRangeComparator());
+    // startTime is not inclusive, so use upper_bound to exclude samples which start exactly at startTime.
+    // endTime is inclusive, so use upper_bound to include samples which start exactly at endTime.
+    auto lower_bound = m_samples.upper_bound(beginTime);
+    auto upper_bound = m_samples.upper_bound(endTime);
+    if (lower_bound == upper_bound)
+        return { end(), end() };
+    return { lower_bound, upper_bound };
 }
 
 PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesWithinPresentationRangeFromEnd(const MediaTime& beginTime, const MediaTime& endTime)

Modified: trunk/Source/WebCore/Modules/mediasource/SampleMap.h (211745 => 211746)


--- trunk/Source/WebCore/Modules/mediasource/SampleMap.h	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WebCore/Modules/mediasource/SampleMap.h	2017-02-06 21:49:24 UTC (rev 211746)
@@ -46,23 +46,23 @@
     typedef MapType::const_reverse_iterator const_reverse_iterator;
     typedef std::pair<iterator, iterator> iterator_range;
 
-    iterator begin() { return m_samples.begin(); }
-    const_iterator begin() const { return m_samples.begin(); }
-    iterator end() { return m_samples.end(); }
-    const_iterator end() const { return m_samples.end(); }
-    reverse_iterator rbegin() { return m_samples.rbegin(); }
-    const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
-    reverse_iterator rend() { return m_samples.rend(); }
-    const_reverse_iterator rend() const { return m_samples.rend(); }
+    WEBCORE_EXPORT iterator begin() { return m_samples.begin(); }
+    WEBCORE_EXPORT const_iterator begin() const { return m_samples.begin(); }
+    WEBCORE_EXPORT iterator end() { return m_samples.end(); }
+    WEBCORE_EXPORT const_iterator end() const { return m_samples.end(); }
+    WEBCORE_EXPORT reverse_iterator rbegin() { return m_samples.rbegin(); }
+    WEBCORE_EXPORT const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
+    WEBCORE_EXPORT reverse_iterator rend() { return m_samples.rend(); }
+    WEBCORE_EXPORT const_reverse_iterator rend() const { return m_samples.rend(); }
 
-    iterator findSampleWithPresentationTime(const MediaTime&);
-    iterator findSampleContainingPresentationTime(const MediaTime&);
-    iterator findSampleOnOrAfterPresentationTime(const MediaTime&);
-    reverse_iterator reverseFindSampleContainingPresentationTime(const MediaTime&);
-    reverse_iterator reverseFindSampleBeforePresentationTime(const MediaTime&);
-    iterator_range findSamplesBetweenPresentationTimes(const MediaTime&, const MediaTime&);
-    iterator_range findSamplesWithinPresentationRange(const MediaTime&, const MediaTime&);
-    iterator_range findSamplesWithinPresentationRangeFromEnd(const MediaTime&, const MediaTime&);
+    WEBCORE_EXPORT iterator findSampleWithPresentationTime(const MediaTime&);
+    WEBCORE_EXPORT iterator findSampleContainingPresentationTime(const MediaTime&);
+    WEBCORE_EXPORT iterator findSampleStartingOnOrAfterPresentationTime(const MediaTime&);
+    WEBCORE_EXPORT reverse_iterator reverseFindSampleContainingPresentationTime(const MediaTime&);
+    WEBCORE_EXPORT reverse_iterator reverseFindSampleBeforePresentationTime(const MediaTime&);
+    WEBCORE_EXPORT iterator_range findSamplesBetweenPresentationTimes(const MediaTime&, const MediaTime&);
+    WEBCORE_EXPORT iterator_range findSamplesWithinPresentationRange(const MediaTime&, const MediaTime&);
+    WEBCORE_EXPORT iterator_range findSamplesWithinPresentationRangeFromEnd(const MediaTime&, const MediaTime&);
 
 private:
     MapType m_samples;
@@ -79,22 +79,22 @@
     typedef MapType::const_reverse_iterator const_reverse_iterator;
     typedef std::pair<reverse_iterator, reverse_iterator> reverse_iterator_range;
 
-    iterator begin() { return m_samples.begin(); }
-    const_iterator begin() const { return m_samples.begin(); }
-    iterator end() { return m_samples.end(); }
-    const_iterator end() const { return m_samples.end(); }
-    reverse_iterator rbegin() { return m_samples.rbegin(); }
-    const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
-    reverse_iterator rend() { return m_samples.rend(); }
-    const_reverse_iterator rend() const { return m_samples.rend(); }
+    WEBCORE_EXPORT iterator begin() { return m_samples.begin(); }
+    WEBCORE_EXPORT const_iterator begin() const { return m_samples.begin(); }
+    WEBCORE_EXPORT iterator end() { return m_samples.end(); }
+    WEBCORE_EXPORT const_iterator end() const { return m_samples.end(); }
+    WEBCORE_EXPORT reverse_iterator rbegin() { return m_samples.rbegin(); }
+    WEBCORE_EXPORT const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
+    WEBCORE_EXPORT reverse_iterator rend() { return m_samples.rend(); }
+    WEBCORE_EXPORT const_reverse_iterator rend() const { return m_samples.rend(); }
 
-    iterator findSampleWithDecodeKey(const KeyType&);
-    reverse_iterator reverseFindSampleWithDecodeKey(const KeyType&);
-    reverse_iterator findSyncSamplePriorToPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
-    reverse_iterator findSyncSamplePriorToDecodeIterator(reverse_iterator);
-    iterator findSyncSampleAfterPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
-    iterator findSyncSampleAfterDecodeIterator(iterator);
-    reverse_iterator_range findDependentSamples(MediaSample*);
+    WEBCORE_EXPORT iterator findSampleWithDecodeKey(const KeyType&);
+    WEBCORE_EXPORT reverse_iterator reverseFindSampleWithDecodeKey(const KeyType&);
+    WEBCORE_EXPORT reverse_iterator findSyncSamplePriorToPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
+    WEBCORE_EXPORT reverse_iterator findSyncSamplePriorToDecodeIterator(reverse_iterator);
+    WEBCORE_EXPORT iterator findSyncSampleAfterPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
+    WEBCORE_EXPORT iterator findSyncSampleAfterDecodeIterator(iterator);
+    WEBCORE_EXPORT reverse_iterator_range findDependentSamples(MediaSample*);
 
 private:
     MapType m_samples;
@@ -103,24 +103,24 @@
 
 class SampleMap {
 public:
-    SampleMap()
+    WEBCORE_EXPORT SampleMap()
         : m_totalSize(0)
     {
     }
 
-    bool empty() const;
-    void clear();
-    void addSample(MediaSample&);
-    void removeSample(MediaSample*);
-    size_t sizeInBytes() const { return m_totalSize; }
+    WEBCORE_EXPORT bool empty() const;
+    WEBCORE_EXPORT void clear();
+    WEBCORE_EXPORT void addSample(MediaSample&);
+    WEBCORE_EXPORT void removeSample(MediaSample*);
+    WEBCORE_EXPORT size_t sizeInBytes() const { return m_totalSize; }
 
     template<typename I>
     void addRange(I begin, I end);
 
-    DecodeOrderSampleMap& decodeOrder() { return m_decodeOrder; }
-    const DecodeOrderSampleMap& decodeOrder() const { return m_decodeOrder; }
-    PresentationOrderSampleMap& presentationOrder() { return m_decodeOrder.m_presentationOrder; }
-    const PresentationOrderSampleMap& presentationOrder() const { return m_decodeOrder.m_presentationOrder; }
+    WEBCORE_EXPORT DecodeOrderSampleMap& decodeOrder() { return m_decodeOrder; }
+    WEBCORE_EXPORT const DecodeOrderSampleMap& decodeOrder() const { return m_decodeOrder; }
+    WEBCORE_EXPORT PresentationOrderSampleMap& presentationOrder() { return m_decodeOrder.m_presentationOrder; }
+    WEBCORE_EXPORT const PresentationOrderSampleMap& presentationOrder() const { return m_decodeOrder.m_presentationOrder; }
 
 private:
     DecodeOrderSampleMap m_decodeOrder;

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (211745 => 211746)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -705,7 +705,7 @@
                 additionalErasedRanges.add(previousSample.presentationTime() + previousSample.duration(), erasedStart);
         }
 
-        auto endIterator = trackBuffer.samples.presentationOrder().findSampleOnOrAfterPresentationTime(erasedEnd);
+        auto endIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(erasedEnd);
         if (endIterator == trackBuffer.samples.presentationOrder().end())
             additionalErasedRanges.add(erasedEnd, MediaTime::positiveInfiniteTime());
         else {
@@ -775,7 +775,7 @@
         else
             removePresentationEnd = trackBuffer.samples.presentationOrder().findSampleWithPresentationTime(removeDecodeEnd->second->presentationTime());
 
-        PresentationOrderSampleMap::iterator removePresentationStart = trackBuffer.samples.presentationOrder().findSampleOnOrAfterPresentationTime(start);
+        PresentationOrderSampleMap::iterator removePresentationStart = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(start);
         if (removePresentationStart == removePresentationEnd)
             continue;
 
@@ -1842,7 +1842,7 @@
     auto currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
 
     if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end())
-        currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleOnOrAfterPresentationTime(time);
+        currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(time);
 
     if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end()
         || (currentSamplePTSIterator->first - time) > MediaSource::currentTimeFudgeFactor())

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (211745 => 211746)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-02-06 21:49:24 UTC (rev 211746)
@@ -5929,7 +5929,7 @@
 		CDCFABBD18C0AF78006F8450 /* SelectionSubtreeRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = CDCFABBB18C0AE31006F8450 /* SelectionSubtreeRoot.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CDCFABBE18C0AF84006F8450 /* SelectionSubtreeRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCFABBC18C0AF19006F8450 /* SelectionSubtreeRoot.cpp */; };
 		CDD7089618359F6F002B3DC6 /* SampleMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDD7089418359F6E002B3DC6 /* SampleMap.cpp */; };
-		CDD7089718359F6F002B3DC6 /* SampleMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CDD7089518359F6F002B3DC6 /* SampleMap.h */; };
+		CDD7089718359F6F002B3DC6 /* SampleMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CDD7089518359F6F002B3DC6 /* SampleMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CDDC1E7A18A952F30027A9D4 /* MediaSourcePrivateClient.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDC1E7918A952F30027A9D4 /* MediaSourcePrivateClient.h */; };
 		CDDE02ED18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDDE02EB18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm */; };
 		CDDE02F018B5651300CF7FF1 /* CDMSessionAVStreamSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDDE02EF18B5651200CF7FF1 /* CDMSessionAVStreamSession.mm */; };

Modified: trunk/Tools/ChangeLog (211745 => 211746)


--- trunk/Tools/ChangeLog	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Tools/ChangeLog	2017-02-06 21:49:24 UTC (rev 211746)
@@ -1,3 +1,22 @@
+2017-02-06  Jer Noble  <jer.no...@apple.com>
+
+        Playback stalls when a SourceBuffer append causes frame eviction
+        https://bugs.webkit.org/show_bug.cgi?id=167834
+
+        Reviewed by Eric Carlson.
+
+        Add new correctness tests for the Webcore::SampleMap class. Add additional subtests
+        for the WTF::MediaTime class.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/MediaTime.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/SampleMap.cpp: Added.
+        (WTF::operator<<):
+        (TestWebKitAPI::TestSample::create):
+        (TestWebKitAPI::TestSample::TestSample):
+        (TestWebKitAPI::TEST_F):
+
 2017-02-06  Ryan Haddad  <ryanhad...@apple.com>
 
         Change capitalization in platform name after r211735.

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (211745 => 211746)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-02-06 21:49:24 UTC (rev 211746)
@@ -531,6 +531,7 @@
 		CDC8E4951BC6F10800594FEC /* video-with-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */; };
 		CDC8E4961BC6F10800594FEC /* video-without-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48B1BC5C96200594FEC /* video-without-audio.html */; };
 		CDC8E4971BC6F10800594FEC /* video-without-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */; };
+		CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDCFA7A91E45122F00C2433D /* SampleMap.cpp */; };
 		CDE195B51CFE0B880053D256 /* FullscreenTopContentInset.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDE195B21CFE0ADE0053D256 /* FullscreenTopContentInset.html */; };
 		CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */; };
 		CE14F1A4181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */; };
@@ -1328,6 +1329,7 @@
 		CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-with-audio.mp4"; sourceTree = "<group>"; };
 		CDC8E48B1BC5C96200594FEC /* video-without-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "video-without-audio.html"; sourceTree = "<group>"; };
 		CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-without-audio.mp4"; sourceTree = "<group>"; };
+		CDCFA7A91E45122F00C2433D /* SampleMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleMap.cpp; sourceTree = "<group>"; };
 		CDE195B21CFE0ADE0053D256 /* FullscreenTopContentInset.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = FullscreenTopContentInset.html; sourceTree = "<group>"; };
 		CDE195B31CFE0ADE0053D256 /* FullscreenTopContentInset.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenTopContentInset.mm; sourceTree = "<group>"; };
 		CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SecurityOrigin.cpp; sourceTree = "<group>"; };
@@ -1626,6 +1628,7 @@
 				83B88A331C80056D00BB2418 /* HTMLParserIdioms.cpp */,
 				14464012167A8305000BD218 /* LayoutUnit.cpp */,
 				CD225C071C45A69200140761 /* ParsedContentRange.cpp */,
+				CDCFA7A91E45122F00C2433D /* SampleMap.cpp */,
 				CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */,
 				41973B5C1AF22875006C7B36 /* SharedBuffer.cpp */,
 				A17991891E1CA24100A505ED /* SharedBufferTest.cpp */,
@@ -2754,6 +2757,7 @@
 				7CCE7F191A411AE600447C4C /* WebArchive.cpp in Sources */,
 				2D4CF8BD1D8360CC0001CE8D /* WKThumbnailView.mm in Sources */,
 				7C83E04C1D0A641800FEBCF3 /* WebCoreNSURLSession.mm in Sources */,
+				CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */,
 				7CCE7F1A1A411AE600447C4C /* WebCoreStatisticsWithNoWebProcess.cpp in Sources */,
 				7CCE7EAB1A411A2400447C4C /* WebKitAgnosticTest.mm in Sources */,
 				51714EB81CF8CA17004723C4 /* WebProcessKillIDBCleanup.mm in Sources */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp (211745 => 211746)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp	2017-02-06 20:49:25 UTC (rev 211745)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -85,6 +85,8 @@
     EXPECT_EQ(MediaTime(1, 1) != MediaTime(2, 1), true);
     EXPECT_EQ(MediaTime(2, 1) == MediaTime(2, 1), true);
     EXPECT_EQ(MediaTime(2, 1) == MediaTime(4, 2), true);
+    EXPECT_TRUE((bool)MediaTime(1, 1));
+    EXPECT_TRUE(!MediaTime(0, 1));
 
     // Addition Operators
     EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime());
@@ -158,8 +160,6 @@
     EXPECT_EQ(abs(MediaTime::invalidTime()), MediaTime::invalidTime());
     EXPECT_EQ(abs(MediaTime(1, 1)), MediaTime(1, 1));
     EXPECT_EQ(abs(MediaTime(-1, 1)), MediaTime(1, 1));
-    EXPECT_EQ(abs(MediaTime(-1, -1)), MediaTime(-1, -1));
-    EXPECT_EQ(abs(MediaTime(1, -1)), MediaTime(-1, -1));
 
     // Floating Point Functions
     EXPECT_EQ(MediaTime::createWithFloat(1.0f), MediaTime(1, 1));
@@ -200,14 +200,14 @@
     // Overflow Behavior
     EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 64.0f)), MediaTime::positiveInfiniteTime());
     EXPECT_EQ(MediaTime::createWithFloat(-pow(2.0f, 64.0f)), MediaTime::negativeInfiniteTime());
-    EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 2).timeScale(), 1);
-    EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 3).timeScale(), 1);
+    EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 2).timeScale(), 1U);
+    EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 3).timeScale(), 1U);
     EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 64.0)), MediaTime::positiveInfiniteTime());
     EXPECT_EQ(MediaTime::createWithDouble(-pow(2.0, 64.0)), MediaTime::negativeInfiniteTime());
-    EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 2).timeScale(), 1);
-    EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 3).timeScale(), 1);
-    EXPECT_EQ((MediaTime(numeric_limits<int64_t>::max(), 2) + MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1);
-    EXPECT_EQ((MediaTime(numeric_limits<int64_t>::min(), 2) - MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1);
+    EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 2).timeScale(), 1U);
+    EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 3).timeScale(), 1U);
+    EXPECT_EQ((MediaTime(numeric_limits<int64_t>::max(), 2) + MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1U);
+    EXPECT_EQ((MediaTime(numeric_limits<int64_t>::min(), 2) - MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1U);
     EXPECT_EQ(MediaTime(numeric_limits<int64_t>::max(), 1) + MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::positiveInfiniteTime());
     EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) + MediaTime(numeric_limits<int64_t>::min(), 1), MediaTime::negativeInfiniteTime());
     EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) - MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::negativeInfiniteTime());

Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp (0 => 211746)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp	2017-02-06 21:49:24 UTC (rev 211746)
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(MEDIA_SOURCE)
+
+#include "Test.h"
+#include <WebCore/MediaSample.h>
+#include <WebCore/SampleMap.h>
+
+namespace WTF {
+inline std::ostream& operator<<(std::ostream& os, const MediaTime& time)
+{
+    if (time.hasDoubleValue())
+        os << "{ " << time.toDouble() << " }";
+    else
+        os << "{ " << time.timeValue() << " / " << time.timeScale() << ", " << time.toDouble() << " }";
+    return os;
+}
+}
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+class TestSample : public MediaSample {
+public:
+    static Ref<TestSample> create(const MediaTime& presentationTime, const MediaTime& decodeTime, const MediaTime& duration, SampleFlags flags)
+    {
+        return adoptRef(*new TestSample(presentationTime, decodeTime, duration, flags));
+    }
+    
+    MediaTime presentationTime() const final { return m_presentationTime; }
+    MediaTime decodeTime() const final { return m_decodeTime; }
+    MediaTime duration() const final { return m_duration; }
+    AtomicString trackID() const final { return m_trackID; }
+    void setTrackID(const String& trackID) final { m_trackID = trackID; }
+    size_t sizeInBytes() const final { return m_sizeInBytes; }
+    FloatSize presentationSize() const final { return m_presentationSize; }
+    void offsetTimestampsBy(const MediaTime& offset) final { m_presentationTime += offset; m_decodeTime += offset; }
+    void setTimestamps(const MediaTime& presentationTime, const MediaTime& decodeTime) final {
+        m_presentationTime = presentationTime;
+        m_decodeTime = decodeTime;
+    };
+    bool isDivisable() const final { return false; }
+    std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> divide(const MediaTime& presentationTime) final { return { }; }
+    Ref<MediaSample> createNonDisplayingCopy() const final {
+        return create(m_presentationTime, m_decodeTime, m_duration, static_cast<SampleFlags>(m_flags | IsNonDisplaying));
+    }
+    SampleFlags flags() const final { return m_flags; }
+    PlatformSample platformSample() final { return { PlatformSample::None, {nullptr}}; }
+    void dump(PrintStream&) const final { }
+
+private:
+    TestSample(const MediaTime& presentationTime, const MediaTime& decodeTime, const MediaTime& duration, SampleFlags flags)
+        : m_presentationTime(presentationTime)
+        , m_decodeTime(decodeTime)
+        , m_duration(duration)
+        , m_flags(flags)
+    {
+    }
+
+    MediaTime m_presentationTime;
+    MediaTime m_decodeTime;
+    MediaTime m_duration;
+    FloatSize m_presentationSize;
+    AtomicString m_trackID;
+    size_t m_sizeInBytes { 0 };
+    SampleFlags m_flags { None };
+};
+
+class SampleMapTest : public testing::Test {
+public:
+    void SetUp() final {
+        map.addSample(TestSample::create(MediaTime(0, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync));
+        map.addSample(TestSample::create(MediaTime(1, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(2, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(3, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(4, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(5, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync));
+        map.addSample(TestSample::create(MediaTime(6, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(7, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(8, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(9, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        // Gap at MediaTime(10, 1) -> MediaTime(11, 1);
+        map.addSample(TestSample::create(MediaTime(11, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync));
+        map.addSample(TestSample::create(MediaTime(12, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(13, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(14, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(15, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync));
+        map.addSample(TestSample::create(MediaTime(16, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(17, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(18, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+        map.addSample(TestSample::create(MediaTime(19, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None));
+    }
+
+    SampleMap map;
+};
+
+TEST_F(SampleMapTest, findSampleWithPresentationTime)
+{
+    auto& presentationMap = map.presentationOrder();
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleWithPresentationTime(MediaTime(0, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleWithPresentationTime(MediaTime(19, 1))->second->presentationTime());
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(-1, 1)));
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(10, 1)));
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(20, 1)));
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(1, 2)));
+}
+
+TEST_F(SampleMapTest, findSampleContainingPresentationTime)
+{
+    auto& presentationMap = map.presentationOrder();
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(0, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(19, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(1, 2))->second->presentationTime());
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(-1, 1)));
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(10, 1)));
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(20, 1)));
+}
+
+TEST_F(SampleMapTest, findSampleStartingOnOrAfterPresentationTime)
+{
+    auto& presentationMap = map.presentationOrder();
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(0, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(19, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(1, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(1, 2))->second->presentationTime());
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(-1, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(11, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(10, 1))->second->presentationTime());
+    EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(20, 1)));
+}
+
+TEST_F(SampleMapTest, findSamplesBetweenPresentationTimes)
+{
+    auto& presentationMap = map.presentationOrder();
+    auto iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(0, 1), MediaTime(1, 1));
+    EXPECT_EQ(MediaTime(0, 1), iterator_range.first->second->presentationTime());
+    EXPECT_EQ(MediaTime(1, 1), iterator_range.second->second->presentationTime());
+
+    iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(1, 2), MediaTime(3, 2));
+    EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime());
+    EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime());
+
+    iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(9, 1), MediaTime(21, 1));
+    EXPECT_EQ(MediaTime(9, 1), iterator_range.first->second->presentationTime());
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+
+    iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(-1, 1), MediaTime(0, 1));
+    EXPECT_TRUE(presentationMap.end() == iterator_range.first);
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+
+    iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(19, 2), MediaTime(10, 1));
+    EXPECT_TRUE(presentationMap.end() == iterator_range.first);
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+
+    iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(20, 1), MediaTime(21, 1));
+    EXPECT_TRUE(presentationMap.end() == iterator_range.first);
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+}
+
+TEST_F(SampleMapTest, findSamplesWithinPresentationRange)
+{
+    auto& presentationMap = map.presentationOrder();
+    auto iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(0, 1), MediaTime(1, 1));
+    EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime());
+    EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime());
+
+    iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(1, 2), MediaTime(3, 2));
+    EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime());
+    EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime());
+
+    iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(9, 1), MediaTime(21, 1));
+    EXPECT_EQ(MediaTime(11, 1), iterator_range.first->second->presentationTime());
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+
+    iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(-1, 1), MediaTime(0, 1));
+    EXPECT_EQ(MediaTime(0, 1), iterator_range.first->second->presentationTime());
+    EXPECT_EQ(MediaTime(1, 1), iterator_range.second->second->presentationTime());
+
+    iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(10, 1), MediaTime(21, 2));
+    EXPECT_TRUE(presentationMap.end() == iterator_range.first);
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+
+    iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(20, 1), MediaTime(21, 1));
+    EXPECT_TRUE(presentationMap.end() == iterator_range.first);
+    EXPECT_TRUE(presentationMap.end() == iterator_range.second);
+}
+
+TEST_F(SampleMapTest, reverseFindSampleBeforePresentationTime)
+{
+    auto& presentationMap = map.presentationOrder();
+    EXPECT_EQ(MediaTime(0, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(0, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(9, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(10, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(19, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(19, 1))->second->presentationTime());
+    EXPECT_EQ(MediaTime(19, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(21, 1))->second->presentationTime());
+    EXPECT_TRUE(presentationMap.rend() == presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(-1, 1)));
+}
+
+}
+
+#endif // ENABLE(MEDIA_SOURCE)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to