[MediaStream] RealtimeMediaSource should be able to vend hashed IDs
[WebKit-https.git] / Source / WebCore / platform / mediastream / gstreamer / GStreamerVideoCaptureSource.cpp
1 /*
2  * Copyright (C) 2018 Metrological Group B.V.
3  * Author: Thibault Saunier <tsaunier@igalia.com>
4  * Author: Alejandro G. Castro  <alex@igalia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * aint with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #if ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
25 #include "GStreamerVideoCaptureSource.h"
26
27 #include "GStreamerCaptureDeviceManager.h"
28 #include "MediaSampleGStreamer.h"
29
30 #include <gst/app/gstappsink.h>
31 #include <webrtc/api/mediastreaminterface.h>
32 #include <webrtc/api/peerconnectioninterface.h>
33 #include <webrtc/media/base/videocommon.h>
34 #include <webrtc/media/engine/webrtcvideocapturer.h>
35 #include <webrtc/media/engine/webrtcvideocapturerfactory.h>
36 #include <webrtc/modules/video_capture/video_capture_defines.h>
37
38 namespace WebCore {
39
40 const static int defaultWidth = 640;
41 const static int defaultHeight = 480;
42
43 GST_DEBUG_CATEGORY(webkit_video_capture_source_debug);
44 #define GST_CAT_DEFAULT webkit_video_capture_source_debug
45
46 static void initializeGStreamerDebug()
47 {
48     static std::once_flag debugRegisteredFlag;
49     std::call_once(debugRegisteredFlag, [] {
50         GST_DEBUG_CATEGORY_INIT(webkit_video_capture_source_debug, "webkitvideocapturesource", 0,
51             "WebKit Video Capture Source.");
52     });
53 }
54
55 class GStreamerVideoCaptureSourceFactory final : public VideoCaptureFactory {
56 public:
57     CaptureSourceOrError createVideoCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
58     {
59         return GStreamerVideoCaptureSource::create(String { device.persistentId() }, WTFMove(hashSalt), constraints);
60     }
61 };
62
63 VideoCaptureFactory& libWebRTCVideoCaptureSourceFactory()
64 {
65     static NeverDestroyed<GStreamerVideoCaptureSourceFactory> factory;
66     return factory.get();
67 }
68
69 class GStreamerDisplayCaptureSourceFactory final : public DisplayCaptureFactory {
70 public:
71     CaptureSourceOrError createDisplayCaptureSource(const CaptureDevice&, const MediaConstraints*) final
72     {
73         // FIXME: Implement this.
74         return { };
75     }
76 };
77
78 DisplayCaptureFactory& libWebRTCDisplayCaptureSourceFactory()
79 {
80     static NeverDestroyed<GStreamerDisplayCaptureSourceFactory> factory;
81     return factory.get();
82 }
83
84 CaptureSourceOrError GStreamerVideoCaptureSource::create(String&& deviceID, String&& hashSalt, const MediaConstraints* constraints)
85 {
86     auto device = GStreamerVideoCaptureDeviceManager::singleton().gstreamerDeviceWithUID(deviceID);
87     if (!device) {
88         auto errorMessage = String::format("GStreamerVideoCaptureSource::create(): GStreamer did not find the device: %s.", deviceID.utf8().data());
89         return CaptureSourceOrError(WTFMove(errorMessage));
90     }
91
92     auto source = adoptRef(*new GStreamerVideoCaptureSource(device.value(), WTFMove(hashSalt)));
93
94     if (constraints) {
95         auto result = source->applyConstraints(*constraints);
96         if (result)
97             return WTFMove(result.value().first);
98     }
99     return CaptureSourceOrError(WTFMove(source));
100 }
101
102 VideoCaptureFactory& GStreamerVideoCaptureSource::factory()
103 {
104     return libWebRTCVideoCaptureSourceFactory();
105 }
106
107 DisplayCaptureFactory& GStreamerVideoCaptureSource::displayFactory()
108 {
109     return libWebRTCDisplayCaptureSourceFactory();
110 }
111
112 GStreamerVideoCaptureSource::GStreamerVideoCaptureSource(String&& deviceID, String&& name, String&& hashSalt, const gchar *source_factory)
113     : RealtimeMediaSource(RealtimeMediaSource::Type::Video, WTFMove(deviceID), WTFMove(name), WTFMove(hashSalt))
114     , m_capturer(std::make_unique<GStreamerVideoCapturer>(source_factory))
115 {
116     initializeGStreamerDebug();
117 }
118
119 GStreamerVideoCaptureSource::GStreamerVideoCaptureSource(GStreamerCaptureDevice device, String&& hashSalt)
120     : RealtimeMediaSource(RealtimeMediaSource::Type::Video, String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt))
121     , m_capturer(std::make_unique<GStreamerVideoCapturer>(device))
122 {
123     initializeGStreamerDebug();
124 }
125
126 GStreamerVideoCaptureSource::~GStreamerVideoCaptureSource()
127 {
128 }
129
130 void GStreamerVideoCaptureSource::settingsDidChange(OptionSet<RealtimeMediaSourceSettings::Flag> settings)
131 {
132     if (settings.containsAny({ RealtimeMediaSourceSettings::Flag::Width, RealtimeMediaSourceSettings::Flag::Height }))
133         m_capturer->setSize(size().width(), size().height());
134     if (settings.contains(RealtimeMediaSourceSettings::Flag::FrameRate))
135         m_capturer->setFrameRate(frameRate());
136 }
137
138 void GStreamerVideoCaptureSource::startProducingData()
139 {
140     m_capturer->setupPipeline();
141     m_capturer->setSize(size().width(), size().height());
142     m_capturer->setFrameRate(frameRate());
143     g_signal_connect(m_capturer->sink(), "new-sample", G_CALLBACK(newSampleCallback), this);
144     m_capturer->play();
145 }
146
147 GstFlowReturn GStreamerVideoCaptureSource::newSampleCallback(GstElement* sink, GStreamerVideoCaptureSource* source)
148 {
149     auto gstSample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
150     auto mediaSample = MediaSampleGStreamer::create(WTFMove(gstSample), WebCore::FloatSize(), String());
151
152     // FIXME - Check how presentationSize is supposed to be used here.
153     callOnMainThread([protectedThis = makeRef(*source), mediaSample = WTFMove(mediaSample)] {
154         protectedThis->videoSampleAvailable(mediaSample.get());
155     });
156
157     return GST_FLOW_OK;
158 }
159
160 void GStreamerVideoCaptureSource::stopProducingData()
161 {
162     g_signal_handlers_disconnect_by_func(m_capturer->sink(), reinterpret_cast<gpointer>(newSampleCallback), this);
163     m_capturer->stop();
164
165     GST_INFO("Reset height and width after stopping source");
166     setSize({ 0, 0 });
167 }
168
169 const RealtimeMediaSourceCapabilities& GStreamerVideoCaptureSource::capabilities()
170 {
171     if (m_capabilities)
172         return m_capabilities.value();
173
174     RealtimeMediaSourceCapabilities capabilities(settings().supportedConstraints());
175     GRefPtr<GstCaps> caps = adoptGRef(m_capturer->caps());
176     int32_t minWidth = G_MAXINT32, maxWidth = 0, minHeight = G_MAXINT32, maxHeight = 0;
177     double minFramerate = G_MAXDOUBLE, maxFramerate = G_MINDOUBLE;
178
179     for (guint i = 0; i < gst_caps_get_size(caps.get()); i++) {
180         GstStructure* str = gst_caps_get_structure(caps.get(), i);
181
182         // Only accept raw video for now.
183         if (!gst_structure_has_name(str, "video/x-raw"))
184             continue;
185
186         int32_t tmpMinWidth, tmpMinHeight, tmpMinFPSNumerator, tmpMinFPSDenominator;
187         int32_t tmpMaxWidth, tmpMaxHeight, tmpMaxFPSNumerator, tmpMaxFPSDenominator;
188         double tmpMinFramerate = G_MAXDOUBLE, tmpMaxFramerate = G_MINDOUBLE;
189
190         if (!gst_structure_get(str, "width", GST_TYPE_INT_RANGE, &tmpMinWidth, &tmpMaxWidth, "height", GST_TYPE_INT_RANGE, &tmpMinHeight, &tmpMaxHeight, nullptr)) {
191             if (!gst_structure_get(str, "width", G_TYPE_INT, &tmpMinWidth, "height", G_TYPE_INT, &tmpMinHeight, nullptr))
192                 continue;
193
194             tmpMaxWidth = tmpMinWidth;
195             tmpMaxHeight = tmpMinHeight;
196         }
197
198         if (gst_structure_get(str, "framerate", GST_TYPE_FRACTION_RANGE, &tmpMinFPSNumerator, &tmpMinFPSDenominator, &tmpMaxFPSNumerator, &tmpMaxFPSDenominator, nullptr)) {
199             gst_util_fraction_to_double(tmpMinFPSNumerator, tmpMinFPSDenominator, &tmpMinFramerate);
200             gst_util_fraction_to_double(tmpMaxFPSNumerator, tmpMaxFPSDenominator, &tmpMaxFramerate);
201         } else if (gst_structure_get(str,
202             "framerate", GST_TYPE_FRACTION, &tmpMinFPSNumerator, &tmpMinFPSDenominator, nullptr)) {
203             tmpMaxFPSNumerator = tmpMinFPSNumerator;
204             tmpMaxFPSDenominator = tmpMinFPSDenominator;
205             gst_util_fraction_to_double(tmpMinFPSNumerator, tmpMinFPSDenominator, &tmpMinFramerate);
206             gst_util_fraction_to_double(tmpMaxFPSNumerator, tmpMaxFPSDenominator, &tmpMaxFramerate);
207         } else {
208             const GValue* frameRates(gst_structure_get_value(str, "framerate"));
209             tmpMinFramerate = G_MAXDOUBLE;
210             tmpMaxFramerate = 0.0;
211
212             guint frameRatesLength = static_cast<guint>(gst_value_list_get_size(frameRates)) - 1;
213
214             for (guint i = 0; i < frameRatesLength; i++) {
215                 double tmpFrameRate;
216                 const GValue* val = gst_value_list_get_value(frameRates, i);
217
218                 ASSERT(G_VALUE_TYPE(val) == GST_TYPE_FRACTION);
219                 gst_util_fraction_to_double(gst_value_get_fraction_numerator(val),
220                     gst_value_get_fraction_denominator(val), &tmpFrameRate);
221
222                 tmpMinFramerate = std::min(tmpMinFramerate, tmpFrameRate);
223                 tmpMaxFramerate = std::max(tmpMaxFramerate, tmpFrameRate);
224             }
225
226             if (i > 0) {
227                 minWidth = std::min(tmpMinWidth, minWidth);
228                 minHeight = std::min(tmpMinHeight, minHeight);
229                 minFramerate = std::min(tmpMinFramerate, minFramerate);
230
231                 maxWidth = std::max(tmpMaxWidth, maxWidth);
232                 maxHeight = std::max(tmpMaxHeight, maxHeight);
233                 maxFramerate = std::max(tmpMaxFramerate, maxFramerate);
234             } else {
235                 minWidth = tmpMinWidth;
236                 minHeight = tmpMinHeight;
237                 minFramerate = tmpMinFramerate;
238
239                 maxWidth = tmpMaxWidth;
240                 maxHeight = tmpMaxHeight;
241                 maxFramerate = tmpMaxFramerate;
242             }
243         }
244
245         capabilities.setDeviceId(hashedId());
246         capabilities.setWidth(CapabilityValueOrRange(minWidth, maxWidth));
247         capabilities.setHeight(CapabilityValueOrRange(minHeight, maxHeight));
248         capabilities.setFrameRate(CapabilityValueOrRange(minFramerate, maxFramerate));
249         m_capabilities = WTFMove(capabilities);
250     }
251
252     return m_capabilities.value();
253 }
254
255 const RealtimeMediaSourceSettings& GStreamerVideoCaptureSource::settings()
256 {
257     if (!m_currentSettings) {
258         RealtimeMediaSourceSettings settings;
259         settings.setDeviceId(hashedId());
260
261         RealtimeMediaSourceSupportedConstraints supportedConstraints;
262         supportedConstraints.setSupportsDeviceId(true);
263         supportedConstraints.setSupportsFacingMode(true);
264         supportedConstraints.setSupportsWidth(true);
265         supportedConstraints.setSupportsHeight(true);
266         supportedConstraints.setSupportsAspectRatio(true);
267         supportedConstraints.setSupportsFrameRate(true);
268         settings.setSupportedConstraints(supportedConstraints);
269
270         m_currentSettings = WTFMove(settings);
271     }
272
273     m_currentSettings->setWidth(size().width());
274     m_currentSettings->setHeight(size().height());
275     m_currentSettings->setFrameRate(frameRate());
276     m_currentSettings->setAspectRatio(aspectRatio());
277     m_currentSettings->setFacingMode(facingMode());
278     return m_currentSettings.value();
279 }
280
281 } // namespace WebCore
282
283 #endif // ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC)