efd137d80fa52e42d147e045acadc2af44c55569
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / mediafoundation / gstmfvideosrc.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 /**
22  * SECTION:element-mfvideosrc
23  * @title: mfvideosrc
24  *
25  * Provides video capture from the Microsoft Media Foundation API.
26  *
27  * ## Example pipelines
28  * |[
29  * gst-launch-1.0 -v mfvideosrc ! fakesink
30  * ]| Capture from the default video capture device and render to fakesink.
31  *
32  * |[
33  * gst-launch-1.0 -v mfvideosrc device-index=1 ! fakesink
34  * ]| Capture from the second video device (if available) and render to fakesink.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include "gstmfconfig.h"
42
43 #include "gstmfvideosrc.h"
44 #include "gstmfutils.h"
45 #include "gstmfsourceobject.h"
46 #include <string.h>
47
48 GST_DEBUG_CATEGORY (gst_mf_video_src_debug);
49 #define GST_CAT_DEFAULT gst_mf_video_src_debug
50
51 #if (GST_MF_WINAPI_APP && !GST_MF_WINAPI_DESKTOP)
52 /* FIXME: need support JPEG for UWP */
53 #define SRC_TEMPLATE_CAPS \
54     GST_VIDEO_CAPS_MAKE (GST_MF_VIDEO_FORMATS)
55 #else
56 #define SRC_TEMPLATE_CAPS \
57     GST_VIDEO_CAPS_MAKE (GST_MF_VIDEO_FORMATS) "; " \
58         "image/jpeg, width = " GST_VIDEO_SIZE_RANGE ", " \
59         "height = " GST_VIDEO_SIZE_RANGE ", " \
60         "framerate = " GST_VIDEO_FPS_RANGE
61 #endif
62
63 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
64     GST_PAD_SRC,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS (SRC_TEMPLATE_CAPS));
67
68 struct _GstMFVideoSrc
69 {
70   GstPushSrc parent;
71
72   GstMFSourceObject *source;
73   gboolean started;
74   GstVideoInfo info;
75
76   guint64 n_frames;
77   GstClockTime latency;
78
79   /* properties */
80   gchar *device_path;
81   gchar *device_name;
82   gint device_index;
83   gpointer dispatcher;
84 };
85
86 enum
87 {
88   PROP_0,
89   PROP_DEVICE_PATH,
90   PROP_DEVICE_NAME,
91   PROP_DEVICE_INDEX,
92   PROP_DISPATCHER,
93 };
94
95 #define DEFAULT_DEVICE_PATH     nullptr
96 #define DEFAULT_DEVICE_NAME     nullptr
97 #define DEFAULT_DEVICE_INDEX    -1
98
99 static void gst_mf_video_src_finalize (GObject * object);
100 static void gst_mf_video_src_get_property (GObject * object, guint prop_id,
101     GValue * value, GParamSpec * pspec);
102 static void gst_mf_video_src_set_property (GObject * object, guint prop_id,
103     const GValue * value, GParamSpec * pspec);
104
105 static gboolean gst_mf_video_src_start (GstBaseSrc * src);
106 static gboolean gst_mf_video_src_stop (GstBaseSrc * src);
107 static gboolean gst_mf_video_src_set_caps (GstBaseSrc * src, GstCaps * caps);
108 static GstCaps *gst_mf_video_src_get_caps (GstBaseSrc * src, GstCaps * filter);
109 static GstCaps *gst_mf_video_src_fixate (GstBaseSrc * src, GstCaps * caps);
110 static gboolean gst_mf_video_src_unlock (GstBaseSrc * src);
111 static gboolean gst_mf_video_src_unlock_stop (GstBaseSrc * src);
112 static gboolean gst_mf_video_src_query (GstBaseSrc * src, GstQuery * query);
113
114 static GstFlowReturn gst_mf_video_src_create (GstPushSrc * pushsrc,
115     GstBuffer ** buffer);
116
117 #define gst_mf_video_src_parent_class parent_class
118 G_DEFINE_TYPE (GstMFVideoSrc, gst_mf_video_src, GST_TYPE_PUSH_SRC);
119
120 static void
121 gst_mf_video_src_class_init (GstMFVideoSrcClass * klass)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
125   GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
126   GstPushSrcClass *pushsrc_class = GST_PUSH_SRC_CLASS (klass);
127   GParamFlags flags = (GParamFlags) (G_PARAM_READWRITE |
128       GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS);
129
130   gobject_class->finalize = gst_mf_video_src_finalize;
131   gobject_class->get_property = gst_mf_video_src_get_property;
132   gobject_class->set_property = gst_mf_video_src_set_property;
133
134   g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
135       g_param_spec_string ("device-path", "Device Path",
136           "The device path", DEFAULT_DEVICE_PATH, flags));
137   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
138       g_param_spec_string ("device-name", "Device Name",
139           "The human-readable device name", DEFAULT_DEVICE_NAME, flags));
140   g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
141       g_param_spec_int ("device-index", "Device Index",
142           "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
143           flags));
144 #if GST_MF_WINAPI_APP
145   /**
146    * GstMFVideoSrc:dispatcher:
147    *
148    * ICoreDispatcher COM object used for activating device from UI thread.
149    *
150    * Since: 1.18
151    */
152   g_object_class_install_property (gobject_class, PROP_DISPATCHER,
153       g_param_spec_pointer ("dispatcher", "Dispatcher",
154           "ICoreDispatcher COM object to use. In order for application to ask "
155           "permission of capture device, device activation should be running "
156           "on UI thread via ICoreDispatcher. This element will increase "
157           "the reference count of given ICoreDispatcher and release it after "
158           "use. Therefore, caller does not need to consider additional "
159           "reference count management",
160           (GParamFlags) (GST_PARAM_CONDITIONALLY_AVAILABLE |
161               GST_PARAM_MUTABLE_READY | G_PARAM_WRITABLE |
162               G_PARAM_STATIC_STRINGS)));
163 #endif
164
165   gst_element_class_set_static_metadata (element_class,
166       "Media Foundation Video Source",
167       "Source/Video/Hardware",
168       "Capture video stream through Windows Media Foundation",
169       "Seungha Yang <seungha.yang@navercorp.com>");
170
171   gst_element_class_add_static_pad_template (element_class, &src_template);
172
173   basesrc_class->start = GST_DEBUG_FUNCPTR (gst_mf_video_src_start);
174   basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_mf_video_src_stop);
175   basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_mf_video_src_set_caps);
176   basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_mf_video_src_get_caps);
177   basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_mf_video_src_fixate);
178   basesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_mf_video_src_unlock);
179   basesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_mf_video_src_unlock_stop);
180   basesrc_class->query = GST_DEBUG_FUNCPTR (gst_mf_video_src_query);
181
182   pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_mf_video_src_create);
183
184   GST_DEBUG_CATEGORY_INIT (gst_mf_video_src_debug, "mfvideosrc", 0,
185       "mfvideosrc");
186 }
187
188 static void
189 gst_mf_video_src_init (GstMFVideoSrc * self)
190 {
191   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
192   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
193
194   self->device_index = DEFAULT_DEVICE_INDEX;
195 }
196
197 static void
198 gst_mf_video_src_finalize (GObject * object)
199 {
200   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (object);
201
202   g_free (self->device_name);
203   g_free (self->device_path);
204
205   G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207
208 static void
209 gst_mf_video_src_get_property (GObject * object, guint prop_id, GValue * value,
210     GParamSpec * pspec)
211 {
212   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (object);
213
214   switch (prop_id) {
215     case PROP_DEVICE_PATH:
216       g_value_set_string (value, self->device_path);
217       break;
218     case PROP_DEVICE_NAME:
219       g_value_set_string (value, self->device_name);
220       break;
221     case PROP_DEVICE_INDEX:
222       g_value_set_int (value, self->device_index);
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227   }
228 }
229
230 static void
231 gst_mf_video_src_set_property (GObject * object, guint prop_id,
232     const GValue * value, GParamSpec * pspec)
233 {
234   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (object);
235
236   switch (prop_id) {
237     case PROP_DEVICE_PATH:
238       g_free (self->device_path);
239       self->device_path = g_value_dup_string (value);
240       break;
241     case PROP_DEVICE_NAME:
242       g_free (self->device_name);
243       self->device_name = g_value_dup_string (value);
244       break;
245     case PROP_DEVICE_INDEX:
246       self->device_index = g_value_get_int (value);
247       break;
248 #if GST_MF_WINAPI_APP
249     case PROP_DISPATCHER:
250       self->dispatcher = g_value_get_pointer (value);
251       break;
252 #endif
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256   }
257 }
258
259 static gboolean
260 gst_mf_video_src_start (GstBaseSrc * src)
261 {
262   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
263
264   GST_DEBUG_OBJECT (self, "Start");
265
266   self->source = gst_mf_source_object_new (GST_MF_SOURCE_TYPE_VIDEO,
267       self->device_index, self->device_name, self->device_path, nullptr);
268
269   self->n_frames = 0;
270   self->latency = 0;
271
272   if (!self->source) {
273     GST_ERROR_OBJECT (self, "Couldn't create capture object");
274     return FALSE;
275   }
276
277   gst_mf_source_object_set_client (self->source, GST_ELEMENT (self));
278
279   return TRUE;
280 }
281
282 static gboolean
283 gst_mf_video_src_stop (GstBaseSrc * src)
284 {
285   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
286
287   GST_DEBUG_OBJECT (self, "Stop");
288
289   if (self->source) {
290     gst_mf_source_object_stop (self->source);
291     gst_object_unref (self->source);
292     self->source = nullptr;
293   }
294
295   self->started = FALSE;
296
297   return TRUE;
298 }
299
300 static gboolean
301 gst_mf_video_src_set_caps (GstBaseSrc * src, GstCaps * caps)
302 {
303   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
304
305   GST_DEBUG_OBJECT (self, "Set caps %" GST_PTR_FORMAT, caps);
306
307   if (!self->source) {
308     GST_ERROR_OBJECT (self, "No capture engine yet");
309     return FALSE;
310   }
311
312   if (!gst_mf_source_object_set_caps (self->source, caps)) {
313     GST_ERROR_OBJECT (self, "CaptureEngine couldn't accept caps");
314     return FALSE;
315   }
316
317   gst_video_info_from_caps (&self->info, caps);
318   if (GST_VIDEO_INFO_FORMAT (&self->info) != GST_VIDEO_FORMAT_ENCODED)
319     gst_base_src_set_blocksize (src, GST_VIDEO_INFO_SIZE (&self->info));
320
321   return TRUE;
322 }
323
324 static GstCaps *
325 gst_mf_video_src_get_caps (GstBaseSrc * src, GstCaps * filter)
326 {
327   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
328   GstCaps *caps = nullptr;
329
330   if (self->source)
331     caps = gst_mf_source_object_get_caps (self->source);
332
333   if (!caps)
334     caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src));
335
336   if (filter) {
337     GstCaps *filtered =
338         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
339     gst_caps_unref (caps);
340     caps = filtered;
341   }
342
343   GST_DEBUG_OBJECT (self, "Returning caps %" GST_PTR_FORMAT, caps);
344
345   return caps;
346 }
347
348 static GstCaps *
349 gst_mf_video_src_fixate (GstBaseSrc * src, GstCaps * caps)
350 {
351   GstStructure *structure;
352   GstCaps *fixated_caps;
353   gint i;
354
355   fixated_caps = gst_caps_make_writable (caps);
356
357   for (i = 0; i < gst_caps_get_size (fixated_caps); ++i) {
358     structure = gst_caps_get_structure (fixated_caps, i);
359     gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
360     gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
361     gst_structure_fixate_field_nearest_fraction (structure, "framerate",
362         G_MAXINT, 1);
363   }
364
365   fixated_caps = gst_caps_fixate (fixated_caps);
366
367   return fixated_caps;
368 }
369
370 static gboolean
371 gst_mf_video_src_unlock (GstBaseSrc * src)
372 {
373   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
374
375   if (self->source)
376     gst_mf_source_object_set_flushing (self->source, TRUE);
377
378   return TRUE;
379 }
380
381 static gboolean
382 gst_mf_video_src_unlock_stop (GstBaseSrc * src)
383 {
384   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
385
386   if (self->source)
387     gst_mf_source_object_set_flushing (self->source, FALSE);
388
389   return TRUE;
390 }
391
392 static gboolean
393 gst_mf_video_src_query (GstBaseSrc * src, GstQuery * query)
394 {
395   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (src);
396
397   switch (GST_QUERY_TYPE (query)) {
398     case GST_QUERY_LATENCY:
399       if (self->started) {
400         gst_query_set_latency (query, TRUE, 0, self->latency);
401
402         return TRUE;
403       }
404       break;
405     default:
406       break;
407   }
408
409   return GST_BASE_SRC_CLASS (parent_class)->query (src, query);
410 }
411
412 static GstFlowReturn
413 gst_mf_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
414 {
415   GstMFVideoSrc *self = GST_MF_VIDEO_SRC (pushsrc);
416   GstFlowReturn ret = GST_FLOW_OK;
417   GstBuffer *buf = nullptr;
418   GstClock *clock;
419   GstClockTime running_time = GST_CLOCK_TIME_NONE;
420   GstClockTimeDiff diff;
421
422   if (!self->started) {
423     if (!gst_mf_source_object_start (self->source)) {
424       GST_ERROR_OBJECT (self, "Failed to start capture object");
425
426       return GST_FLOW_ERROR;
427     }
428
429     self->started = TRUE;
430   }
431
432   if (GST_VIDEO_INFO_FORMAT (&self->info) != GST_VIDEO_FORMAT_ENCODED) {
433     ret = GST_BASE_SRC_CLASS (parent_class)->alloc (GST_BASE_SRC (self), 0,
434         GST_VIDEO_INFO_SIZE (&self->info), &buf);
435
436     if (ret != GST_FLOW_OK)
437       return ret;
438
439     ret = gst_mf_source_object_fill (self->source, buf);
440   } else {
441     ret = gst_mf_source_object_create (self->source, &buf);
442   }
443
444   if (ret != GST_FLOW_OK)
445     return ret;
446
447   GST_BUFFER_OFFSET (buf) = self->n_frames;
448   GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
449   self->n_frames++;
450
451   GST_LOG_OBJECT (self,
452       "Captured buffer timestamp %" GST_TIME_FORMAT ", duration %"
453       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
454       GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
455
456   /* Update latency */
457   clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
458   if (clock) {
459     GstClockTime now;
460
461     now = gst_clock_get_time (clock);
462     running_time = now - GST_ELEMENT_CAST (self)->base_time;
463     gst_object_unref (clock);
464   }
465
466   diff = GST_CLOCK_DIFF (GST_BUFFER_PTS (buf), running_time);
467   if (diff > self->latency) {
468     self->latency = (GstClockTime) diff;
469     GST_DEBUG_OBJECT (self, "Updated latency value %" GST_TIME_FORMAT,
470         GST_TIME_ARGS (self->latency));
471   }
472
473   *buffer = buf;
474
475   return GST_FLOW_OK;
476 }