Playback stalls when a SourceBuffer append causes frame eviction
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Feb 2017 21:49:24 +0000 (21:49 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Feb 2017 21:49:24 +0000 (21:49 +0000)
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):

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

21 files changed:
PerformanceTests/ChangeLog
PerformanceTests/Media/MSERemoveCodedFrames.html [new file with mode: 0644]
PerformanceTests/Media/media-source-loader.js
PerformanceTests/MediaTime/Configurations/Base.xcconfig [new file with mode: 0644]
PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig [new file with mode: 0644]
PerformanceTests/MediaTime/Makefile [new file with mode: 0644]
PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj [new file with mode: 0644]
PerformanceTests/MediaTime/main.cpp [new file with mode: 0644]
PerformanceTests/Skipped
Source/WTF/ChangeLog
Source/WTF/wtf/MediaTime.cpp
Source/WTF/wtf/MediaTime.h
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediasource/SampleMap.cpp
Source/WebCore/Modules/mediasource/SampleMap.h
Source/WebCore/Modules/mediasource/SourceBuffer.cpp
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp
Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp [new file with mode: 0644]

index e0104be..65af1db 100644 (file)
@@ -1,3 +1,30 @@
+2017-02-06  Jer Noble  <jer.noble@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  <sbarati@apple.com>
 
         Make ARES-6 work from the CLI again
diff --git a/PerformanceTests/Media/MSERemoveCodedFrames.html b/PerformanceTests/Media/MSERemoveCodedFrames.html
new file mode 100644 (file)
index 0000000..667d4ee
--- /dev/null
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="media-source-loader.js"></script>
+<script src="../resources/runner.js"></script>
+<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 = null;
+                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 = URL.createObjectURL(source);
+    });
+}
+</script>
+</head>
+<body>
+</body>
+</html>
\ No newline at end of file
index 8af4015..24144f7 100644 (file)
@@ -87,7 +87,9 @@ class MediaSourceLoader {
 
     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()
diff --git a/PerformanceTests/MediaTime/Configurations/Base.xcconfig b/PerformanceTests/MediaTime/Configurations/Base.xcconfig
new file mode 100644 (file)
index 0000000..0dbe8bf
--- /dev/null
@@ -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;
+
+JAVASCRIPTCORE_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);
diff --git a/PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig b/PerformanceTests/MediaTime/Configurations/DebugRelease.xcconfig
new file mode 100644 (file)
index 0000000..83fa351
--- /dev/null
@@ -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;
diff --git a/PerformanceTests/MediaTime/Makefile b/PerformanceTests/MediaTime/Makefile
new file mode 100644 (file)
index 0000000..95e48d6
--- /dev/null
@@ -0,0 +1,2 @@
+SCRIPTS_PATH ?= ../../Tools/Scripts
+include ../../Makefile.shared
diff --git a/PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj b/PerformanceTests/MediaTime/MediaTime.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..fcf08a4
--- /dev/null
@@ -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 */;
+}
diff --git a/PerformanceTests/MediaTime/main.cpp b/PerformanceTests/MediaTime/main.cpp
new file mode 100644 (file)
index 0000000..62ae9f7
--- /dev/null
@@ -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;
+}
index efd23bc..9f03199 100644 (file)
@@ -107,6 +107,7 @@ Animation/svg-animation.html
 # 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
index 71e3b29..cb3f181 100644 (file)
@@ -1,3 +1,41 @@
+2017-02-06  Jer Noble  <jer.noble@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  <mcatanzaro@igalia.com>
 
         [GTK] Fix huge ENABLE_RESOURCE_USAGE warning spam
index 67cbdf7..9db4c70 100644 (file)
 #include "MediaTime.h"
 
 #include <algorithm>
+#include <cstdlib>
 #include <wtf/CheckedArithmetic.h>
 #include <wtf/MathExtras.h>
 #include <wtf/PrintStream.h>
 
 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 @@ static int32_t greatestCommonDivisor(int32_t a, int32_t b)
     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()
 {
 }
 
-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 @@ MediaTime MediaTime::createWithFloat(float floatTime)
     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 @@ MediaTime MediaTime::createWithDouble(double doubleTime)
     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 @@ MediaTime MediaTime::operator+(const MediaTime& rhs) const
     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 @@ MediaTime MediaTime::operator-(const MediaTime& rhs) const
     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,80 +335,48 @@ MediaTime MediaTime::operator*(int32_t rhs) const
     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;
-
-    if (rhs.isInvalid())
-        return LessThan;
+    auto orFlags = m_timeFlags | rhs.m_timeFlags;
+    if (!(orFlags & Valid))
+        return EqualTo;
 
-    if (rhs.isNegativeInfinite() || isPositiveInfinite())
-        return GreaterThan;
+    if (!(andFlags & Valid))
+        return isInvalid() ? GreaterThan : LessThan;
 
-    if (rhs.isPositiveInfinite() || isNegativeInfinite())
-        return LessThan;
+    if (orFlags & NegativeInfinite)
+        return isNegativeInfinite() ? LessThan : GreaterThan;
 
-    if (isIndefinite())
-        return GreaterThan;
+    if (orFlags & PositiveInfinite)
+        return isPositiveInfinite() ? GreaterThan : LessThan;
 
-    if (rhs.isIndefinite())
-        return LessThan;
+    if (orFlags & Indefinite)
+        return isIndefinite() ? GreaterThan : LessThan;
 
-    if (hasDoubleValue() && rhs.hasDoubleValue()) {
+    if (andFlags & DoubleValue) {
         if (m_timeValueAsDouble == rhs.m_timeValueAsDouble)
             return EqualTo;
 
         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 @@ MediaTime::ComparisonFlags MediaTime::compare(const MediaTime& rhs) const
         return EqualTo;
     }
 
-    MediaTime a = *this;
-    MediaTime b = rhs;
+    if ((m_timeValue < 0) != (rhs.m_timeValue < 0))
+        return m_timeValue < 0 ? LessThan : GreaterThan;
+
+    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 = b.m_timeValue / b.m_timeScale;
-    int64_t lhsWhole = a.m_timeValue / a.m_timeScale;
+    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 @@ const MediaTime& MediaTime::indefiniteTime()
     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 @@ void MediaTime::setTimeScale(int32_t timeScale)
     // 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 @@ MediaTime abs(const MediaTime& rhs)
         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;
 }
 
index 4cf793a..50a0180 100644 (file)
@@ -53,14 +53,14 @@ public:
     };
 
     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 @@ public:
     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 @@ public:
     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 @@ public:
 
     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); }
index c706700..c303198 100644 (file)
@@ -1,3 +1,51 @@
+2017-02-06  Jer Noble  <jer.noble@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  <sabouhallawa@apple.com>
 
         Rename AnimationController to CSSAnimationController
index 21a6ff2..42157c5 100644 (file)
@@ -142,13 +142,21 @@ PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleWithP
 
 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::reverse
 
 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::reverse_iterator DecodeOrderSampleMap::findSyncSamplePrior
 
 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 @@ DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSyncSampleAfterDecodeIt
 
 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)
index da56f95..9ba886d 100644 (file)
@@ -46,23 +46,23 @@ public:
     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(); }
-
-    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 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(); }
+
+    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 @@ public:
     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(); }
-
-    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 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(); }
+
+    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 @@ private:
 
 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;
index d9ae527..2b4ac58 100644 (file)
@@ -705,7 +705,7 @@ static PlatformTimeRanges removeSamplesFromTrackBuffer(const DecodeOrderSampleMa
                 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 @@ void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& en
         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 @@ void SourceBuffer::reenqueueMediaForTime(TrackBuffer& trackBuffer, const AtomicS
     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())
index 1565f46..918b67f 100644 (file)
                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 */; };
index 2eee5e4..465effc 100644 (file)
@@ -1,3 +1,22 @@
+2017-02-06  Jer Noble  <jer.noble@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  <ryanhaddad@apple.com>
 
         Change capitalization in platform name after r211735.
index 384925d..1e20ca3 100644 (file)
                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 */; };
                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>"; };
                                83B88A331C80056D00BB2418 /* HTMLParserIdioms.cpp */,
                                14464012167A8305000BD218 /* LayoutUnit.cpp */,
                                CD225C071C45A69200140761 /* ParsedContentRange.cpp */,
+                               CDCFA7A91E45122F00C2433D /* SampleMap.cpp */,
                                CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */,
                                41973B5C1AF22875006C7B36 /* SharedBuffer.cpp */,
                                A17991891E1CA24100A505ED /* SharedBufferTest.cpp */,
                                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 */,
index b7d1c6e..ac8bb1c 100644 (file)
@@ -85,6 +85,8 @@ TEST(WTF, MediaTime)
     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 @@ TEST(WTF, MediaTime)
     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 @@ TEST(WTF, MediaTime)
     // 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());
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp
new file mode 100644 (file)
index 0000000..f7e91c6
--- /dev/null
@@ -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)