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