Title: [261922] trunk
Revision
261922
Author
[email protected]
Date
2020-05-20 08:52:36 -0700 (Wed, 20 May 2020)

Log Message

[GStreamer] <img> tag needs to support video formats
https://bugs.webkit.org/show_bug.cgi?id=180370

Patch by Philippe Normand <[email protected]> on 2020-05-20
Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

GStreamer implementation of the ImageDecoder. It currently doesn't support zero-copy
rendering though due to the the NativeImagePtr requirement.

* platform/GStreamer.cmake:
* platform/MIMETypeRegistry.cpp:
(WebCore::MIMETypeRegistry::isSupportedImageVideoOrSVGMIMEType):
* platform/graphics/ImageDecoder.cpp:
(WebCore::ImageDecoder::create):
(WebCore::ImageDecoder::supportsMediaType):
* platform/graphics/gstreamer/ImageDecoderGStreamer.cpp: Added.
(WebCore::toSample):
(WebCore::ImageDecoderGStreamer::create):
(WebCore::ImageDecoderGStreamer::ImageDecoderGStreamer):
(WebCore::ImageDecoderGStreamer::supportsContainerType):
(WebCore::ImageDecoderGStreamer::canDecodeType):
(WebCore::ImageDecoderGStreamer::encodedDataStatus const):
(WebCore::ImageDecoderGStreamer::size const):
(WebCore::ImageDecoderGStreamer::repetitionCount const):
(WebCore::ImageDecoderGStreamer::uti const):
(WebCore::ImageDecoderGStreamer::frameOrientationAtIndex const):
(WebCore::ImageDecoderGStreamer::frameDurationAtIndex const):
(WebCore::ImageDecoderGStreamer::frameHasAlphaAtIndex const):
(WebCore::ImageDecoderGStreamer::frameBytesAtIndex const):
(WebCore::ImageDecoderGStreamer::createFrameImageAtIndex):
(WebCore::ImageDecoderGStreamer::setData):
(WebCore::ImageDecoderGStreamer::clearFrameBufferCache):
(WebCore::ImageDecoderGStreamer::sampleAtIndex const):
(WebCore::ImageDecoderGStreamer::InnerDecoder::decodebinPadAddedCallback):
(WebCore::ImageDecoderGStreamer::InnerDecoder::connectDecoderPad):
(WebCore::ImageDecoderGStreamer::handleSample):
(WebCore::ImageDecoderGStreamer::InnerDecoder::handleMessage):
(WebCore::ImageDecoderGStreamer::InnerDecoder::preparePipeline):
(WebCore::ImageDecoderGStreamer::InnerDecoder::run):
(WebCore::ImageDecoderGStreamer::InnerDecoder::encodedDataStatus const):
(WebCore::ImageDecoderGStreamer::pushEncodedData):
* platform/graphics/gstreamer/ImageDecoderGStreamer.h: Added.
* platform/graphics/gstreamer/ImageGStreamer.h:
(WebCore::ImageGStreamer::createImage):
(WebCore::ImageGStreamer::image):
(WebCore::ImageGStreamer::setCropRect):
(WebCore::ImageGStreamer::rect):
(WebCore::ImageGStreamer::hasAlpha const):
* platform/graphics/gstreamer/ImageGStreamerCairo.cpp:
(WebCore::ImageGStreamer::ImageGStreamer):
* platform/graphics/gstreamer/MediaSampleGStreamer.h:

LayoutTests:

Update TestExpectations for tests using videos for the <img> tag.

* http/conf/flatpak-httpd.conf:
* platform/glib/TestExpectations:
* platform/gtk/TestExpectations:
* platform/wpe/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261921 => 261922)


--- trunk/LayoutTests/ChangeLog	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/LayoutTests/ChangeLog	2020-05-20 15:52:36 UTC (rev 261922)
@@ -1,3 +1,17 @@
+2020-05-20  Philippe Normand  <[email protected]>
+
+        [GStreamer] <img> tag needs to support video formats
+        https://bugs.webkit.org/show_bug.cgi?id=180370
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Update TestExpectations for tests using videos for the <img> tag.
+        
+        * http/conf/flatpak-httpd.conf:
+        * platform/glib/TestExpectations:
+        * platform/gtk/TestExpectations:
+        * platform/wpe/TestExpectations:
+
 2020-05-20  Alan Kinsley  <[email protected]>
 
         [LFC][TFC] Internal table boxes should take collapsed border into account

Modified: trunk/LayoutTests/http/conf/flatpak-httpd.conf (261921 => 261922)


--- trunk/LayoutTests/http/conf/flatpak-httpd.conf	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/LayoutTests/http/conf/flatpak-httpd.conf	2020-05-20 15:52:36 UTC (rev 261922)
@@ -52,8 +52,6 @@
 UseCanonicalName On
 HostnameLookups Off
 
-TypesConfig /usr/conf/mime.types
-
 LogLevel warn
 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
 LogFormat "%h %l %u %t \"%r\" %>s %b" common

Modified: trunk/LayoutTests/platform/glib/TestExpectations (261921 => 261922)


--- trunk/LayoutTests/platform/glib/TestExpectations	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/LayoutTests/platform/glib/TestExpectations	2020-05-20 15:52:36 UTC (rev 261922)
@@ -74,6 +74,8 @@
 # 2. EXPECTED FAILURES
 #////////////////////////////////////////////////////////////////////////////////////////
 
+webkit.org/b/211995 fast/images/animated-image-mp4.html [ Failure ]
+
 #////////////////////////////////////////////////////////////////////////////////////////
 # End of Expected Failures
 #////////////////////////////////////////////////////////////////////////////////////////

Modified: trunk/LayoutTests/platform/gtk/TestExpectations (261921 => 261922)


--- trunk/LayoutTests/platform/gtk/TestExpectations	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/LayoutTests/platform/gtk/TestExpectations	2020-05-20 15:52:36 UTC (rev 261922)
@@ -1068,11 +1068,6 @@
 webkit.org/b/175419 imported/w3c/web-platform-tests/background-fetch/interfaces.html [ Failure ]
 webkit.org/b/175419 imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https.html [ Failure Pass ]
 
-# Loading MP4 as an image is not supported by GTK
-webkit.org/b/180370 fast/images/animated-image-mp4.html [ Skip ]
-webkit.org/b/180370 http/tests/images/image-supports-video.html [ Skip ]
-webkit.org/b/180370 fast/images/animated-image-mp4-crash.html [ Skip ]
-
 # GTK ought to support async clipboard
 webkit.org/b/211979 editing/async-clipboard/clipboard-change-data-while-writing.html [ Failure ]
 webkit.org/b/211979 editing/async-clipboard/clipboard-read-text-from-platform.html [ Failure ]

Modified: trunk/LayoutTests/platform/wpe/TestExpectations (261921 => 261922)


--- trunk/LayoutTests/platform/wpe/TestExpectations	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/LayoutTests/platform/wpe/TestExpectations	2020-05-20 15:52:36 UTC (rev 261922)
@@ -369,11 +369,6 @@
 
 Bug(WPE) fast/hidpi/pdf-image-scaled.html [ Skip ]
 
-# Loading MP4 as an image is not supported
-webkit.org/b/180370 fast/images/animated-image-mp4.html [ Skip ]
-webkit.org/b/180370 fast/images/animated-image-mp4-crash.html [ Skip ]
-webkit.org/b/180370 http/tests/images/image-supports-video.html [ Skip ]
-
 # Only Mac has implemented DictionaryLookup
 fast/layers/prevent-hit-test-during-layout.html [ Skip ]
 

Modified: trunk/Source/WebCore/ChangeLog (261921 => 261922)


--- trunk/Source/WebCore/ChangeLog	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/ChangeLog	2020-05-20 15:52:36 UTC (rev 261922)
@@ -1,3 +1,56 @@
+2020-05-20  Philippe Normand  <[email protected]>
+
+        [GStreamer] <img> tag needs to support video formats
+        https://bugs.webkit.org/show_bug.cgi?id=180370
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        GStreamer implementation of the ImageDecoder. It currently doesn't support zero-copy
+        rendering though due to the the NativeImagePtr requirement.
+
+        * platform/GStreamer.cmake:
+        * platform/MIMETypeRegistry.cpp:
+        (WebCore::MIMETypeRegistry::isSupportedImageVideoOrSVGMIMEType):
+        * platform/graphics/ImageDecoder.cpp:
+        (WebCore::ImageDecoder::create):
+        (WebCore::ImageDecoder::supportsMediaType):
+        * platform/graphics/gstreamer/ImageDecoderGStreamer.cpp: Added.
+        (WebCore::toSample):
+        (WebCore::ImageDecoderGStreamer::create):
+        (WebCore::ImageDecoderGStreamer::ImageDecoderGStreamer):
+        (WebCore::ImageDecoderGStreamer::supportsContainerType):
+        (WebCore::ImageDecoderGStreamer::canDecodeType):
+        (WebCore::ImageDecoderGStreamer::encodedDataStatus const):
+        (WebCore::ImageDecoderGStreamer::size const):
+        (WebCore::ImageDecoderGStreamer::repetitionCount const):
+        (WebCore::ImageDecoderGStreamer::uti const):
+        (WebCore::ImageDecoderGStreamer::frameOrientationAtIndex const):
+        (WebCore::ImageDecoderGStreamer::frameDurationAtIndex const):
+        (WebCore::ImageDecoderGStreamer::frameHasAlphaAtIndex const):
+        (WebCore::ImageDecoderGStreamer::frameBytesAtIndex const):
+        (WebCore::ImageDecoderGStreamer::createFrameImageAtIndex):
+        (WebCore::ImageDecoderGStreamer::setData):
+        (WebCore::ImageDecoderGStreamer::clearFrameBufferCache):
+        (WebCore::ImageDecoderGStreamer::sampleAtIndex const):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::decodebinPadAddedCallback):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::connectDecoderPad):
+        (WebCore::ImageDecoderGStreamer::handleSample):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::handleMessage):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::preparePipeline):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::run):
+        (WebCore::ImageDecoderGStreamer::InnerDecoder::encodedDataStatus const):
+        (WebCore::ImageDecoderGStreamer::pushEncodedData):
+        * platform/graphics/gstreamer/ImageDecoderGStreamer.h: Added.
+        * platform/graphics/gstreamer/ImageGStreamer.h:
+        (WebCore::ImageGStreamer::createImage):
+        (WebCore::ImageGStreamer::image):
+        (WebCore::ImageGStreamer::setCropRect):
+        (WebCore::ImageGStreamer::rect):
+        (WebCore::ImageGStreamer::hasAlpha const):
+        * platform/graphics/gstreamer/ImageGStreamerCairo.cpp:
+        (WebCore::ImageGStreamer::ImageGStreamer):
+        * platform/graphics/gstreamer/MediaSampleGStreamer.h:
+
 2020-05-20  Andy Estes  <[email protected]>
 
         [Mac] UI processes spin when creating the "Share" context menu item

Modified: trunk/Source/WebCore/platform/GStreamer.cmake (261921 => 261922)


--- trunk/Source/WebCore/platform/GStreamer.cmake	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/GStreamer.cmake	2020-05-20 15:52:36 UTC (rev 261922)
@@ -13,6 +13,7 @@
         platform/graphics/gstreamer/GstAllocatorFastMalloc.cpp
         platform/graphics/gstreamer/GStreamerRegistryScanner.cpp
         platform/graphics/gstreamer/GStreamerVideoFrameHolder.cpp
+        platform/graphics/gstreamer/ImageDecoderGStreamer.cpp
         platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp
         platform/graphics/gstreamer/MediaEngineConfigurationFactoryGStreamer.cpp
         platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp

Modified: trunk/Source/WebCore/platform/MIMETypeRegistry.cpp (261921 => 261922)


--- trunk/Source/WebCore/platform/MIMETypeRegistry.cpp	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/MIMETypeRegistry.cpp	2020-05-20 15:52:36 UTC (rev 261922)
@@ -58,6 +58,10 @@
 #include "PreviewConverter.h"
 #endif
 
+#if USE(GSTREAMER)
+#include "ImageDecoderGStreamer.h"
+#endif
+
 namespace WebCore {
 
 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::supportedImageMIMETypes()
@@ -436,6 +440,11 @@
         return true;
 #endif
 
+#if USE(GSTREAMER)
+    if (ImageDecoderGStreamer::supportsContainerType(mimeType))
+        return true;
+#endif
+
     return false;
 }
 

Modified: trunk/Source/WebCore/platform/graphics/ImageDecoder.cpp (261921 => 261922)


--- trunk/Source/WebCore/platform/graphics/ImageDecoder.cpp	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/graphics/ImageDecoder.cpp	2020-05-20 15:52:36 UTC (rev 261922)
@@ -38,6 +38,10 @@
 #include "ImageDecoderAVFObjC.h"
 #endif
 
+#if USE(GSTREAMER)
+#include "ImageDecoderGStreamer.h"
+#endif
+
 namespace WebCore {
 
 RefPtr<ImageDecoder> ImageDecoder::create(SharedBuffer& data, const String& mimeType, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
@@ -49,6 +53,11 @@
         return ImageDecoderAVFObjC::create(data, mimeType, alphaOption, gammaAndColorProfileOption);
 #endif
 
+#if USE(GSTREAMER)
+    if (ImageDecoderGStreamer::canDecodeType(mimeType))
+        return ImageDecoderGStreamer::create(data, mimeType, alphaOption, gammaAndColorProfileOption);
+#endif
+
 #if USE(CG)
     return ImageDecoderCG::create(data, alphaOption, gammaAndColorProfileOption);
 #elif USE(DIRECT2D)
@@ -76,6 +85,11 @@
         return true;
 #endif
 
+#if USE(GSTREAMER)
+    if (ImageDecoderGStreamer::supportsMediaType(type))
+        return true;
+#endif
+
     return false;
 }
 

Added: trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.cpp (0 => 261922)


--- trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.cpp	2020-05-20 15:52:36 UTC (rev 261922)
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ImageDecoderGStreamer.h"
+
+#if USE(GSTREAMER)
+
+#include "FloatSize.h"
+#include "GStreamerCommon.h"
+#include "GStreamerRegistryScanner.h"
+#include "ImageGStreamer.h"
+#include "MediaSampleGStreamer.h"
+#include "NotImplemented.h"
+#include <gst/app/gstappsink.h>
+#include <wtf/MainThread.h>
+#include <wtf/Optional.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+GST_DEBUG_CATEGORY(webkit_image_decoder_debug);
+#define GST_CAT_DEFAULT webkit_image_decoder_debug
+
+class ImageDecoderGStreamerSample final : public MediaSampleGStreamer {
+public:
+    static Ref<ImageDecoderGStreamerSample> create(GRefPtr<GstSample>&& sample, const FloatSize& presentationSize)
+    {
+        return adoptRef(*new ImageDecoderGStreamerSample(WTFMove(sample), presentationSize));
+    }
+
+    NativeImagePtr image() const
+    {
+        if (!m_image)
+            return nullptr;
+        return m_image->image().nativeImage();
+    }
+    void dropImage() { m_image = nullptr; }
+
+    SampleFlags flags() const override
+    {
+        return (SampleFlags)(MediaSampleGStreamer::flags() | (m_image && m_image->hasAlpha() ? HasAlpha : 0));
+    }
+
+private:
+    ImageDecoderGStreamerSample(GRefPtr<GstSample>&& sample, const FloatSize& presentationSize)
+        : MediaSampleGStreamer(WTFMove(sample), presentationSize, { })
+    {
+        m_image = ImageGStreamer::createImage(platformSample().sample.gstSample);
+    }
+
+    RefPtr<ImageGStreamer> m_image;
+};
+
+static ImageDecoderGStreamerSample* toSample(const PresentationOrderSampleMap::value_type& pair)
+{
+    return (ImageDecoderGStreamerSample*)pair.second.get();
+}
+
+template <typename Iterator>
+ImageDecoderGStreamerSample* toSample(Iterator iter)
+{
+    return (ImageDecoderGStreamerSample*)iter->second.get();
+}
+
+RefPtr<ImageDecoderGStreamer> ImageDecoderGStreamer::create(SharedBuffer& data, const String& mimeType, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
+{
+    return adoptRef(*new ImageDecoderGStreamer(data, mimeType, alphaOption, gammaAndColorProfileOption));
+}
+
+ImageDecoderGStreamer::ImageDecoderGStreamer(SharedBuffer& data, const String& mimeType, AlphaOption, GammaAndColorProfileOption)
+    : m_mimeType(mimeType)
+{
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [] {
+        GST_DEBUG_CATEGORY_INIT(webkit_image_decoder_debug, "webkitimagedecoder", 0, "WebKit image decoder");
+    });
+
+    pushEncodedData(data);
+}
+
+bool ImageDecoderGStreamer::supportsContainerType(const String& type)
+{
+    return GStreamerRegistryScanner::singleton().isContainerTypeSupported(type);
+}
+
+bool ImageDecoderGStreamer::canDecodeType(const String& mimeType)
+{
+    if (mimeType.isEmpty())
+        return false;
+
+    return GStreamerRegistryScanner::singleton().isContainerTypeSupported(mimeType);
+}
+
+EncodedDataStatus ImageDecoderGStreamer::encodedDataStatus() const
+{
+    if (m_eos)
+        return EncodedDataStatus::Complete;
+    if (m_size)
+        return EncodedDataStatus::SizeAvailable;
+    if (m_innerDecoder)
+        return m_innerDecoder->encodedDataStatus();
+    return EncodedDataStatus::Unknown;
+}
+
+IntSize ImageDecoderGStreamer::size() const
+{
+    if (m_size)
+        return m_size.value();
+    return { };
+}
+
+RepetitionCount ImageDecoderGStreamer::repetitionCount() const
+{
+    // In the absence of instructions to the contrary, assume all media formats repeat infinitely.
+    return frameCount() > 1 ? RepetitionCountInfinite : RepetitionCountNone;
+}
+
+String ImageDecoderGStreamer::uti() const
+{
+    notImplemented();
+    return { };
+}
+
+ImageOrientation ImageDecoderGStreamer::frameOrientationAtIndex(size_t) const
+{
+    notImplemented();
+    return ImageOrientation::None;
+}
+
+Seconds ImageDecoderGStreamer::frameDurationAtIndex(size_t index) const
+{
+    auto* sampleData = sampleAtIndex(index);
+    if (!sampleData)
+        return { };
+
+    return Seconds(sampleData->duration().toDouble());
+}
+
+bool ImageDecoderGStreamer::frameHasAlphaAtIndex(size_t index) const
+{
+    auto* sampleData = sampleAtIndex(index);
+    return sampleData ? sampleData->hasAlpha() : false;
+}
+
+unsigned ImageDecoderGStreamer::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
+{
+    if (!frameIsCompleteAtIndex(index))
+        return 0;
+
+    IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel);
+    return (frameSize.area() * 4).unsafeGet();
+}
+
+NativeImagePtr ImageDecoderGStreamer::createFrameImageAtIndex(size_t index, SubsamplingLevel, const DecodingOptions&)
+{
+    LockHolder holder { m_sampleGeneratorLock };
+
+    auto* sampleData = sampleAtIndex(index);
+    if (!sampleData)
+        return nullptr;
+
+    if (auto image = sampleData->image())
+        return image;
+
+    return nullptr;
+}
+
+void ImageDecoderGStreamer::setData(SharedBuffer& data, bool)
+{
+    pushEncodedData(data);
+}
+
+void ImageDecoderGStreamer::clearFrameBufferCache(size_t index)
+{
+    size_t i = 0;
+    for (auto& samplePair : m_sampleData.presentationOrder()) {
+        toSample(samplePair)->dropImage();
+        if (++i > index)
+            break;
+    }
+}
+
+const ImageDecoderGStreamerSample* ImageDecoderGStreamer::sampleAtIndex(size_t index) const
+{
+    if (index >= m_sampleData.presentationOrder().size())
+        return nullptr;
+
+    // FIXME: std::map is not random-accessible; this can get expensive if callers repeatedly call
+    // with monotonically increasing indexes. Investigate adding an O(1) side structure to make this
+    // style of access faster.
+    auto iter = m_sampleData.presentationOrder().begin();
+    for (size_t i = 0; i != index; ++i)
+        ++iter;
+
+    return toSample(iter);
+}
+
+void ImageDecoderGStreamer::InnerDecoder::decodebinPadAddedCallback(ImageDecoderGStreamer::InnerDecoder* decoder, GstPad* pad)
+{
+    decoder->connectDecoderPad(pad);
+}
+
+void ImageDecoderGStreamer::InnerDecoder::connectDecoderPad(GstPad* pad)
+{
+    auto padCaps = adoptGRef(gst_pad_get_current_caps(pad));
+    GST_DEBUG_OBJECT(m_pipeline.get(), "New decodebin pad %" GST_PTR_FORMAT " caps: %" GST_PTR_FORMAT, pad, padCaps.get());
+    RELEASE_ASSERT(doCapsHaveType(padCaps.get(), "video"));
+
+    GstElement* sink = gst_element_factory_make("appsink", nullptr);
+    static GstAppSinkCallbacks callbacks = {
+        nullptr,
+        [](GstAppSink* sink, gpointer userData) -> GstFlowReturn {
+            auto sample = adoptGRef(gst_app_sink_try_pull_preroll(sink, 0));
+            static_cast<ImageDecoderGStreamer*>(userData)->handleSample(WTFMove(sample));
+            return GST_FLOW_OK;
+        },
+        [](GstAppSink* sink, gpointer userData) -> GstFlowReturn {
+            auto sample = adoptGRef(gst_app_sink_try_pull_sample(sink, 0));
+            static_cast<ImageDecoderGStreamer*>(userData)->handleSample(WTFMove(sample));
+            return GST_FLOW_OK;
+        },
+        { nullptr }
+    };
+    gst_app_sink_set_callbacks(GST_APP_SINK(sink), &callbacks, m_decoder.get(), nullptr);
+
+    GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string("video/x-raw, format=(string)RGBA"));
+    g_object_set(sink, "sync", false, "caps", caps.get(), nullptr);
+
+    GstElement* videoconvert = gst_element_factory_make("videoconvert", nullptr);
+
+    gst_bin_add_many(GST_BIN_CAST(m_pipeline.get()), videoconvert, sink, nullptr);
+    gst_element_link(videoconvert, sink);
+    auto sinkPad = adoptGRef(gst_element_get_static_pad(videoconvert, "sink"));
+    gst_pad_link(pad, sinkPad.get());
+    gst_element_sync_state_with_parent(videoconvert);
+    gst_element_sync_state_with_parent(sink);
+}
+
+void ImageDecoderGStreamer::handleSample(GRefPtr<GstSample>&& sample)
+{
+    auto* caps = gst_sample_get_caps(sample.get());
+    GST_DEBUG("Handling sample with caps %" GST_PTR_FORMAT, caps);
+    auto presentationSize = getVideoResolutionFromCaps(caps);
+    if (presentationSize && !presentationSize->isEmpty() && (!m_size || m_size != roundedIntSize(*presentationSize)))
+        m_size = roundedIntSize(*presentationSize);
+    m_sampleData.addSample(ImageDecoderGStreamerSample::create(WTFMove(sample), *m_size));
+}
+
+void ImageDecoderGStreamer::InnerDecoder::handleMessage(GstMessage* message)
+{
+    ASSERT(&m_runLoop == &RunLoop::current());
+
+    GUniqueOutPtr<GError> error;
+    GUniqueOutPtr<gchar> debug;
+
+    switch (GST_MESSAGE_TYPE(message)) {
+    case GST_MESSAGE_EOS:
+        gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
+        m_runLoop.stop();
+        break;
+    case GST_MESSAGE_WARNING:
+        gst_message_parse_warning(message, &error.outPtr(), &debug.outPtr());
+        g_warning("Warning: %d, %s. Debug output: %s", error->code, error->message, debug.get());
+        break;
+    case GST_MESSAGE_ERROR:
+        gst_message_parse_error(message, &error.outPtr(), &debug.outPtr());
+        g_warning("Error: %d, %s. Debug output: %s", error->code, error->message, debug.get());
+        gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
+        m_runLoop.stop();
+        break;
+    default:
+        break;
+    }
+}
+
+void ImageDecoderGStreamer::InnerDecoder::preparePipeline()
+{
+    static Atomic<uint32_t> pipelineId;
+    m_pipeline = gst_pipeline_new(makeString("image-decoder-", pipelineId.exchangeAdd(1)).utf8().data());
+
+    GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
+    ASSERT(bus);
+
+    gst_bus_set_sync_handler(bus.get(), [](GstBus*, GstMessage* message, gpointer userData) {
+        auto& decoder = *static_cast<ImageDecoderGStreamer::InnerDecoder*>(userData);
+        if (&decoder.m_runLoop == &RunLoop::current())
+            decoder.handleMessage(message);
+        else {
+            GRefPtr<GstMessage> protectedMessage(message);
+            auto weakThis = makeWeakPtr(decoder);
+            decoder.m_runLoop.dispatch([weakThis, protectedMessage] {
+                if (weakThis)
+                    weakThis->handleMessage(protectedMessage.get());
+            });
+        }
+        gst_message_unref(message);
+        return GST_BUS_DROP;
+    }, this, nullptr);
+
+    GstElement* source = gst_element_factory_make("giostreamsrc", nullptr);
+    g_object_set(source, "stream", m_memoryStream.get(), nullptr);
+
+    GstElement* decoder = gst_element_factory_make("decodebin", nullptr);
+    auto allowedCaps = adoptGRef(gst_caps_new_empty_simple("video/x-raw"));
+    g_object_set(decoder, "caps", allowedCaps.get(), "expose-all-streams", false, nullptr);
+    g_signal_connect_swapped(decoder, "pad-added", G_CALLBACK(decodebinPadAddedCallback), this);
+
+    gst_bin_add_many(GST_BIN_CAST(m_pipeline.get()), source, decoder, nullptr);
+    gst_element_link(source, decoder);
+    gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING);
+}
+
+void ImageDecoderGStreamer::InnerDecoder::run()
+{
+    m_runLoop.dispatch([this]() {
+        preparePipeline();
+    });
+
+    m_runLoop.run();
+}
+
+EncodedDataStatus ImageDecoderGStreamer::InnerDecoder::encodedDataStatus() const
+{
+    GstState state;
+    gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
+    if (state >= GST_STATE_READY)
+        return EncodedDataStatus::TypeAvailable;
+    return EncodedDataStatus::Unknown;
+}
+
+void ImageDecoderGStreamer::pushEncodedData(const SharedBuffer& buffer)
+{
+    m_eos = false;
+    auto thread = Thread::create("ImageDecoderGStreamer", [this, data = "" size = buffer.size()] {
+        m_innerDecoder = ImageDecoderGStreamer::InnerDecoder::create(*this, data, size);
+        m_innerDecoder->run();
+    });
+    thread->waitForCompletion();
+    m_eos = true;
+    m_innerDecoder = nullptr;
+    callOnMainThread([this] {
+        if (m_encodedDataStatusChangedCallback)
+            m_encodedDataStatusChangedCallback(encodedDataStatus());
+    });
+}
+
+}
+#endif

Added: trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.h (0 => 261922)


--- trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/ImageDecoderGStreamer.h	2020-05-20 15:52:36 UTC (rev 261922)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * aint with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if USE(GSTREAMER)
+
+#include "GRefPtrGStreamer.h"
+#include "ImageDecoder.h"
+#include "MIMETypeRegistry.h"
+#include "SampleMap.h"
+#include "SharedBuffer.h"
+#include <wtf/Forward.h>
+#include <wtf/RunLoop.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+
+class ContentType;
+class ImageDecoderGStreamerSample;
+
+class ImageDecoderGStreamer final : public ImageDecoder, public CanMakeWeakPtr<ImageDecoderGStreamer> {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(ImageDecoderGStreamer);
+public:
+    static RefPtr<ImageDecoderGStreamer> create(SharedBuffer&, const String& mimeType, AlphaOption, GammaAndColorProfileOption);
+    ImageDecoderGStreamer(SharedBuffer&, const String& mimeType, AlphaOption, GammaAndColorProfileOption);
+    virtual ~ImageDecoderGStreamer() = default;
+
+    static bool supportsMediaType(MediaType type) { return type == MediaType::Video; }
+    static bool supportsContainerType(const String&);
+
+    size_t bytesDecodedToDetermineProperties() const override { return 0; }
+    static bool canDecodeType(const String& mimeType);
+
+    void setEncodedDataStatusChangeCallback(WTF::Function<void(EncodedDataStatus)>&& callback) final { m_encodedDataStatusChangedCallback = WTFMove(callback); }
+    EncodedDataStatus encodedDataStatus() const final;
+    IntSize size() const final;
+    size_t frameCount() const final { return m_sampleData.size(); }
+    RepetitionCount repetitionCount() const final;
+    String uti() const final;
+    String filenameExtension() const final { return MIMETypeRegistry::getPreferredExtensionForMIMEType(m_mimeType); }
+    Optional<IntPoint> hotSpot() const final { return WTF::nullopt; }
+
+    IntSize frameSizeAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const final { return size(); }
+    bool frameIsCompleteAtIndex(size_t index) const final { return sampleAtIndex(index); }
+    ImageOrientation frameOrientationAtIndex(size_t) const final;
+
+    Seconds frameDurationAtIndex(size_t) const final;
+    bool frameHasAlphaAtIndex(size_t) const final;
+    bool frameAllowSubsamplingAtIndex(size_t index) const final { return index <= m_sampleData.size(); }
+    unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const final;
+
+    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const DecodingOptions& = DecodingOptions(DecodingMode::Synchronous)) final;
+
+    void setExpectedContentSize(long long) final { }
+    void setData(SharedBuffer&, bool allDataReceived) final;
+    bool isAllDataReceived() const final { return m_eos; }
+    void clearFrameBufferCache(size_t) final;
+
+private:
+    class InnerDecoder : public ThreadSafeRefCounted<InnerDecoder>, public CanMakeWeakPtr<InnerDecoder> {
+        WTF_MAKE_FAST_ALLOCATED;
+        WTF_MAKE_NONCOPYABLE(InnerDecoder);
+    public:
+        static RefPtr<InnerDecoder> create(ImageDecoderGStreamer& decoder, const char* data, gssize size)
+        {
+            return adoptRef(*new InnerDecoder(decoder, data, size));
+        }
+
+        InnerDecoder(ImageDecoderGStreamer& decoder, const char* data, gssize size)
+            : m_decoder(makeWeakPtr(decoder))
+            , m_runLoop(RunLoop::current())
+        {
+            m_memoryStream = adoptGRef(g_memory_input_stream_new_from_data(data, size, nullptr));
+        }
+
+        void run();
+        EncodedDataStatus encodedDataStatus() const;
+
+    private :
+        static void decodebinPadAddedCallback(ImageDecoderGStreamer::InnerDecoder*, GstPad*);
+        void handleMessage(GstMessage*);
+        void preparePipeline();
+        void connectDecoderPad(GstPad*);
+
+        WeakPtr<ImageDecoderGStreamer> m_decoder;
+        GRefPtr<GstElement> m_pipeline;
+        GRefPtr<GInputStream> m_memoryStream;
+        RunLoop& m_runLoop;
+    };
+
+    void handleSample(GRefPtr<GstSample>&&);
+    void pushEncodedData(const SharedBuffer&);
+
+    const ImageDecoderGStreamerSample* sampleAtIndex(size_t) const;
+
+    WTF::Function<void(EncodedDataStatus)> m_encodedDataStatusChangedCallback;
+    SampleMap m_sampleData;
+    DecodeOrderSampleMap::iterator m_cursor;
+    Lock m_sampleGeneratorLock;
+    bool m_eos { false };
+    Optional<IntSize> m_size;
+    String m_mimeType;
+    RefPtr<ImageDecoderGStreamer::InnerDecoder> m_innerDecoder;
+};
+
+}
+#endif

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h (261921 => 261922)


--- trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h	2020-05-20 15:52:36 UTC (rev 261922)
@@ -17,8 +17,7 @@
  * Boston, MA 02110-1301, USA.
  */
 
-#ifndef ImageGStreamer_h
-#define ImageGStreamer_h
+#pragma once
 
 #if ENABLE(VIDEO) && USE(GSTREAMER)
 
@@ -37,42 +36,44 @@
 class IntSize;
 
 class ImageGStreamer : public RefCounted<ImageGStreamer> {
-    public:
-        static RefPtr<ImageGStreamer> createImage(GstSample* sample)
-        {
-            auto image = adoptRef(new ImageGStreamer(sample));
-            if (!image->m_image)
-                return nullptr;
+public:
+    static RefPtr<ImageGStreamer> createImage(GstSample* sample)
+    {
+        auto image = adoptRef(new ImageGStreamer(sample));
+        if (!image->m_image)
+            return nullptr;
 
-            return image;
-        }
-        ~ImageGStreamer();
+        return image;
+    }
+    ~ImageGStreamer();
 
-        BitmapImage& image()
-        {
-            ASSERT(m_image);
-            return *m_image.get();
-        }
+    BitmapImage& image()
+    {
+        ASSERT(m_image);
+        return *m_image.get();
+    }
 
-        void setCropRect(FloatRect rect) { m_cropRect = rect; }
-        FloatRect rect()
-        {
-            ASSERT(m_image);
-            if (!m_cropRect.isEmpty())
-                return FloatRect(m_cropRect);
-            return FloatRect(0, 0, m_image->size().width(), m_image->size().height());
-        }
+    void setCropRect(FloatRect rect) { m_cropRect = rect; }
+    FloatRect rect()
+    {
+        ASSERT(m_image);
+        if (!m_cropRect.isEmpty())
+            return FloatRect(m_cropRect);
+        return FloatRect(0, 0, m_image->size().width(), m_image->size().height());
+    }
 
-    private:
-        ImageGStreamer(GstSample*);
-        RefPtr<BitmapImage> m_image;
-        FloatRect m_cropRect;
+    bool hasAlpha() const { return m_hasAlpha; }
+
+private:
+    ImageGStreamer(GstSample*);
+    RefPtr<BitmapImage> m_image;
+    FloatRect m_cropRect;
 #if USE(CAIRO)
-        GstVideoFrame m_videoFrame;
-        bool m_frameMapped { false };
+    GstVideoFrame m_videoFrame;
+    bool m_frameMapped { false };
 #endif
-    };
+    bool m_hasAlpha { false };
+};
+
 }
-
-#endif // USE(GSTREAMER)
-#endif
+#endif // ENABLE(VIDEO) && USE(GSTREAMER)

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp (261921 => 261922)


--- trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp	2020-05-20 15:52:36 UTC (rev 261922)
@@ -42,6 +42,8 @@
     // Right now the TextureMapper only supports chromas with one plane
     ASSERT(GST_VIDEO_INFO_N_PLANES(&videoInfo) == 1);
 
+    m_hasAlpha = GST_VIDEO_INFO_HAS_ALPHA(&videoInfo);
+
     GstBuffer* buffer = gst_sample_get_buffer(sample);
     if (UNLIKELY(!GST_IS_BUFFER(buffer)))
         return;

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h (261921 => 261922)


--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h	2020-05-20 15:43:19 UTC (rev 261921)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h	2020-05-20 15:52:36 UTC (rev 261922)
@@ -29,7 +29,7 @@
 
 namespace WebCore {
 
-class MediaSampleGStreamer final : public MediaSample {
+class MediaSampleGStreamer : public MediaSample {
 public:
     static Ref<MediaSampleGStreamer> create(GRefPtr<GstSample>&& sample, const FloatSize& presentationSize, const AtomString& trackId)
     {
@@ -55,11 +55,13 @@
     PlatformSample platformSample() override;
     void dump(PrintStream&) const override;
 
-private:
+protected:
     MediaSampleGStreamer(GRefPtr<GstSample>&&, const FloatSize& presentationSize, const AtomString& trackId);
-    MediaSampleGStreamer(const FloatSize& presentationSize, const AtomString& trackId);
     virtual ~MediaSampleGStreamer() = default;
 
+private:
+    MediaSampleGStreamer(const FloatSize& presentationSize, const AtomString& trackId);
+
     MediaTime m_pts;
     MediaTime m_dts;
     MediaTime m_duration;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to