Title: [294104] trunk/Source/WebCore
Revision
294104
Author
commit-qu...@webkit.org
Date
2022-05-12 09:24:00 -0700 (Thu, 12 May 2022)

Log Message

[GStreamer][VideoCapture] Add support for capturing encoded video streams from a webcam
https://bugs.webkit.org/show_bug.cgi?id=240229

Patch by Loïc Le Page <llep...@igalia.com> on 2022-05-12
Reviewed by Philippe Normand.

Takes into account encoded video streams produced by a webcam for
video capture.

Manually tested (requires a webcam with encoded video streams).

* platform/mediastream/gstreamer/GStreamerVideoCaptureSource.cpp:
(WebCore::GStreamerVideoCaptureSource::generatePresets):
* platform/mediastream/gstreamer/GStreamerVideoCapturer.cpp:
(WebCore::GStreamerVideoCapturer::createConverter):
(WebCore::GStreamerVideoCapturer::setSize):
(WebCore::GStreamerVideoCapturer::setFrameRate):
(WebCore::GStreamerVideoCapturer::adjustVideoSrcMIMEType):
* platform/mediastream/gstreamer/GStreamerVideoCapturer.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (294103 => 294104)


--- trunk/Source/WebCore/ChangeLog	2022-05-12 14:27:48 UTC (rev 294103)
+++ trunk/Source/WebCore/ChangeLog	2022-05-12 16:24:00 UTC (rev 294104)
@@ -1,3 +1,24 @@
+2022-05-12  Loïc Le Page  <llep...@igalia.com>
+
+        [GStreamer][VideoCapture] Add support for capturing encoded video streams from a webcam
+        https://bugs.webkit.org/show_bug.cgi?id=240229
+
+        Reviewed by Philippe Normand.
+
+        Takes into account encoded video streams produced by a webcam for
+        video capture.
+
+        Manually tested (requires a webcam with encoded video streams).
+
+        * platform/mediastream/gstreamer/GStreamerVideoCaptureSource.cpp:
+        (WebCore::GStreamerVideoCaptureSource::generatePresets):
+        * platform/mediastream/gstreamer/GStreamerVideoCapturer.cpp:
+        (WebCore::GStreamerVideoCapturer::createConverter):
+        (WebCore::GStreamerVideoCapturer::setSize):
+        (WebCore::GStreamerVideoCapturer::setFrameRate):
+        (WebCore::GStreamerVideoCapturer::adjustVideoSrcMIMEType):
+        * platform/mediastream/gstreamer/GStreamerVideoCapturer.h:
+
 2022-05-12  Youenn Fablet  <you...@apple.com>
 
         Add a better mock for audio units used by CoreAudioSharedUnit

Modified: trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCaptureSource.cpp (294103 => 294104)


--- trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCaptureSource.cpp	2022-05-12 14:27:48 UTC (rev 294103)
+++ trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCaptureSource.cpp	2022-05-12 16:24:00 UTC (rev 294104)
@@ -264,10 +264,6 @@
     for (unsigned i = 0; i < gst_caps_get_size(caps.get()); i++) {
         GstStructure* str = gst_caps_get_structure(caps.get(), i);
 
-        // Only accept raw video for now.
-        if (!gst_structure_has_name(str, "video/x-raw"))
-            continue;
-
         int32_t width, height;
         if (!gst_structure_get(str, "width", G_TYPE_INT, &width, "height", G_TYPE_INT, &height, nullptr)) {
             GST_INFO("Could not find discret height and width values in %" GST_PTR_FORMAT, str);

Modified: trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.cpp (294103 => 294104)


--- trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.cpp	2022-05-12 14:27:48 UTC (rev 294103)
+++ trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.cpp	2022-05-12 16:24:00 UTC (rev 294104)
@@ -68,7 +68,16 @@
 GstElement* GStreamerVideoCapturer::createConverter()
 {
     // https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/97#note_56575
-    return makeGStreamerBin("videoscale ! videoconvert ! videorate drop-_only_=1 average-period=1", true);
+    auto bin = makeGStreamerBin("capsfilter caps=\"video/x-raw\" name=mimetype-filter ! decodebin3 ! videoscale ! videoconvert ! videorate drop-_only_=1 average-period=1 name=videorate", false);
+
+    m_videoSrcMIMETypeFilter = adoptGRef(gst_bin_get_by_name(GST_BIN(bin), "mimetype-filter"));
+    auto videorate = adoptGRef(gst_bin_get_by_name(GST_BIN(bin), "videorate"));
+
+    RELEASE_ASSERT(gst_element_add_pad(bin, gst_ghost_pad_new("sink", GST_PAD(m_videoSrcMIMETypeFilter->sinkpads->data))));
+    RELEASE_ASSERT(gst_element_add_pad(bin, gst_ghost_pad_new("src", GST_PAD(videorate->srcpads->data))));
+
+    adjustVideoSrcMIMEType();
+    return bin;
 }
 
 GstVideoInfo GStreamerVideoCapturer::getBestFormat()
@@ -101,6 +110,8 @@
     m_caps = adoptGRef(gst_caps_copy(m_caps.get()));
     gst_caps_set_simple(m_caps.get(), "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, nullptr);
 
+    adjustVideoSrcMIMEType();
+
     if (!m_capsfilter)
         return false;
 
@@ -133,6 +144,8 @@
     m_caps = adoptGRef(gst_caps_copy(m_caps.get()));
     gst_caps_set_simple(m_caps.get(), "framerate", GST_TYPE_FRACTION, numerator, denominator, nullptr);
 
+    adjustVideoSrcMIMEType();
+
     if (!m_capsfilter)
         return false;
 
@@ -142,6 +155,160 @@
     return true;
 }
 
+static std::optional<int> getMaxIntValueFromStructure(const GstStructure* structure, const char* fieldName)
+{
+    const GValue* value = gst_structure_get_value(structure, fieldName);
+    if (!value)
+        return std::nullopt;
+
+    int maxInt = -G_MAXINT;
+    if (G_VALUE_HOLDS_INT(value))
+        maxInt = g_value_get_int(value);
+    else if (GST_VALUE_HOLDS_INT_RANGE(value))
+        maxInt = gst_value_get_int_range_max(value);
+    else if (GST_VALUE_HOLDS_ARRAY(value)) {
+        const guint size = gst_value_array_get_size(value);
+        for (guint i = 0; i < size; ++i) {
+            const GValue* item = gst_value_array_get_value(value, i);
+            if (G_VALUE_HOLDS_INT(item)) {
+                int val = g_value_get_int(item);
+                if (val > maxInt)
+                    maxInt = val;
+            }
+        }
+    } else if (GST_VALUE_HOLDS_LIST(value)) {
+        const guint size = gst_value_list_get_size(value);
+        for (guint i = 0; i < size; ++i) {
+            const GValue* item = gst_value_list_get_value(value, i);
+            if (G_VALUE_HOLDS_INT(item)) {
+                int val = g_value_get_int(item);
+                if (val > maxInt)
+                    maxInt = val;
+            }
+        }
+    }
+
+    return (maxInt > -G_MAXINT) ? std::make_optional<>(maxInt) : std::nullopt;
+}
+
+static std::optional<double> getMaxFractionValueFromStructure(const GstStructure* structure, const char* fieldName)
+{
+    const GValue* value = gst_structure_get_value(structure, fieldName);
+    if (!value)
+        return std::nullopt;
+
+    double maxFraction = -G_MAXDOUBLE;
+    if (GST_VALUE_HOLDS_FRACTION(value)) {
+        gst_util_fraction_to_double(gst_value_get_fraction_numerator(value),
+            gst_value_get_fraction_denominator(value), &maxFraction);
+    } else if (GST_VALUE_HOLDS_FRACTION_RANGE(value)) {
+        const GValue* fractionValue = gst_value_get_fraction_range_max(value);
+        gst_util_fraction_to_double(gst_value_get_fraction_numerator(fractionValue),
+            gst_value_get_fraction_denominator(fractionValue), &maxFraction);
+    } else if (GST_VALUE_HOLDS_ARRAY(value)) {
+        const guint size = gst_value_array_get_size(value);
+        for (guint i = 0; i < size; ++i) {
+            const GValue* item = gst_value_array_get_value(value, i);
+            if (GST_VALUE_HOLDS_FRACTION(item)) {
+                double val = -G_MAXDOUBLE;
+                gst_util_fraction_to_double(gst_value_get_fraction_numerator(item),
+                    gst_value_get_fraction_denominator(item), &val);
+                if (val > maxFraction)
+                    maxFraction = val;
+            }
+        }
+    } else if (GST_VALUE_HOLDS_LIST(value)) {
+        const guint size = gst_value_list_get_size(value);
+        for (guint i = 0; i < size; ++i) {
+            const GValue* item = gst_value_list_get_value(value, i);
+            if (GST_VALUE_HOLDS_FRACTION(item)) {
+                double val = -G_MAXDOUBLE;
+                gst_util_fraction_to_double(gst_value_get_fraction_numerator(item),
+                    gst_value_get_fraction_denominator(item), &val);
+                if (val > maxFraction)
+                    maxFraction = val;
+            }
+        }
+    }
+
+    return (maxFraction > -G_MAXDOUBLE) ? std::make_optional<>(maxFraction) : std::nullopt;
+}
+
+void GStreamerVideoCapturer::adjustVideoSrcMIMEType()
+{
+    if (!m_videoSrcMIMETypeFilter)
+        return;
+
+    struct MimeTypeSelector {
+        const char* mimeType = "video/x-raw";
+
+        int maxWidth = 0;
+        int maxHeight = 0;
+        double maxFrameRate = 0;
+
+        struct {
+            int width = 0;
+            int height = 0;
+            double frameRate = 0;
+        } stopCondition;
+    } selector;
+
+    // If nothing has been specified by the user, we target at least an arbitrary resolution of 1920x1080@24fps.
+    const GstStructure* capsStruct = gst_caps_get_structure(m_caps.get(), 0);
+    if (!gst_structure_get_int(capsStruct, "width", &selector.stopCondition.width))
+        selector.stopCondition.width = 1920;
+
+    if (!gst_structure_get_int(capsStruct, "height", &selector.stopCondition.height))
+        selector.stopCondition.height = 1080;
+
+    int numerator = 0;
+    int denominator = 1;
+    if (gst_structure_get_fraction(capsStruct, "framerate", &numerator, &denominator))
+        gst_util_fraction_to_double(numerator, denominator, &selector.stopCondition.frameRate);
+    else
+        selector.stopCondition.frameRate = 24;
+
+    GST_DEBUG_OBJECT(m_pipeline.get(), "Searching best video capture device mime type for resolution %dx%d@%.3f",
+        selector.stopCondition.width, selector.stopCondition.height, selector.stopCondition.frameRate);
+
+    auto deviceCaps = adoptGRef(gst_device_get_caps(m_device.get()));
+    gst_caps_foreach(deviceCaps.get(),
+        reinterpret_cast<GstCapsForeachFunc>(+[](GstCapsFeatures*, GstStructure* structure, MimeTypeSelector* selector) -> gboolean {
+            auto width = getMaxIntValueFromStructure(structure, "width");
+            if (!width.has_value())
+                return TRUE;
+
+            auto height = getMaxIntValueFromStructure(structure, "height");
+            if (!height.has_value())
+                return TRUE;
+
+            auto frameRate = getMaxFractionValueFromStructure(structure, "framerate");
+            if (!frameRate.has_value())
+                return TRUE;
+
+            if (*width >= selector->stopCondition.width && *height >= selector->stopCondition.height
+                && *frameRate >= selector->stopCondition.frameRate) {
+                selector->mimeType = gst_structure_get_name(structure);
+                return FALSE;
+            }
+
+            if (*width >= selector->maxWidth && *height >= selector->maxHeight && *frameRate >= selector->maxFrameRate) {
+                selector->maxWidth = *width;
+                selector->maxHeight = *height;
+                selector->maxFrameRate = *frameRate;
+                selector->mimeType = gst_structure_get_name(structure);
+            }
+
+            return TRUE;
+        }),
+        &selector);
+
+    GST_INFO_OBJECT(m_pipeline.get(), "Setting video capture device mime type to %s", selector.mimeType);
+
+    auto caps = adoptGRef(gst_caps_new_empty_simple(selector.mimeType));
+    g_object_set(m_videoSrcMIMETypeFilter.get(), "caps", caps.get(), nullptr);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM) && USE(GSTREAMER)

Modified: trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.h (294103 => 294104)


--- trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.h	2022-05-12 14:27:48 UTC (rev 294103)
+++ trunk/Source/WebCore/platform/mediastream/gstreamer/GStreamerVideoCapturer.h	2022-05-12 16:24:00 UTC (rev 294104)
@@ -50,6 +50,8 @@
 
 private:
     std::optional<NodeAndFD> m_nodeAndFd;
+    GRefPtr<GstElement> m_videoSrcMIMETypeFilter;
+    void adjustVideoSrcMIMEType();
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to