Title: [239636] trunk
Revision
239636
Author
jer.no...@apple.com
Date
2019-01-04 13:32:48 -0800 (Fri, 04 Jan 2019)

Log Message

Web Content process main thread blocked beneath ImageDecoderAVFObjC::readSamples for many seconds on imgur.com
https://bugs.webkit.org/show_bug.cgi?id=191806
<rdar://problem/46151477>

Reviewed by Dean Jackson.

Source/WebCore:

Test: http/tests/images/mp4-partial-load.html

Rather than use an AVAssetReaderTrackOutput, which will load both sample metadata and sample data
synchronously when a sample is requested, use AVAssetReaderSampleReferenceOutput, which only loads
sample metadata, including the byte offset and byte length of the sample data. By waiting until the
AVAsset signals that it's own metadata is loaded, we can safely parse all the sample metadata without
blocking on network loads. Once enough data is loaded, we can replace the byte reference and offset
attachements in the sample with actual data, and mark the sample as "complete".

Because the existing ImageSource assumes that image data parsing will occur synchronously, and that
synchronous parsing could cause a hang if the metadata is not loaded, add a new callback method which
allows the ImageSource to be notified when the encodedDataStatus changes. The ImageSource notifies the
CacheImage, which notifies the RenderImage, and thus the asynchronous parsing will kick off the
renderer's animation loop.

* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::CachedImageObserver::encodedDataStatusChanged):
(WebCore::CachedImage::encodedDataStatusChanged):
* loader/cache/CachedImage.h:
* platform/graphics/ImageDecoder.h:
(WebCore::ImageDecoder::setEncodedDataStatusChangeCallback):
* platform/graphics/ImageObserver.h:
(WebCore::ImageObserver::encodedDataStatusChanged):
* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::ensureDecoderAvailable):
(WebCore::ImageSource::encodedDataStatusChanged):
(WebCore::ImageSource::frameDecodingStatusAtIndex):
* platform/graphics/ImageSource.h:
* platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.h:
* platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.mm:
(-[WebCoreSharedBufferResourceLoaderDelegate data]):
(WebCore::ImageDecoderAVFObjCSample::byteRange const):
(WebCore::ImageDecoderAVFObjC::readSamples):
(WebCore::ImageDecoderAVFObjC::setEncodedDataStatusChangeCallback):
(WebCore::ImageDecoderAVFObjC::encodedDataStatus const):
(WebCore::ImageDecoderAVFObjC::frameIsCompleteAtIndex const):
(WebCore::ImageDecoderAVFObjC::createFrameImageAtIndex):
(WebCore::ImageDecoderAVFObjC::sampleIsComplete const):

Source/WebCore/PAL:

* pal/cf/CoreMediaSoftLink.cpp:
* pal/cf/CoreMediaSoftLink.h:

LayoutTests:

* http/tests/images/mp4-partial-load-expected.txt: Added.
* http/tests/images/mp4-partial-load.html: Added.
* platform/win/http/tests/mp4-partial-load-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (239635 => 239636)


--- trunk/LayoutTests/ChangeLog	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/LayoutTests/ChangeLog	2019-01-04 21:32:48 UTC (rev 239636)
@@ -1,3 +1,15 @@
+2019-01-04  Jer Noble  <jer.no...@apple.com>
+
+        Web Content process main thread blocked beneath ImageDecoderAVFObjC::readSamples for many seconds on imgur.com
+        https://bugs.webkit.org/show_bug.cgi?id=191806
+        <rdar://problem/46151477>
+
+        Reviewed by Dean Jackson.
+
+        * http/tests/images/mp4-partial-load-expected.txt: Added.
+        * http/tests/images/mp4-partial-load.html: Added.
+        * platform/win/http/tests/mp4-partial-load-expected.txt: Added.
+
 2019-01-04  Youenn Fablet  <you...@apple.com>
 
         CSP violation reports should bypass CSP checks

Added: trunk/LayoutTests/http/tests/images/mp4-partial-load-expected.txt (0 => 239636)


--- trunk/LayoutTests/http/tests/images/mp4-partial-load-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/images/mp4-partial-load-expected.txt	2019-01-04 21:32:48 UTC (rev 239636)
@@ -0,0 +1,10 @@
+This tests that an mp4 loaded in a video tag does not cause a hang when the load stalls.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS img.naturalWidth became 320
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/http/tests/images/mp4-partial-load.html (0 => 239636)


--- trunk/LayoutTests/http/tests/images/mp4-partial-load.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/images/mp4-partial-load.html	2019-01-04 21:32:48 UTC (rev 239636)
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>mp4-partial-load</title>
+    <script src=""
+    <script>
+    jsTestIsAsync = true;
+    if (window.internals && window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        internals.clearMemoryCache();
+    }
+
+    function loadAndStall()
+    {
+        return "http://127.0.0.1:8000/resources/load-and-stall.php";
+    }
+
+    function mp4Image()
+    {
+        return "?name=../../../media/content/test.mp4&mimeType=video%2Fmp4";
+    }
+
+    function endTest()
+    {
+        if (window.timeoutID)
+            clearTimeout(timeoutID);
+        finishJSTest();
+    }
+
+    function errorReceived()
+    {
+        testFailed('Received an erorr loading image');
+        endTest();
+    }
+
+    function timedOut()
+    {
+        testFailed('Took too long');
+        endTest();
+    }
+
+    function runTest()
+    {
+        description('This tests that an mp4 loaded in a video tag does not cause a hang when the load stalls.')
+
+        setTimeout(() => {
+            img = document.querySelector('img');
+            img.addEventListener("error", errorReceived);
+            img.src = "" + mp4Image() + "&stallAt=15799&stallFor=60";
+            window.timeoutID = setTimeout(timedOut, 5000);
+            shouldBecomeEqual('img.naturalWidth', '320', endTest);
+        });
+    }
+
+    window.addEventListener('load', runTest, {once: true});
+    </script>
+    <script src=""
+</head>
+<body>
+    <img>
+</body>
+</html>

Added: trunk/LayoutTests/platform/win/http/tests/images/mp4-partial-load-expected.txt (0 => 239636)


--- trunk/LayoutTests/platform/win/http/tests/images/mp4-partial-load-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/win/http/tests/images/mp4-partial-load-expected.txt	2019-01-04 21:32:48 UTC (rev 239636)
@@ -0,0 +1,10 @@
+This tests that an mp4 loaded in a video tag does not cause a hang when the load stalls.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+FAIL Took too long
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Modified: trunk/Source/WebCore/ChangeLog (239635 => 239636)


--- trunk/Source/WebCore/ChangeLog	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/ChangeLog	2019-01-04 21:32:48 UTC (rev 239636)
@@ -1,3 +1,50 @@
+2019-01-04  Jer Noble  <jer.no...@apple.com>
+
+        Web Content process main thread blocked beneath ImageDecoderAVFObjC::readSamples for many seconds on imgur.com
+        https://bugs.webkit.org/show_bug.cgi?id=191806
+        <rdar://problem/46151477>
+
+        Reviewed by Dean Jackson.
+
+        Test: http/tests/images/mp4-partial-load.html
+
+        Rather than use an AVAssetReaderTrackOutput, which will load both sample metadata and sample data
+        synchronously when a sample is requested, use AVAssetReaderSampleReferenceOutput, which only loads
+        sample metadata, including the byte offset and byte length of the sample data. By waiting until the
+        AVAsset signals that it's own metadata is loaded, we can safely parse all the sample metadata without
+        blocking on network loads. Once enough data is loaded, we can replace the byte reference and offset
+        attachements in the sample with actual data, and mark the sample as "complete".
+
+        Because the existing ImageSource assumes that image data parsing will occur synchronously, and that
+        synchronous parsing could cause a hang if the metadata is not loaded, add a new callback method which
+        allows the ImageSource to be notified when the encodedDataStatus changes. The ImageSource notifies the
+        CacheImage, which notifies the RenderImage, and thus the asynchronous parsing will kick off the
+        renderer's animation loop.
+
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::CachedImageObserver::encodedDataStatusChanged):
+        (WebCore::CachedImage::encodedDataStatusChanged):
+        * loader/cache/CachedImage.h:
+        * platform/graphics/ImageDecoder.h:
+        (WebCore::ImageDecoder::setEncodedDataStatusChangeCallback):
+        * platform/graphics/ImageObserver.h:
+        (WebCore::ImageObserver::encodedDataStatusChanged):
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::ensureDecoderAvailable):
+        (WebCore::ImageSource::encodedDataStatusChanged):
+        (WebCore::ImageSource::frameDecodingStatusAtIndex):
+        * platform/graphics/ImageSource.h:
+        * platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.h:
+        * platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.mm:
+        (-[WebCoreSharedBufferResourceLoaderDelegate data]):
+        (WebCore::ImageDecoderAVFObjCSample::byteRange const):
+        (WebCore::ImageDecoderAVFObjC::readSamples):
+        (WebCore::ImageDecoderAVFObjC::setEncodedDataStatusChangeCallback):
+        (WebCore::ImageDecoderAVFObjC::encodedDataStatus const):
+        (WebCore::ImageDecoderAVFObjC::frameIsCompleteAtIndex const):
+        (WebCore::ImageDecoderAVFObjC::createFrameImageAtIndex):
+        (WebCore::ImageDecoderAVFObjC::sampleIsComplete const):
+
 2019-01-04  Youenn Fablet  <you...@apple.com>
 
         CSP violation reports should bypass CSP checks

Modified: trunk/Source/WebCore/PAL/ChangeLog (239635 => 239636)


--- trunk/Source/WebCore/PAL/ChangeLog	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/PAL/ChangeLog	2019-01-04 21:32:48 UTC (rev 239636)
@@ -1,3 +1,14 @@
+2019-01-04  Jer Noble  <jer.no...@apple.com>
+
+        Web Content process main thread blocked beneath ImageDecoderAVFObjC::readSamples for many seconds on imgur.com
+        https://bugs.webkit.org/show_bug.cgi?id=191806
+        <rdar://problem/46151477>
+
+        Reviewed by Dean Jackson.
+
+        * pal/cf/CoreMediaSoftLink.cpp:
+        * pal/cf/CoreMediaSoftLink.h:
+
 2018-12-27  Alex Christensen  <achristen...@webkit.org>
 
         Resurrect Mac CMake build

Modified: trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.cpp (239635 => 239636)


--- trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.cpp	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.cpp	2019-01-04 21:32:48 UTC (rev 239636)
@@ -108,7 +108,10 @@
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferGetSampleTimingInfoArray, OSStatus, (CMSampleBufferRef sbuf, CMItemCount timingArrayEntries, CMSampleTimingInfo *timingArrayOut, CMItemCount *timingArrayEntriesNeededOut), (sbuf, timingArrayEntries, timingArrayOut, timingArrayEntriesNeededOut))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMTimeConvertScale, CMTime, (CMTime time, int32_t newTimescale, CMTimeRoundingMethod method), (time, newTimescale, method))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferGetTotalSampleSize, size_t, (CMSampleBufferRef sbuf), (sbuf))
+SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferSetDataBuffer, OSStatus, (CMSampleBufferRef sbuf, CMBlockBufferRef buffer), (sbuf, buffer))
+SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMGetAttachment, CFTypeRef, (CMAttachmentBearerRef target, CFStringRef key, CMAttachmentMode* attachmentModeOut), (target, key, attachmentModeOut))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSetAttachment, void, (CMAttachmentBearerRef target, CFStringRef key, CFTypeRef value, CMAttachmentMode attachmentMode), (target, key, value, attachmentMode))
+SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMRemoveAttachment, void, (CMAttachmentBearerRef target, CFStringRef key), (target, key))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMTimebaseCreateWithMasterClock, OSStatus, (CFAllocatorRef allocator, CMClockRef masterClock, CMTimebaseRef *timebaseOut), (allocator, masterClock, timebaseOut))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMTimebaseGetTime, CMTime, (CMTimebaseRef timebase), (timebase))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMTimebaseGetRate, Float64, (CMTimebaseRef timebase), (timebase))
@@ -141,6 +144,8 @@
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferAttachmentKey_EmptyMedia, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferAttachmentKey_PostNotificationWhenConsumed, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferAttachmentKey_ResetDecoderBeforeDecoding, CFStringRef)
+SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferAttachmentKey_SampleReferenceByteOffset, CFStringRef)
+SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferAttachmentKey_SampleReferenceURL, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleAttachmentKey_DisplayImmediately, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleAttachmentKey_IsDependedOnByOthers, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMSampleBufferConsumerNotification_BufferConsumed, CFStringRef)
@@ -148,6 +153,7 @@
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMTimebaseNotification_EffectiveRateChanged, CFStringRef)
 SOFT_LINK_CONSTANT_FOR_SOURCE(PAL, CoreMedia, kCMTimebaseNotification_TimeJumped, CFStringRef)
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMAudioFormatDescriptionGetStreamBasicDescription, const AudioStreamBasicDescription *, (CMAudioFormatDescriptionRef desc), (desc))
+SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMBlockBufferCreateWithMemoryBlock, OSStatus, (CFAllocatorRef structureAllocator, void* memoryBlock, size_t blockLength, CFAllocatorRef blockAllocator, const CMBlockBufferCustomBlockSource* customBlockSource, size_t offsetToData, size_t dataLength, CMBlockBufferFlags flags, CMBlockBufferRef* blockBufferOut), (structureAllocator, memoryBlock, blockLength, blockAllocator, customBlockSource, offsetToData, dataLength, flags, blockBufferOut))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer, OSStatus, (CMSampleBufferRef sbuf, size_t *bufferListSizeNeededOut, AudioBufferList *bufferListOut, size_t bufferListSize, CFAllocatorRef bbufStructAllocator, CFAllocatorRef bbufMemoryAllocator, uint32_t flags, CMBlockBufferRef *blockBufferOut), (sbuf, bufferListSizeNeededOut, bufferListOut, bufferListSize, bbufStructAllocator, bbufMemoryAllocator, flags, blockBufferOut))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferGetNumSamples, CMItemCount, (CMSampleBufferRef sbuf), (sbuf))
 SOFT_LINK_FUNCTION_FOR_SOURCE(PAL, CoreMedia, CMSampleBufferCopySampleBufferForRange, OSStatus, (CFAllocatorRef allocator, CMSampleBufferRef sbuf, CFRange sampleRange, CMSampleBufferRef* sBufOut), (allocator, sbuf, sampleRange, sBufOut))

Modified: trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.h (239635 => 239636)


--- trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/PAL/pal/cf/CoreMediaSoftLink.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -171,8 +171,14 @@
 #define CMSampleBufferGetSampleTimingInfoArray softLink_CoreMedia_CMSampleBufferGetSampleTimingInfoArray
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMSampleBufferGetTotalSampleSize, size_t, (CMSampleBufferRef sbuf), (sbuf))
 #define CMSampleBufferGetTotalSampleSize softLink_CoreMedia_CMSampleBufferGetTotalSampleSize
+SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMSampleBufferSetDataBuffer, OSStatus, (CMSampleBufferRef sbuf, CMBlockBufferRef buffer), (sbuf, buffer))
+#define CMSampleBufferSetDataBuffer softLink_CoreMedia_CMSampleBufferSetDataBuffer
+SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMGetAttachment, CFTypeRef, (CMAttachmentBearerRef target, CFStringRef key, CMAttachmentMode* attachmentModeOut), (target, key, attachmentModeOut))
+#define CMGetAttachment softLink_CoreMedia_CMGetAttachment
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMSetAttachment, void, (CMAttachmentBearerRef target, CFStringRef key, CFTypeRef value, CMAttachmentMode attachmentMode), (target, key, value, attachmentMode))
 #define CMSetAttachment softLink_CoreMedia_CMSetAttachment
+SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMRemoveAttachment, void, (CMAttachmentBearerRef target, CFStringRef key), (target, key))
+#define CMRemoveAttachment softLink_CoreMedia_CMRemoveAttachment
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMTimebaseCreateWithMasterClock, OSStatus, (CFAllocatorRef allocator, CMClockRef masterClock, CMTimebaseRef *timebaseOut), (allocator, masterClock, timebaseOut))
 #define CMTimebaseCreateWithMasterClock softLink_CoreMedia_CMTimebaseCreateWithMasterClock
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMTimebaseGetTime, CMTime, (CMTimebaseRef timebase), (timebase))
@@ -240,6 +246,10 @@
 #define kCMSampleBufferAttachmentKey_PostNotificationWhenConsumed get_CoreMedia_kCMSampleBufferAttachmentKey_PostNotificationWhenConsumed()
 SOFT_LINK_CONSTANT_FOR_HEADER(PAL, CoreMedia, kCMSampleBufferAttachmentKey_ResetDecoderBeforeDecoding, CFStringRef)
 #define kCMSampleBufferAttachmentKey_ResetDecoderBeforeDecoding get_CoreMedia_kCMSampleBufferAttachmentKey_ResetDecoderBeforeDecoding()
+SOFT_LINK_CONSTANT_FOR_HEADER(PAL, CoreMedia, kCMSampleBufferAttachmentKey_SampleReferenceByteOffset, CFStringRef)
+#define kCMSampleBufferAttachmentKey_SampleReferenceByteOffset get_CoreMedia_kCMSampleBufferAttachmentKey_SampleReferenceByteOffset()
+SOFT_LINK_CONSTANT_FOR_HEADER(PAL, CoreMedia, kCMSampleBufferAttachmentKey_SampleReferenceURL, CFStringRef)
+#define kCMSampleBufferAttachmentKey_SampleReferenceURL get_CoreMedia_kCMSampleBufferAttachmentKey_SampleReferenceURL()
 SOFT_LINK_CONSTANT_FOR_HEADER(PAL, CoreMedia, kCMTimebaseNotification_EffectiveRateChanged, CFStringRef)
 #define kCMTimebaseNotification_EffectiveRateChanged get_CoreMedia_kCMTimebaseNotification_EffectiveRateChanged()
 SOFT_LINK_CONSTANT_FOR_HEADER(PAL, CoreMedia, kCMTimebaseNotification_TimeJumped, CFStringRef)
@@ -248,6 +258,8 @@
 #define kCMSampleBufferConsumerNotification_BufferConsumed get_CoreMedia_kCMSampleBufferConsumerNotification_BufferConsumed()
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMAudioFormatDescriptionGetStreamBasicDescription, const AudioStreamBasicDescription *, (CMAudioFormatDescriptionRef desc), (desc))
 #define CMAudioFormatDescriptionGetStreamBasicDescription softLink_CoreMedia_CMAudioFormatDescriptionGetStreamBasicDescription
+SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMBlockBufferCreateWithMemoryBlock, OSStatus, (CFAllocatorRef structureAllocator, void* memoryBlock, size_t blockLength, CFAllocatorRef blockAllocator, const CMBlockBufferCustomBlockSource* customBlockSource, size_t offsetToData, size_t dataLength, CMBlockBufferFlags flags, CMBlockBufferRef* blockBufferOut), (structureAllocator, memoryBlock, blockLength, blockAllocator, customBlockSource, offsetToData, dataLength, flags, blockBufferOut))
+#define CMBlockBufferCreateWithMemoryBlock softLink_CoreMedia_CMBlockBufferCreateWithMemoryBlock
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer, OSStatus, (CMSampleBufferRef sbuf, size_t *bufferListSizeNeededOut, AudioBufferList *bufferListOut, size_t bufferListSize, CFAllocatorRef bbufStructAllocator, CFAllocatorRef bbufMemoryAllocator, uint32_t flags, CMBlockBufferRef *blockBufferOut), (sbuf, bufferListSizeNeededOut, bufferListOut, bufferListSize, bbufStructAllocator, bbufMemoryAllocator, flags, blockBufferOut))
 #define CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer softLink_CoreMedia_CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer
 SOFT_LINK_FUNCTION_FOR_HEADER(PAL, CoreMedia, CMSampleBufferGetNumSamples, CMItemCount, (CMSampleBufferRef sbuf), (sbuf))

Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (239635 => 239636)


--- trunk/Source/WebCore/loader/cache/CachedImage.cpp	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp	2019-01-04 21:32:48 UTC (rev 239636)
@@ -371,6 +371,12 @@
     m_cachedImages.add(&image);
 }
 
+void CachedImage::CachedImageObserver::encodedDataStatusChanged(const Image& image, EncodedDataStatus status)
+{
+    for (auto cachedImage : m_cachedImages)
+        cachedImage->encodedDataStatusChanged(image, status);
+}
+
 void CachedImage::CachedImageObserver::decodedSizeChanged(const Image& image, long long delta)
 {
     for (auto cachedImage : m_cachedImages)
@@ -582,6 +588,14 @@
         m_image->destroyDecodedData();
 }
 
+void CachedImage::encodedDataStatusChanged(const Image& image, EncodedDataStatus)
+{
+    if (&image != m_image)
+        return;
+
+    notifyObservers();
+}
+
 void CachedImage::decodedSizeChanged(const Image& image, long long delta)
 {
     if (&image != m_image)

Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (239635 => 239636)


--- trunk/Source/WebCore/loader/cache/CachedImage.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -143,6 +143,7 @@
         String mimeType() const override { return !m_cachedImages.isEmpty() ? (*m_cachedImages.begin())->mimeType() : emptyString(); }
         long long expectedContentLength() const override { return !m_cachedImages.isEmpty() ? (*m_cachedImages.begin())->expectedContentLength() : 0; }
 
+        void encodedDataStatusChanged(const Image&, EncodedDataStatus) final;
         void decodedSizeChanged(const Image&, long long delta) final;
         void didDraw(const Image&) final;
 
@@ -153,6 +154,7 @@
         HashSet<CachedImage*> m_cachedImages;
     };
 
+    void encodedDataStatusChanged(const Image&, EncodedDataStatus);
     void decodedSizeChanged(const Image&, long long delta);
     void didDraw(const Image&);
     bool canDestroyDecodedData(const Image&);

Modified: trunk/Source/WebCore/platform/graphics/ImageDecoder.h (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/ImageDecoder.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/ImageDecoder.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -55,6 +55,7 @@
     virtual size_t bytesDecodedToDetermineProperties() const = 0;
 
     virtual EncodedDataStatus encodedDataStatus() const = 0;
+    virtual void setEncodedDataStatusChangeCallback(WTF::Function<void(EncodedDataStatus)>&&) { }
     virtual bool isSizeAvailable() const { return encodedDataStatus() >= EncodedDataStatus::SizeAvailable; }
     virtual IntSize size() const = 0;
     virtual size_t frameCount() const = 0;

Modified: trunk/Source/WebCore/platform/graphics/ImageObserver.h (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/ImageObserver.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/ImageObserver.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -42,6 +42,7 @@
     virtual String mimeType() const = 0;
     virtual long long expectedContentLength() const = 0;
 
+    virtual void encodedDataStatusChanged(const Image&, EncodedDataStatus) { };
     virtual void decodedSizeChanged(const Image&, long long delta) = 0;
 
     virtual void didDraw(const Image&) = 0;

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.cpp (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2019-01-04 21:32:48 UTC (rev 239636)
@@ -76,6 +76,10 @@
         return true;
 
     m_decoder = ImageDecoder::create(*data, mimeType(), m_alphaOption, m_gammaAndColorProfileOption);
+    m_decoder->setEncodedDataStatusChangeCallback([weakThis = makeWeakPtr(this)] (auto status) {
+        if (weakThis)
+            weakThis->encodedDataStatusChanged(status);
+    });
     if (!isDecoderAvailable())
         return false;
 
@@ -154,6 +158,20 @@
     m_decoder->clearFrameBufferCache(beforeFrame);
 }
 
+void ImageSource::encodedDataStatusChanged(EncodedDataStatus status)
+{
+    if (status == m_encodedDataStatus)
+        return;
+
+    m_encodedDataStatus = status;
+
+    if (status >= EncodedDataStatus::SizeAvailable)
+        growFrames();
+
+    if (m_image && m_image->imageObserver())
+        m_image->imageObserver()->encodedDataStatusChanged(*m_image, status);
+}
+
 void ImageSource::decodedSizeChanged(long long decodedSize)
 {
     if (!decodedSize || !m_image || !m_image->imageObserver())
@@ -595,7 +613,7 @@
 
 DecodingStatus ImageSource::frameDecodingStatusAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<DecodingStatus>(index, (&ImageFrame::decodingStatus));
+    return frameMetadataAtIndexCacheIfNeeded<DecodingStatus>(index, (&ImageFrame::decodingStatus), nullptr, ImageFrame::Caching::Metadata);
 }
 
 bool ImageSource::frameHasAlphaAtIndex(size_t index)

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.h (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/ImageSource.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -30,6 +30,7 @@
 #include <wtf/Forward.h>
 #include <wtf/Optional.h>
 #include <wtf/SynchronizedFixedQueue.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/WorkQueue.h>
 #include <wtf/text/TextStream.h>
 
@@ -39,7 +40,7 @@
 class GraphicsContext;
 class ImageDecoder;
 
-class ImageSource : public ThreadSafeRefCounted<ImageSource> {
+class ImageSource : public ThreadSafeRefCounted<ImageSource>, public CanMakeWeakPtr<ImageSource> {
     friend class BitmapImage;
 public:
     ~ImageSource();
@@ -142,6 +143,7 @@
     void decodedSizeIncreased(unsigned decodedSize);
     void decodedSizeDecreased(unsigned decodedSize);
     void decodedSizeReset(unsigned decodedSize);
+    void encodedDataStatusChanged(EncodedDataStatus);
 
     void setNativeImage(NativeImagePtr&&);
     void cacheMetadataAtIndex(size_t, SubsamplingLevel, DecodingStatus = DecodingStatus::Invalid);

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.h (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.h	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.h	2019-01-04 21:32:48 UTC (rev 239636)
@@ -35,7 +35,6 @@
 
 OBJC_CLASS AVAssetTrack;
 OBJC_CLASS AVAssetReader;
-OBJC_CLASS AVAssetReaderTrackOutput;
 OBJC_CLASS AVURLAsset;
 OBJC_CLASS WebCoreSharedBufferResourceLoaderDelegate;
 typedef struct opaqueCMSampleBuffer* CMSampleBufferRef;
@@ -66,6 +65,7 @@
 
     const String& mimeType() const { return m_mimeType; }
 
+    void setEncodedDataStatusChangeCallback(WTF::Function<void(EncodedDataStatus)>&&) final;
     EncodedDataStatus encodedDataStatus() const final;
     IntSize size() const final;
     size_t frameCount() const final;
@@ -109,6 +109,7 @@
     void setTrack(AVAssetTrack *);
 
     const ImageDecoderAVFObjCSample* sampleAtIndex(size_t) const;
+    bool sampleIsComplete(const ImageDecoderAVFObjCSample&) const;
 
     String m_mimeType;
     String m_uti;
@@ -118,6 +119,7 @@
     RetainPtr<VTImageRotationSessionRef> m_rotationSession;
     RetainPtr<CVPixelBufferPoolRef> m_rotationPool;
     Ref<WebCoreDecompressionSession> m_decompressionSession;
+    WTF::Function<void(EncodedDataStatus)> m_encodedDataStatusChangedCallback;
 
     SampleMap m_sampleData;
     DecodeOrderSampleMap::iterator m_cursor;

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.mm (239635 => 239636)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.mm	2019-01-04 21:27:34 UTC (rev 239635)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/ImageDecoderAVFObjC.mm	2019-01-04 21:32:48 UTC (rev 239636)
@@ -51,6 +51,7 @@
 #import <wtf/MainThread.h>
 #import <wtf/MediaTime.h>
 #import <wtf/NeverDestroyed.h>
+#import <wtf/Optional.h>
 #import <wtf/SoftLinking.h>
 #import <wtf/Vector.h>
 
@@ -63,7 +64,7 @@
 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
 SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVURLAsset)
 SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVAssetReader)
-SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVAssetReaderTrackOutput)
+SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVAssetReaderSampleReferenceOutput)
 SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVMediaCharacteristicVisual, NSString *)
 SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
 SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVURLAssetUsesNoPersistentCacheKey, NSString *)
@@ -81,6 +82,7 @@
     Vector<RetainPtr<AVAssetResourceLoadingRequest>> _requests;
     Lock _dataLock;
 }
+@property (readonly) NSData* data;
 - (id)initWithParent:(WebCore::ImageDecoderAVFObjC*)parent;
 - (void)setExpectedContentSize:(long long)expectedContentSize;
 - (void)updateData:(NSData *)data complete:(BOOL)complete;
@@ -100,6 +102,11 @@
     return self;
 }
 
+- (NSData*)data
+{
+    return _data.get();
+}
+
 - (void)setExpectedContentSize:(long long)expectedContentSize
 {
     LockHolder holder { _dataLock };
@@ -285,6 +292,36 @@
         m_hasAlpha = alphaInfo != kCGImageAlphaNone && alphaInfo != kCGImageAlphaNoneSkipLast && alphaInfo != kCGImageAlphaNoneSkipFirst;
     }
 
+    struct ByteRange {
+        size_t byteOffset { 0 };
+        size_t byteLength { 0 };
+    };
+
+    Optional<ByteRange> byteRange() const
+    {
+        if (PAL::CMSampleBufferGetDataBuffer(m_sample.get())
+            || PAL::CMSampleBufferGetImageBuffer(m_sample.get())
+            || !PAL::CMSampleBufferDataIsReady(m_sample.get()))
+            return WTF::nullopt;
+
+        CFNumberRef byteOffsetCF = (CFNumberRef)PAL::CMGetAttachment(m_sample.get(), PAL::kCMSampleBufferAttachmentKey_SampleReferenceByteOffset, nullptr);
+        if (!byteOffsetCF || CFGetTypeID(byteOffsetCF) != CFNumberGetTypeID())
+            return WTF::nullopt;
+
+        int64_t byteOffset { 0 };
+        if (!CFNumberGetValue(byteOffsetCF, kCFNumberSInt64Type, &byteOffset))
+            return WTF::nullopt;
+
+        CMItemCount sizeArrayEntries = 0;
+        PAL::CMSampleBufferGetSampleSizeArray(m_sample.get(), 0, nullptr, &sizeArrayEntries);
+        if (sizeArrayEntries != 1)
+            return WTF::nullopt;
+
+        size_t singleSizeEntry;
+        PAL::CMSampleBufferGetSampleSizeArray(m_sample.get(), 1, &singleSizeEntry, nullptr);
+        return {{static_cast<size_t>(byteOffset), singleSizeEntry}};
+    }
+
     SampleFlags flags() const override
     {
         return (SampleFlags)(MediaSampleAVFObjC::flags() | (m_hasAlpha ? HasAlpha : 0));
@@ -387,13 +424,13 @@
         return;
 
     auto assetReader = adoptNS([allocAVAssetReaderInstance() initWithAsset:m_asset.get() error:nil]);
-    auto assetReaderOutput = adoptNS([allocAVAssetReaderTrackOutputInstance() initWithTrack:m_track.get() outputSettings:nil]);
+    auto referenceOutput = adoptNS([allocAVAssetReaderSampleReferenceOutputInstance() initWithTrack:m_track.get()]);
 
-    assetReaderOutput.get().alwaysCopiesSampleData = NO;
-    [assetReader addOutput:assetReaderOutput.get()];
+    referenceOutput.get().alwaysCopiesSampleData = NO;
+    [assetReader addOutput:referenceOutput.get()];
     [assetReader startReading];
 
-    while (auto sampleBuffer = adoptCF([assetReaderOutput copyNextSampleBuffer])) {
+    while (auto sampleBuffer = adoptCF([referenceOutput copyNextSampleBuffer])) {
         // NOTE: Some samples emitted by the AVAssetReader simply denote the boundary of edits
         // and do not carry media data.
         if (!(PAL::CMSampleBufferGetNumSamples(sampleBuffer.get())))
@@ -400,6 +437,9 @@
             continue;
         m_sampleData.addSample(ImageDecoderAVFObjCSample::create(WTFMove(sampleBuffer)).get());
     }
+
+    if (m_encodedDataStatusChangedCallback)
+        m_encodedDataStatusChangedCallback(encodedDataStatus());
 }
 
 void ImageDecoderAVFObjC::readTrackMetadata()
@@ -500,11 +540,20 @@
     }];
 }
 
+void ImageDecoderAVFObjC::setEncodedDataStatusChangeCallback(WTF::Function<void(EncodedDataStatus)>&& callback)
+{
+    m_encodedDataStatusChangedCallback = WTFMove(callback);
+}
+
 EncodedDataStatus ImageDecoderAVFObjC::encodedDataStatus() const
 {
-    if (m_sampleData.empty())
-        return EncodedDataStatus::Unknown;
-    return EncodedDataStatus::Complete;
+    if (!m_sampleData.empty())
+        return EncodedDataStatus::Complete;
+    if (m_size)
+        return EncodedDataStatus::SizeAvailable;
+    if (m_track)
+        return EncodedDataStatus::TypeAvailable;
+    return EncodedDataStatus::Unknown;
 }
 
 IntSize ImageDecoderAVFObjC::size() const
@@ -548,7 +597,7 @@
     if (!sampleData)
         return false;
 
-    return PAL::CMSampleBufferDataIsReady(sampleData->sampleBuffer());
+    return sampleIsComplete(*sampleData);
 }
 
 ImageOrientation ImageDecoderAVFObjC::frameOrientationAtIndex(size_t) const
@@ -615,11 +664,44 @@
         if (decodeTime < m_cursor->second->decodeTime())
             return nullptr;
 
-        auto cursorSample = toSample(m_cursor)->sampleBuffer();
+        auto cursorSample = toSample(m_cursor);
         if (!cursorSample)
+            return nullptr;
+
+        if (!sampleIsComplete(*cursorSample))
+            return nullptr;
+
+        if (auto byteRange = cursorSample->byteRange()) {
+            auto& byteRangeValue = byteRange.value();
+            auto* data = ""
+            CMBlockBufferCustomBlockSource source {
+                0,
+                nullptr,
+                [](void* refcon, void*, size_t) {
+                    [(id)refcon release];
+                },
+                [data retain]
+            };
+            CMBlockBufferRef rawBlockBuffer = nullptr;
+            if (noErr != PAL::CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, const_cast<void*>(data.bytes), data.length, nullptr, &source, byteRangeValue.byteOffset, byteRangeValue.byteLength, 0, &rawBlockBuffer))
+                return nullptr;
+
+            if (!rawBlockBuffer)
+                return nullptr;
+
+            if (noErr != PAL::CMSampleBufferSetDataBuffer(cursorSample->sampleBuffer(), rawBlockBuffer))
+                return nullptr;
+            CFRelease(rawBlockBuffer);
+
+            PAL::CMRemoveAttachment(cursorSample->sampleBuffer(), PAL::kCMSampleBufferAttachmentKey_SampleReferenceByteOffset);
+            PAL::CMRemoveAttachment(cursorSample->sampleBuffer(), PAL::kCMSampleBufferAttachmentKey_SampleReferenceURL);
+        }
+
+        auto cursorSampleBuffer = cursorSample->sampleBuffer();
+        if (!cursorSampleBuffer)
             break;
 
-        if (!storeSampleBuffer(cursorSample))
+        if (!storeSampleBuffer(cursorSampleBuffer))
             break;
 
         advanceCursor();
@@ -679,6 +761,16 @@
     return toSample(iter);
 }
 
+bool ImageDecoderAVFObjC::sampleIsComplete(const ImageDecoderAVFObjCSample& sample) const
+{
+    if (auto byteRange = sample.byteRange()) {
+        auto& byteRangeValue = byteRange.value();
+        return byteRangeValue.byteOffset + byteRangeValue.byteLength <= m_loader.get().data.length;
+    }
+
+    return PAL::CMSampleBufferDataIsReady(sample.sampleBuffer());
 }
 
+}
+
 #endif
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to