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