mfvideosrc: Add support for DirectShow capture filter
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / mediafoundation / gstmfsourceobject.cpp
1 /* GStreamer
2  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstmfconfig.h"
26
27 #include "gstmfsourceobject.h"
28
29 #if GST_MF_WINAPI_APP
30 #include "gstmfcapturewinrt.h"
31 #endif
32 #if GST_MF_WINAPI_DESKTOP
33 #include "gstmfsourcereader.h"
34 #endif
35
36 GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug);
37 #define GST_CAT_DEFAULT gst_mf_source_object_debug
38
39 enum
40 {
41   PROP_0,
42   PROP_DEVICE_PATH,
43   PROP_DEVICE_NAME,
44   PROP_DEVICE_INDEX,
45   PROP_SOURCE_TYPE,
46 };
47
48 #define DEFAULT_DEVICE_PATH         nullptr
49 #define DEFAULT_DEVICE_NAME         nullptr
50 #define DEFAULT_DEVICE_INDEX        -1
51 #define DEFAULT_SOURCE_TYPE        GST_MF_SOURCE_TYPE_VIDEO
52
53 GType
54 gst_mf_source_type_get_type (void)
55 {
56   static GType source_type = 0;
57
58   static const GEnumValue source_types[] = {
59     {GST_MF_SOURCE_TYPE_VIDEO, "Video", "video"},
60     {0, nullptr, nullptr}
61   };
62
63   if (!source_type) {
64     source_type = g_enum_register_static ("GstMFSourceMode", source_types);
65   }
66
67   return source_type;
68 }
69
70 static void gst_mf_source_object_finalize (GObject * object);
71 static void gst_mf_source_object_get_property (GObject * object, guint prop_id,
72     GValue * value, GParamSpec * pspec);
73 static void gst_mf_source_object_set_property (GObject * object, guint prop_id,
74     const GValue * value, GParamSpec * pspec);
75
76 #define gst_mf_source_object_parent_class parent_class
77 G_DEFINE_ABSTRACT_TYPE (GstMFSourceObject, gst_mf_source_object,
78     GST_TYPE_OBJECT);
79
80 static void
81 gst_mf_source_object_class_init (GstMFSourceObjectClass * klass)
82 {
83   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
84   GParamFlags flags =
85       (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
86       G_PARAM_STATIC_STRINGS);
87
88   gobject_class->finalize = gst_mf_source_object_finalize;
89   gobject_class->get_property = gst_mf_source_object_get_property;
90   gobject_class->set_property = gst_mf_source_object_set_property;
91
92   g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
93       g_param_spec_string ("device-path", "Device Path",
94           "The device path", DEFAULT_DEVICE_PATH, flags));
95   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
96       g_param_spec_string ("device-name", "Device Name",
97           "The human-readable device name", DEFAULT_DEVICE_NAME, flags));
98   g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
99       g_param_spec_int ("device-index", "Device Index",
100           "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
101           flags));
102   g_object_class_install_property (gobject_class, PROP_SOURCE_TYPE,
103       g_param_spec_enum ("source-type", "Source Type",
104           "Source Type", GST_TYPE_MF_SOURCE_TYPE, DEFAULT_SOURCE_TYPE, flags));
105 }
106
107 static void
108 gst_mf_source_object_init (GstMFSourceObject * self)
109 {
110   self->device_index = DEFAULT_DEVICE_INDEX;
111   self->source_type = DEFAULT_SOURCE_TYPE;
112
113   g_weak_ref_init (&self->client, nullptr);
114 }
115
116 static void
117 gst_mf_source_object_finalize (GObject * object)
118 {
119   GstMFSourceObject *self = GST_MF_SOURCE_OBJECT (object);
120
121   g_free (self->device_path);
122   g_free (self->device_name);
123
124   g_weak_ref_clear (&self->client);
125
126   G_OBJECT_CLASS (parent_class)->finalize (object);
127 }
128
129 static void
130 gst_mf_source_object_get_property (GObject * object, guint prop_id,
131     GValue * value, GParamSpec * pspec)
132 {
133   GstMFSourceObject *self = GST_MF_SOURCE_OBJECT (object);
134
135   switch (prop_id) {
136     case PROP_DEVICE_PATH:
137       g_value_set_string (value, self->device_path);
138       break;
139     case PROP_DEVICE_NAME:
140       g_value_set_string (value, self->device_name);
141       break;
142     case PROP_DEVICE_INDEX:
143       g_value_set_int (value, self->device_index);
144       break;
145     case PROP_SOURCE_TYPE:
146       g_value_set_enum (value, self->source_type);
147       break;
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150       break;
151   }
152 }
153
154 static void
155 gst_mf_source_object_set_property (GObject * object, guint prop_id,
156     const GValue * value, GParamSpec * pspec)
157 {
158   GstMFSourceObject *self = GST_MF_SOURCE_OBJECT (object);
159
160   switch (prop_id) {
161     case PROP_DEVICE_PATH:
162       g_free (self->device_path);
163       self->device_path = g_value_dup_string (value);
164       break;
165     case PROP_DEVICE_NAME:
166       g_free (self->device_name);
167       self->device_name = g_value_dup_string (value);
168       break;
169     case PROP_DEVICE_INDEX:
170       self->device_index = g_value_get_int (value);
171       break;
172     case PROP_SOURCE_TYPE:
173       self->source_type = (GstMFSourceType) g_value_get_enum (value);
174       break;
175     default:
176       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177       break;
178   }
179 }
180
181 gboolean
182 gst_mf_source_object_start (GstMFSourceObject * object)
183 {
184   GstMFSourceObjectClass *klass;
185
186   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
187
188   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
189   g_assert (klass->start != nullptr);
190
191   return klass->start (object);
192 }
193
194 gboolean
195 gst_mf_source_object_stop (GstMFSourceObject * object)
196 {
197   GstMFSourceObjectClass *klass;
198
199   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
200
201   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
202   g_assert (klass->stop != nullptr);
203
204   return klass->stop (object);
205 }
206
207 GstFlowReturn
208 gst_mf_source_object_fill (GstMFSourceObject * object, GstBuffer * buffer)
209 {
210   GstMFSourceObjectClass *klass;
211
212   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_FLOW_ERROR);
213   g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
214
215   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
216   g_assert (klass->fill != nullptr);
217
218   return klass->fill (object, buffer);
219 }
220
221 GstFlowReturn
222 gst_mf_source_object_create (GstMFSourceObject * object, GstBuffer ** buffer)
223 {
224   GstMFSourceObjectClass *klass;
225
226   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_FLOW_ERROR);
227   g_return_val_if_fail (buffer != nullptr, GST_FLOW_ERROR);
228
229   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
230   g_assert (klass->create != nullptr);
231
232   return klass->create (object, buffer);
233 }
234
235 GstFlowReturn
236 gst_mf_source_object_get_sample (GstMFSourceObject * object,
237     GstSample ** sample)
238 {
239   GstMFSourceObjectClass *klass;
240
241   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_FLOW_ERROR);
242   g_return_val_if_fail (sample != nullptr, GST_FLOW_ERROR);
243
244   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
245   g_assert (klass->get_sample != nullptr);
246
247   return klass->get_sample (object, sample);
248 }
249
250 void
251 gst_mf_source_object_set_flushing (GstMFSourceObject * object,
252     gboolean flushing)
253 {
254   GstMFSourceObjectClass *klass;
255
256   g_return_if_fail (GST_IS_MF_SOURCE_OBJECT (object));
257
258   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
259
260   if (flushing) {
261     if (klass->unlock)
262       klass->unlock (object);
263   } else {
264     if (klass->unlock_stop)
265       klass->unlock_stop (object);
266   }
267 }
268
269 gboolean
270 gst_mf_source_object_set_caps (GstMFSourceObject * object, GstCaps * caps)
271 {
272   GstMFSourceObjectClass *klass;
273
274   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
275
276   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
277   g_assert (klass->set_caps != nullptr);
278
279   return klass->set_caps (object, caps);
280 }
281
282 GstCaps *
283 gst_mf_source_object_get_caps (GstMFSourceObject * object)
284 {
285   GstMFSourceObjectClass *klass;
286
287   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), nullptr);
288
289   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
290   g_assert (klass->get_caps != nullptr);
291
292   return klass->get_caps (object);
293 }
294
295 gboolean
296 gst_mf_source_object_set_client (GstMFSourceObject * object,
297     GstElement * client)
298 {
299   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
300
301   g_weak_ref_set (&object->client, client);
302
303   return TRUE;
304 }
305
306 GstClockTime
307 gst_mf_source_object_get_running_time (GstMFSourceObject * object)
308 {
309   GstElement *client = nullptr;
310   GstClockTime timestamp = GST_CLOCK_TIME_NONE;
311
312   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_CLOCK_TIME_NONE);
313
314   client = (GstElement *) g_weak_ref_get (&object->client);
315   if (client) {
316     GstClockTime basetime = client->base_time;
317     GstClock *clock;
318
319     clock = gst_element_get_clock (client);
320     if (clock) {
321       GstClockTime now;
322
323       now = gst_clock_get_time (clock);
324       timestamp = now - basetime;
325       gst_object_unref (clock);
326     }
327
328     gst_object_unref (client);
329   }
330
331   return timestamp;
332 }
333
334 static gboolean
335 gst_mf_source_object_use_winrt_api (void)
336 {
337   static gsize check_once = 0;
338   static gboolean ret = FALSE;
339
340   if (g_once_init_enter (&check_once)) {
341 #if (!GST_MF_WINAPI_APP)
342     /* WinRT is not supported, always false */
343     ret = FALSE;
344 #else
345 #if (!GST_MF_WINAPI_DESKTOP)
346     /* WinRT is supported but desktop API was disabled,
347      * always true */
348     ret = TRUE;
349 #else
350     /* Both app and desktop APIs were enabled, check user choice */
351     {
352       const gchar *env;
353
354       env = g_getenv ("GST_USE_MF_WINRT_CAPTURE");
355       if (env && g_str_has_prefix (env, "1"))
356         ret = TRUE;
357       else
358         ret = FALSE;
359     }
360 #endif
361 #endif
362     g_once_init_leave (&check_once, 1);
363   }
364
365   return ret;
366 }
367
368 GstMFSourceObject *
369 gst_mf_source_object_new (GstMFSourceType type, gint device_index,
370     const gchar * device_name, const gchar * device_path, gpointer dispatcher)
371 {
372 #if (!GST_MF_WINAPI_APP)
373   GST_INFO ("Try IMFSourceReader implementation");
374   return gst_mf_source_reader_new (type,
375       device_index, device_name, device_path);
376 #else
377 #if (!GST_MF_WINAPI_DESKTOP)
378   GST_INFO ("Try WinRT implementation");
379   return gst_mf_capture_winrt_new (type,
380       device_index, device_name, device_path, dispatcher);
381 #else
382   if (gst_mf_source_object_use_winrt_api ()) {
383     GST_INFO ("Both Desktop and WinRT APIs were enabled, user choice: WinRT");
384     return gst_mf_capture_winrt_new (type,
385         device_index, device_name, device_path, dispatcher);
386   } else {
387     GST_INFO
388         ("Both Desktop and WinRT APIs were enabled, default: IMFSourceReader");
389     return gst_mf_source_reader_new (type,
390         device_index, device_name, device_path);
391   }
392 #endif
393 #endif
394   g_assert_not_reached ();
395
396   return nullptr;
397 }
398
399 gint
400 gst_mf_source_object_caps_compare (GstCaps * caps1, GstCaps * caps2)
401 {
402   GstStructure *s1, *s2;
403   const gchar *n1, *n2;
404   gboolean m1_is_raw, m2_is_raw;
405   gint w1 = 0, h1 = 0, w2 = 0, h2 = 0;
406   gint r1, r2;
407   gint num1 = 0, den1 = 1, num2 = 0, den2 = 1;
408   gint fraction_cmp;
409
410   /* sorting priority
411    * - raw video > comprssed
412    *   - raw video format
413    * - higher resolution
414    * - higher framerate
415    */
416   s1 = gst_caps_get_structure (caps1, 0);
417   n1 = gst_structure_get_name (s1);
418
419   s2 = gst_caps_get_structure (caps2, 0);
420   n2 = gst_structure_get_name (s2);
421
422   m1_is_raw = g_strcmp0 (n1, "video/x-raw") == 0;
423   m2_is_raw = g_strcmp0 (n2, "video/x-raw") == 0;
424
425   if (m1_is_raw && !m2_is_raw)
426     return -1;
427   else if (!m1_is_raw && m2_is_raw)
428     return 1;
429
430   /* if both are raw formats */
431   if (m1_is_raw) {
432     gint format_cmp = g_strcmp0 (gst_structure_get_string (s1, "format"),
433         gst_structure_get_string (s2, "format"));
434     if (format_cmp)
435       return format_cmp;
436   }
437
438   /* resolution */
439   gst_structure_get_int (s1, "width", &w1);
440   gst_structure_get_int (s1, "height", &h1);
441   gst_structure_get_int (s2, "width", &w2);
442   gst_structure_get_int (s2, "height", &h2);
443
444   r1 = w1 * h1;
445   r2 = w2 * h2;
446
447   /* higher resolution first */
448   if (r1 != r2)
449     return r2 - r1;
450
451   gst_structure_get_fraction (s1, "framerate", &num1, &den1);
452   gst_structure_get_fraction (s2, "framerate", &num2, &den2);
453
454   fraction_cmp = gst_util_fraction_compare (num1, den1, num2, den2);
455
456   /* higher framerate first */
457   return fraction_cmp * -1;
458 }