8772038c90af307a4d9078f900ff5b2ac16b6b2f
[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 void
236 gst_mf_source_object_set_flushing (GstMFSourceObject * object,
237     gboolean flushing)
238 {
239   GstMFSourceObjectClass *klass;
240
241   g_return_if_fail (GST_IS_MF_SOURCE_OBJECT (object));
242
243   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
244
245   if (flushing) {
246     if (klass->unlock)
247       klass->unlock (object);
248   } else {
249     if (klass->unlock_stop)
250       klass->unlock_stop (object);
251   }
252 }
253
254 gboolean
255 gst_mf_source_object_set_caps (GstMFSourceObject * object, GstCaps * caps)
256 {
257   GstMFSourceObjectClass *klass;
258
259   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
260
261   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
262   g_assert (klass->set_caps != nullptr);
263
264   return klass->set_caps (object, caps);
265 }
266
267 GstCaps *
268 gst_mf_source_object_get_caps (GstMFSourceObject * object)
269 {
270   GstMFSourceObjectClass *klass;
271
272   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), nullptr);
273
274   klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object);
275   g_assert (klass->get_caps != nullptr);
276
277   return klass->get_caps (object);
278 }
279
280 gboolean
281 gst_mf_source_object_set_client (GstMFSourceObject * object,
282     GstElement * client)
283 {
284   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE);
285
286   g_weak_ref_set (&object->client, client);
287
288   return TRUE;
289 }
290
291 GstClockTime
292 gst_mf_source_object_get_running_time (GstMFSourceObject * object)
293 {
294   GstElement *client = nullptr;
295   GstClockTime timestamp = GST_CLOCK_TIME_NONE;
296
297   g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_CLOCK_TIME_NONE);
298
299   client = (GstElement *) g_weak_ref_get (&object->client);
300   if (client) {
301     GstClockTime basetime = client->base_time;
302     GstClock *clock;
303
304     clock = gst_element_get_clock (client);
305     if (clock) {
306       GstClockTime now;
307
308       now = gst_clock_get_time (clock);
309       timestamp = now - basetime;
310       gst_object_unref (clock);
311     }
312
313     gst_object_unref (client);
314   }
315
316   return timestamp;
317 }
318
319 static gboolean
320 gst_mf_source_object_use_winrt_api (void)
321 {
322   static gsize check_once = 0;
323   static gboolean ret = FALSE;
324
325   if (g_once_init_enter (&check_once)) {
326 #if (!GST_MF_WINAPI_APP)
327     /* WinRT is not supported, always false */
328     ret = FALSE;
329 #else
330 #if (!GST_MF_WINAPI_DESKTOP)
331     /* WinRT is supported but desktop API was disabled,
332      * always true */
333     ret = TRUE;
334 #else
335     /* Both app and desktop APIs were enabled, check user choice */
336     {
337       const gchar *env;
338
339       env = g_getenv ("GST_USE_MF_WINRT_CAPTURE");
340       if (env && g_str_has_prefix (env, "1"))
341         ret = TRUE;
342       else
343         ret = FALSE;
344     }
345 #endif
346 #endif
347     g_once_init_leave (&check_once, 1);
348   }
349
350   return ret;
351 }
352
353 GstMFSourceObject *
354 gst_mf_source_object_new (GstMFSourceType type, gint device_index,
355     const gchar * device_name, const gchar * device_path, gpointer dispatcher)
356 {
357 #if (!GST_MF_WINAPI_APP)
358   GST_INFO ("Try IMFSourceReader implementation");
359   return gst_mf_source_reader_new (type,
360       device_index, device_name, device_path);
361 #else
362 #if (!GST_MF_WINAPI_DESKTOP)
363   GST_INFO ("Try WinRT implementation");
364   return gst_mf_capture_winrt_new (type,
365       device_index, device_name, device_path, dispatcher);
366 #else
367   if (gst_mf_source_object_use_winrt_api ()) {
368     GST_INFO ("Both Desktop and WinRT APIs were enabled, user choice: WinRT");
369     return gst_mf_capture_winrt_new (type,
370         device_index, device_name, device_path, dispatcher);
371   } else {
372     GST_INFO
373         ("Both Desktop and WinRT APIs were enabled, default: IMFSourceReader");
374     return gst_mf_source_reader_new (type,
375         device_index, device_name, device_path);
376   }
377 #endif
378 #endif
379   g_assert_not_reached ();
380
381   return nullptr;
382 }
383
384 gint
385 gst_mf_source_object_caps_compare (GstCaps * caps1, GstCaps * caps2)
386 {
387   GstStructure *s1, *s2;
388   const gchar *n1, *n2;
389   gboolean m1_is_raw, m2_is_raw;
390   gint w1 = 0, h1 = 0, w2 = 0, h2 = 0;
391   gint r1, r2;
392   gint num1 = 0, den1 = 1, num2 = 0, den2 = 1;
393   gint fraction_cmp;
394
395   /* sorting priority
396    * - raw video > comprssed
397    *   - raw video format
398    * - higher resolution
399    * - higher framerate
400    */
401   s1 = gst_caps_get_structure (caps1, 0);
402   n1 = gst_structure_get_name (s1);
403
404   s2 = gst_caps_get_structure (caps2, 0);
405   n2 = gst_structure_get_name (s2);
406
407   m1_is_raw = g_strcmp0 (n1, "video/x-raw") == 0;
408   m2_is_raw = g_strcmp0 (n2, "video/x-raw") == 0;
409
410   if (m1_is_raw && !m2_is_raw)
411     return -1;
412   else if (!m1_is_raw && m2_is_raw)
413     return 1;
414
415   /* if both are raw formats */
416   if (m1_is_raw) {
417     gint format_cmp = g_strcmp0 (gst_structure_get_string (s1, "format"),
418         gst_structure_get_string (s2, "format"));
419     if (format_cmp)
420       return format_cmp;
421   }
422
423   /* resolution */
424   gst_structure_get_int (s1, "width", &w1);
425   gst_structure_get_int (s1, "height", &h1);
426   gst_structure_get_int (s2, "width", &w2);
427   gst_structure_get_int (s2, "height", &h2);
428
429   r1 = w1 * h1;
430   r2 = w2 * h2;
431
432   /* higher resolution first */
433   if (r1 != r2)
434     return r2 - r1;
435
436   gst_structure_get_fraction (s1, "framerate", &num1, &den1);
437   gst_structure_get_fraction (s2, "framerate", &num2, &den2);
438
439   fraction_cmp = gst_util_fraction_compare (num1, den1, num2, den2);
440
441   /* higher framerate first */
442   return fraction_cmp * -1;
443 }