Use Optional::valueOr() instead of Optional::value_or()
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / GStreamerCommon.cpp
1 /*
2  *  Copyright (C) 2012, 2015, 2016 Igalia S.L
3  *  Copyright (C) 2015, 2016 Metrological Group B.V.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20
21 #include "config.h"
22 #include "GStreamerCommon.h"
23
24 #if USE(GSTREAMER)
25
26 #include "GstAllocatorFastMalloc.h"
27 #include "IntSize.h"
28 #include <gst/audio/audio-info.h>
29 #include <gst/gst.h>
30 #include <mutex>
31 #include <wtf/glib/GLibUtilities.h>
32 #include <wtf/glib/GUniquePtr.h>
33 #include <wtf/glib/RunLoopSourcePriority.h>
34
35 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
36 #define GST_USE_UNSTABLE_API
37 #include <gst/mpegts/mpegts.h>
38 #undef GST_USE_UNSTABLE_API
39 #endif
40
41 #if ENABLE(MEDIA_SOURCE)
42 #include "WebKitMediaSourceGStreamer.h"
43 #endif
44
45 #if ENABLE(MEDIA_STREAM) && GST_CHECK_VERSION(1, 10, 0)
46 #include "GStreamerMediaStreamSource.h"
47 #endif
48
49 #if ENABLE(ENCRYPTED_MEDIA)
50 #include "WebKitClearKeyDecryptorGStreamer.h"
51 #endif
52
53 #if ENABLE(VIDEO)
54 #include "WebKitWebSourceGStreamer.h"
55 #endif
56
57 namespace WebCore {
58
59 GstPad* webkitGstGhostPadFromStaticTemplate(GstStaticPadTemplate* staticPadTemplate, const gchar* name, GstPad* target)
60 {
61     GstPad* pad;
62     GstPadTemplate* padTemplate = gst_static_pad_template_get(staticPadTemplate);
63
64     if (target)
65         pad = gst_ghost_pad_new_from_template(name, target, padTemplate);
66     else
67         pad = gst_ghost_pad_new_no_target_from_template(name, padTemplate);
68
69     gst_object_unref(padTemplate);
70
71     return pad;
72 }
73
74 #if ENABLE(VIDEO)
75 bool getVideoSizeAndFormatFromCaps(GstCaps* caps, WebCore::IntSize& size, GstVideoFormat& format, int& pixelAspectRatioNumerator, int& pixelAspectRatioDenominator, int& stride)
76 {
77     if (!doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
78         GST_WARNING("Failed to get the video size and format, these are not a video caps");
79         return false;
80     }
81
82     if (areEncryptedCaps(caps)) {
83         GstStructure* structure = gst_caps_get_structure(caps, 0);
84         format = GST_VIDEO_FORMAT_ENCODED;
85         stride = 0;
86         int width = 0, height = 0;
87         gst_structure_get_int(structure, "width", &width);
88         gst_structure_get_int(structure, "height", &height);
89         if (!gst_structure_get_fraction(structure, "pixel-aspect-ratio", &pixelAspectRatioNumerator, &pixelAspectRatioDenominator)) {
90             pixelAspectRatioNumerator = 1;
91             pixelAspectRatioDenominator = 1;
92         }
93
94         size.setWidth(width);
95         size.setHeight(height);
96     } else {
97         GstVideoInfo info;
98         gst_video_info_init(&info);
99         if (!gst_video_info_from_caps(&info, caps))
100             return false;
101
102         format = GST_VIDEO_INFO_FORMAT(&info);
103         size.setWidth(GST_VIDEO_INFO_WIDTH(&info));
104         size.setHeight(GST_VIDEO_INFO_HEIGHT(&info));
105         pixelAspectRatioNumerator = GST_VIDEO_INFO_PAR_N(&info);
106         pixelAspectRatioDenominator = GST_VIDEO_INFO_PAR_D(&info);
107         stride = GST_VIDEO_INFO_PLANE_STRIDE(&info, 0);
108     }
109
110     return true;
111 }
112
113 Optional<FloatSize> getVideoResolutionFromCaps(const GstCaps* caps)
114 {
115     if (!doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
116         GST_WARNING("Failed to get the video resolution, these are not a video caps");
117         return WTF::nullopt;
118     }
119
120     int width = 0, height = 0;
121     int pixelAspectRatioNumerator = 1, pixelAspectRatioDenominator = 1;
122
123     if (areEncryptedCaps(caps)) {
124         GstStructure* structure = gst_caps_get_structure(caps, 0);
125         gst_structure_get_int(structure, "width", &width);
126         gst_structure_get_int(structure, "height", &height);
127         gst_structure_get_fraction(structure, "pixel-aspect-ratio", &pixelAspectRatioNumerator, &pixelAspectRatioDenominator);
128     } else {
129         GstVideoInfo info;
130         gst_video_info_init(&info);
131         if (!gst_video_info_from_caps(&info, caps))
132             return WTF::nullopt;
133
134         width = GST_VIDEO_INFO_WIDTH(&info);
135         height = GST_VIDEO_INFO_HEIGHT(&info);
136         pixelAspectRatioNumerator = GST_VIDEO_INFO_PAR_N(&info);
137         pixelAspectRatioDenominator = GST_VIDEO_INFO_PAR_D(&info);
138     }
139
140     return makeOptional(FloatSize(width, height * (static_cast<float>(pixelAspectRatioDenominator) / static_cast<float>(pixelAspectRatioNumerator))));
141 }
142
143 bool getSampleVideoInfo(GstSample* sample, GstVideoInfo& videoInfo)
144 {
145     if (!GST_IS_SAMPLE(sample))
146         return false;
147
148     GstCaps* caps = gst_sample_get_caps(sample);
149     if (!caps)
150         return false;
151
152     gst_video_info_init(&videoInfo);
153     if (!gst_video_info_from_caps(&videoInfo, caps))
154         return false;
155
156     return true;
157 }
158 #endif
159
160
161 const char* capsMediaType(const GstCaps* caps)
162 {
163     ASSERT(caps);
164     GstStructure* structure = gst_caps_get_structure(caps, 0);
165     if (!structure) {
166         GST_WARNING("caps are empty");
167         return nullptr;
168     }
169 #if ENABLE(ENCRYPTED_MEDIA)
170     if (gst_structure_has_name(structure, "application/x-cenc") || gst_structure_has_name(structure, "application/x-webm-enc"))
171         return gst_structure_get_string(structure, "original-media-type");
172 #endif
173     return gst_structure_get_name(structure);
174 }
175
176 bool doCapsHaveType(const GstCaps* caps, const char* type)
177 {
178     const char* mediaType = capsMediaType(caps);
179     if (!mediaType) {
180         GST_WARNING("Failed to get MediaType");
181         return false;
182     }
183     return g_str_has_prefix(mediaType, type);
184 }
185
186 bool areEncryptedCaps(const GstCaps* caps)
187 {
188     ASSERT(caps);
189 #if ENABLE(ENCRYPTED_MEDIA)
190     GstStructure* structure = gst_caps_get_structure(caps, 0);
191     if (!structure) {
192         GST_WARNING("caps are empty");
193         return false;
194     }
195     return gst_structure_has_name(structure, "application/x-cenc") || gst_structure_has_name(structure, "application/x-webm-enc");
196 #else
197     UNUSED_PARAM(caps);
198     return false;
199 #endif
200 }
201
202 Vector<String> extractGStreamerOptionsFromCommandLine()
203 {
204     GUniqueOutPtr<char> contents;
205     gsize length;
206     if (!g_file_get_contents("/proc/self/cmdline", &contents.outPtr(), &length, nullptr))
207         return { };
208
209     Vector<String> options;
210     auto optionsString = String::fromUTF8(contents.get(), length);
211     optionsString.split('\0', [&options](StringView item) {
212         if (item.startsWith("--gst"))
213             options.append(item.toString());
214     });
215     return options;
216 }
217
218 bool initializeGStreamer(Optional<Vector<String>>&& options)
219 {
220     static std::once_flag onceFlag;
221     static bool isGStreamerInitialized;
222     std::call_once(onceFlag, [options = WTFMove(options)] {
223         isGStreamerInitialized = false;
224
225 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
226         Vector<String> parameters = options.valueOr(extractGStreamerOptionsFromCommandLine());
227         char** argv = g_new0(char*, parameters.size() + 2);
228         int argc = parameters.size() + 1;
229         argv[0] = g_strdup(getCurrentExecutableName().data());
230         for (unsigned i = 0; i < parameters.size(); i++)
231             argv[i + 1] = g_strdup(parameters[i].utf8().data());
232
233         GUniqueOutPtr<GError> error;
234         isGStreamerInitialized = gst_init_check(&argc, &argv, &error.outPtr());
235         ASSERT_WITH_MESSAGE(isGStreamerInitialized, "GStreamer initialization failed: %s", error ? error->message : "unknown error occurred");
236         g_strfreev(argv);
237
238         if (isFastMallocEnabled()) {
239             const char* disableFastMalloc = getenv("WEBKIT_GST_DISABLE_FAST_MALLOC");
240             if (!disableFastMalloc || !strcmp(disableFastMalloc, "0"))
241                 gst_allocator_set_default(GST_ALLOCATOR(g_object_new(gst_allocator_fast_malloc_get_type(), nullptr)));
242         }
243
244 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
245         if (isGStreamerInitialized)
246             gst_mpegts_initialize();
247 #endif
248 #endif
249     });
250     return isGStreamerInitialized;
251 }
252
253 bool initializeGStreamerAndRegisterWebKitElements()
254 {
255     if (!initializeGStreamer())
256         return false;
257
258     static std::once_flag onceFlag;
259     std::call_once(onceFlag, [] {
260 #if ENABLE(ENCRYPTED_MEDIA)
261         if (webkitGstCheckVersion(1, 6, 1))
262             gst_element_register(nullptr, "webkitclearkey", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_CK_DECRYPT);
263 #endif
264
265 #if ENABLE(MEDIA_STREAM) && GST_CHECK_VERSION(1, 10, 0)
266         if (webkitGstCheckVersion(1, 10, 0))
267             gst_element_register(nullptr, "mediastreamsrc", GST_RANK_PRIMARY, WEBKIT_TYPE_MEDIA_STREAM_SRC);
268 #endif
269
270 #if ENABLE(MEDIA_SOURCE)
271         gst_element_register(nullptr, "webkitmediasrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_SRC);
272 #endif
273
274 #if ENABLE(VIDEO)
275         gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
276 #endif
277     });
278     return true;
279 }
280
281 unsigned getGstPlayFlag(const char* nick)
282 {
283     static GFlagsClass* flagsClass = static_cast<GFlagsClass*>(g_type_class_ref(g_type_from_name("GstPlayFlags")));
284     ASSERT(flagsClass);
285
286     GFlagsValue* flag = g_flags_get_value_by_nick(flagsClass, nick);
287     if (!flag)
288         return 0;
289
290     return flag->value;
291 }
292
293 // Convert a MediaTime in seconds to a GstClockTime. Note that we can get MediaTime objects with a time scale that isn't a GST_SECOND, since they can come to
294 // us through the internal testing API, the DOM and internally. It would be nice to assert the format of the incoming time, but all the media APIs assume time
295 // is passed around in fractional seconds, so we'll just have to assume the same.
296 uint64_t toGstUnsigned64Time(const MediaTime& mediaTime)
297 {
298     MediaTime time = mediaTime.toTimeScale(GST_SECOND);
299     if (time.isInvalid())
300         return GST_CLOCK_TIME_NONE;
301     return time.timeValue();
302 }
303
304 bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString)
305 {
306     GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string(capsString));
307     GList* candidates = gst_element_factory_list_filter(elementFactories, caps.get(), GST_PAD_SINK, false);
308     bool result = candidates;
309
310     gst_plugin_feature_list_free(candidates);
311     return result;
312 }
313
314 static void simpleBusMessageCallback(GstBus*, GstMessage* message, GstBin* pipeline)
315 {
316     switch (GST_MESSAGE_TYPE(message)) {
317     case GST_MESSAGE_ERROR:
318         GST_ERROR_OBJECT(pipeline, "Got message: %" GST_PTR_FORMAT, message);
319         {
320             WTF::String dotFileName = String::format("%s_error", GST_OBJECT_NAME(pipeline));
321             GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(pipeline, GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
322         }
323         break;
324     case GST_MESSAGE_STATE_CHANGED:
325         if (GST_MESSAGE_SRC(message) == GST_OBJECT(pipeline)) {
326             GstState oldState, newState, pending;
327             gst_message_parse_state_changed(message, &oldState, &newState, &pending);
328
329             GST_INFO_OBJECT(pipeline, "State changed (old: %s, new: %s, pending: %s)",
330                 gst_element_state_get_name(oldState),
331                 gst_element_state_get_name(newState),
332                 gst_element_state_get_name(pending));
333
334             WTF::String dotFileName = String::format("%s_%s_%s",
335                 GST_OBJECT_NAME(pipeline),
336                 gst_element_state_get_name(oldState),
337                 gst_element_state_get_name(newState));
338
339             GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
340         }
341         break;
342     default:
343         break;
344     }
345 }
346
347 void disconnectSimpleBusMessageCallback(GstElement* pipeline)
348 {
349     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline)));
350     g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(simpleBusMessageCallback), pipeline);
351 }
352
353 void connectSimpleBusMessageCallback(GstElement* pipeline)
354 {
355     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline)));
356     gst_bus_add_signal_watch_full(bus.get(), RunLoopSourcePriority::RunLoopDispatcher);
357     g_signal_connect(bus.get(), "message", G_CALLBACK(simpleBusMessageCallback), pipeline);
358 }
359
360 }
361
362 #endif // USE(GSTREAMER)