Unreviewed, rolling out r234489.
[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 namespace WebCore {
42
43 const char* webkitGstMapInfoQuarkString = "webkit-gst-map-info";
44
45 GstPad* webkitGstGhostPadFromStaticTemplate(GstStaticPadTemplate* staticPadTemplate, const gchar* name, GstPad* target)
46 {
47     GstPad* pad;
48     GstPadTemplate* padTemplate = gst_static_pad_template_get(staticPadTemplate);
49
50     if (target)
51         pad = gst_ghost_pad_new_from_template(name, target, padTemplate);
52     else
53         pad = gst_ghost_pad_new_no_target_from_template(name, padTemplate);
54
55     gst_object_unref(padTemplate);
56
57     return pad;
58 }
59
60 #if ENABLE(VIDEO)
61 bool getVideoSizeAndFormatFromCaps(GstCaps* caps, WebCore::IntSize& size, GstVideoFormat& format, int& pixelAspectRatioNumerator, int& pixelAspectRatioDenominator, int& stride)
62 {
63     if (!doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
64         GST_WARNING("Failed to get the video size and format, these are not a video caps");
65         return false;
66     }
67
68     if (areEncryptedCaps(caps)) {
69         GstStructure* structure = gst_caps_get_structure(caps, 0);
70         format = GST_VIDEO_FORMAT_ENCODED;
71         stride = 0;
72         int width = 0, height = 0;
73         gst_structure_get_int(structure, "width", &width);
74         gst_structure_get_int(structure, "height", &height);
75         if (!gst_structure_get_fraction(structure, "pixel-aspect-ratio", &pixelAspectRatioNumerator, &pixelAspectRatioDenominator)) {
76             pixelAspectRatioNumerator = 1;
77             pixelAspectRatioDenominator = 1;
78         }
79
80         size.setWidth(width);
81         size.setHeight(height);
82     } else {
83         GstVideoInfo info;
84         gst_video_info_init(&info);
85         if (!gst_video_info_from_caps(&info, caps))
86             return false;
87
88         format = GST_VIDEO_INFO_FORMAT(&info);
89         size.setWidth(GST_VIDEO_INFO_WIDTH(&info));
90         size.setHeight(GST_VIDEO_INFO_HEIGHT(&info));
91         pixelAspectRatioNumerator = GST_VIDEO_INFO_PAR_N(&info);
92         pixelAspectRatioDenominator = GST_VIDEO_INFO_PAR_D(&info);
93         stride = GST_VIDEO_INFO_PLANE_STRIDE(&info, 0);
94     }
95
96     return true;
97 }
98
99 std::optional<FloatSize> getVideoResolutionFromCaps(const GstCaps* caps)
100 {
101     if (!doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
102         GST_WARNING("Failed to get the video resolution, these are not a video caps");
103         return std::nullopt;
104     }
105
106     int width = 0, height = 0;
107     int pixelAspectRatioNumerator = 1, pixelAspectRatioDenominator = 1;
108
109     if (areEncryptedCaps(caps)) {
110         GstStructure* structure = gst_caps_get_structure(caps, 0);
111         gst_structure_get_int(structure, "width", &width);
112         gst_structure_get_int(structure, "height", &height);
113         gst_structure_get_fraction(structure, "pixel-aspect-ratio", &pixelAspectRatioNumerator, &pixelAspectRatioDenominator);
114     } else {
115         GstVideoInfo info;
116         gst_video_info_init(&info);
117         if (!gst_video_info_from_caps(&info, caps))
118             return std::nullopt;
119
120         width = GST_VIDEO_INFO_WIDTH(&info);
121         height = GST_VIDEO_INFO_HEIGHT(&info);
122         pixelAspectRatioNumerator = GST_VIDEO_INFO_PAR_N(&info);
123         pixelAspectRatioDenominator = GST_VIDEO_INFO_PAR_D(&info);
124     }
125
126     return std::make_optional(FloatSize(width, height * (static_cast<float>(pixelAspectRatioNumerator) / static_cast<float>(pixelAspectRatioDenominator))));
127 }
128
129 bool getSampleVideoInfo(GstSample* sample, GstVideoInfo& videoInfo)
130 {
131     if (!GST_IS_SAMPLE(sample))
132         return false;
133
134     GstCaps* caps = gst_sample_get_caps(sample);
135     if (!caps)
136         return false;
137
138     gst_video_info_init(&videoInfo);
139     if (!gst_video_info_from_caps(&videoInfo, caps))
140         return false;
141
142     return true;
143 }
144 #endif
145
146 GstBuffer* createGstBuffer(GstBuffer* buffer)
147 {
148     gsize bufferSize = gst_buffer_get_size(buffer);
149     GstBuffer* newBuffer = gst_buffer_new_and_alloc(bufferSize);
150
151     if (!newBuffer)
152         return 0;
153
154     gst_buffer_copy_into(newBuffer, buffer, static_cast<GstBufferCopyFlags>(GST_BUFFER_COPY_METADATA), 0, bufferSize);
155     return newBuffer;
156 }
157
158 GstBuffer* createGstBufferForData(const char* data, int length)
159 {
160     GstBuffer* buffer = gst_buffer_new_and_alloc(length);
161
162     gst_buffer_fill(buffer, 0, data, length);
163
164     return buffer;
165 }
166
167 const char* capsMediaType(const GstCaps* caps)
168 {
169     ASSERT(caps);
170     GstStructure* structure = gst_caps_get_structure(caps, 0);
171     if (!structure) {
172         GST_WARNING("caps are empty");
173         return nullptr;
174     }
175 #if ENABLE(ENCRYPTED_MEDIA)
176     if (gst_structure_has_name(structure, "application/x-cenc"))
177         return gst_structure_get_string(structure, "original-media-type");
178 #endif
179     return gst_structure_get_name(structure);
180 }
181
182 bool doCapsHaveType(const GstCaps* caps, const char* type)
183 {
184     const char* mediaType = capsMediaType(caps);
185     if (!mediaType) {
186         GST_WARNING("Failed to get MediaType");
187         return false;
188     }
189     return g_str_has_prefix(mediaType, type);
190 }
191
192 bool areEncryptedCaps(const GstCaps* caps)
193 {
194     ASSERT(caps);
195 #if ENABLE(ENCRYPTED_MEDIA)
196     GstStructure* structure = gst_caps_get_structure(caps, 0);
197     if (!structure) {
198         GST_WARNING("caps are empty");
199         return false;
200     }
201     return gst_structure_has_name(structure, "application/x-cenc");
202 #else
203     UNUSED_PARAM(caps);
204     return false;
205 #endif
206 }
207
208 char* getGstBufferDataPointer(GstBuffer* buffer)
209 {
210     GstMiniObject* miniObject = reinterpret_cast<GstMiniObject*>(buffer);
211     GstMapInfo* mapInfo = static_cast<GstMapInfo*>(gst_mini_object_get_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString)));
212     return reinterpret_cast<char*>(mapInfo->data);
213 }
214
215 void mapGstBuffer(GstBuffer* buffer, uint32_t flags)
216 {
217     GstMapInfo* mapInfo = static_cast<GstMapInfo*>(fastMalloc(sizeof(GstMapInfo)));
218     if (!gst_buffer_map(buffer, mapInfo, static_cast<GstMapFlags>(flags))) {
219         fastFree(mapInfo);
220         gst_buffer_unref(buffer);
221         return;
222     }
223
224     GstMiniObject* miniObject = reinterpret_cast<GstMiniObject*>(buffer);
225     gst_mini_object_set_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString), mapInfo, nullptr);
226 }
227
228 void unmapGstBuffer(GstBuffer* buffer)
229 {
230     GstMiniObject* miniObject = reinterpret_cast<GstMiniObject*>(buffer);
231     GstMapInfo* mapInfo = static_cast<GstMapInfo*>(gst_mini_object_steal_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString)));
232
233     if (!mapInfo)
234         return;
235
236     gst_buffer_unmap(buffer, mapInfo);
237     fastFree(mapInfo);
238 }
239
240 Vector<String> extractGStreamerOptionsFromCommandLine()
241 {
242     GUniqueOutPtr<char> contents;
243     gsize length;
244     if (!g_file_get_contents("/proc/self/cmdline", &contents.outPtr(), &length, nullptr))
245         return { };
246
247     Vector<String> options;
248     auto optionsString = String::fromUTF8(contents.get(), length);
249     optionsString.split('\0', [&options](StringView item) {
250         if (item.startsWith("--gst"))
251             options.append(item.toString());
252     });
253     return options;
254 }
255
256 bool initializeGStreamer(std::optional<Vector<String>>&& options)
257 {
258     static std::once_flag onceFlag;
259     static bool isGStreamerInitialized;
260     std::call_once(onceFlag, [options = WTFMove(options)] {
261         isGStreamerInitialized = false;
262
263 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
264         Vector<String> parameters = options.value_or(extractGStreamerOptionsFromCommandLine());
265         char** argv = g_new0(char*, parameters.size() + 2);
266         int argc = parameters.size() + 1;
267         argv[0] = g_strdup(getCurrentExecutableName().data());
268         for (unsigned i = 0; i < parameters.size(); i++)
269             argv[i + 1] = g_strdup(parameters[i].utf8().data());
270
271         GUniqueOutPtr<GError> error;
272         isGStreamerInitialized = gst_init_check(&argc, &argv, &error.outPtr());
273         ASSERT_WITH_MESSAGE(isGStreamerInitialized, "GStreamer initialization failed: %s", error ? error->message : "unknown error occurred");
274         g_strfreev(argv);
275
276         if (isFastMallocEnabled()) {
277             const char* disableFastMalloc = getenv("WEBKIT_GST_DISABLE_FAST_MALLOC");
278             if (!disableFastMalloc || !strcmp(disableFastMalloc, "0"))
279                 gst_allocator_set_default(GST_ALLOCATOR(g_object_new(gst_allocator_fast_malloc_get_type(), nullptr)));
280         }
281
282 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
283         if (isGStreamerInitialized)
284             gst_mpegts_initialize();
285 #endif
286 #endif
287     });
288     return isGStreamerInitialized;
289 }
290
291 unsigned getGstPlayFlag(const char* nick)
292 {
293     static GFlagsClass* flagsClass = static_cast<GFlagsClass*>(g_type_class_ref(g_type_from_name("GstPlayFlags")));
294     ASSERT(flagsClass);
295
296     GFlagsValue* flag = g_flags_get_value_by_nick(flagsClass, nick);
297     if (!flag)
298         return 0;
299
300     return flag->value;
301 }
302
303 // 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
304 // 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
305 // is passed around in fractional seconds, so we'll just have to assume the same.
306 uint64_t toGstUnsigned64Time(const MediaTime& mediaTime)
307 {
308     MediaTime time = mediaTime.toTimeScale(GST_SECOND);
309     if (time.isInvalid())
310         return GST_CLOCK_TIME_NONE;
311     return time.timeValue();
312 }
313
314 bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString)
315 {
316     GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string(capsString));
317     GList* candidates = gst_element_factory_list_filter(elementFactories, caps.get(), GST_PAD_SINK, false);
318     bool result = candidates;
319
320     gst_plugin_feature_list_free(candidates);
321     return result;
322 }
323
324 static void simpleBusMessageCallback(GstBus*, GstMessage* message, GstBin* pipeline)
325 {
326     switch (GST_MESSAGE_TYPE(message)) {
327     case GST_MESSAGE_ERROR:
328         GST_ERROR_OBJECT(pipeline, "Got message: %" GST_PTR_FORMAT, message);
329         {
330             WTF::String dotFileName = String::format("%s_error", GST_OBJECT_NAME(pipeline));
331             GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(pipeline, GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
332         }
333         break;
334     case GST_MESSAGE_STATE_CHANGED:
335         if (GST_MESSAGE_SRC(message) == GST_OBJECT(pipeline)) {
336             GstState oldState, newState, pending;
337             gst_message_parse_state_changed(message, &oldState, &newState, &pending);
338
339             GST_INFO_OBJECT(pipeline, "State changed (old: %s, new: %s, pending: %s)",
340                 gst_element_state_get_name(oldState),
341                 gst_element_state_get_name(newState),
342                 gst_element_state_get_name(pending));
343
344             WTF::String dotFileName = String::format("%s_%s_%s",
345                 GST_OBJECT_NAME(pipeline),
346                 gst_element_state_get_name(oldState),
347                 gst_element_state_get_name(newState));
348
349             GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
350         }
351         break;
352     default:
353         break;
354     }
355 }
356
357 void disconnectSimpleBusMessageCallback(GstElement* pipeline)
358 {
359     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline)));
360     g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(simpleBusMessageCallback), pipeline);
361 }
362
363 void connectSimpleBusMessageCallback(GstElement* pipeline)
364 {
365     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline)));
366     gst_bus_add_signal_watch_full(bus.get(), RunLoopSourcePriority::RunLoopDispatcher);
367     g_signal_connect(bus.get(), "message", G_CALLBACK(simpleBusMessageCallback), pipeline);
368 }
369
370 }
371
372 #endif // USE(GSTREAMER)