d4d7e50de6ad54e8cb3bbe07b20e1fe31d93efbc
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / mediafoundation / gstmfsourcereader.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 <gst/base/base.h>
26 #include <gst/video/video.h>
27 #include "gstmfsourcereader.h"
28 #include <string.h>
29 #include <wrl.h>
30 #include <string>
31 #include <vector>
32 #include <algorithm>
33
34 /* *INDENT-OFF* */
35 using namespace Microsoft::WRL;
36 /* *INDENT-ON* */
37
38 GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug);
39 #define GST_CAT_DEFAULT gst_mf_source_object_debug
40
41 typedef struct _GstMFStreamMediaType
42 {
43   IMFMediaType *media_type;
44
45   /* the stream index of media type */
46   guint stream_index;
47
48   /* the media index in the stream index */
49   guint media_type_index;
50
51   GstCaps *caps;
52 } GstMFStreamMediaType;
53
54 typedef struct
55 {
56   IMFActivate *handle;
57   guint index;
58   gchar *name;
59   gchar *path;
60 } GstMFDeviceActivate;
61
62 struct _GstMFSourceReader
63 {
64   GstMFSourceObject parent;
65
66   GThread *thread;
67   GMutex lock;
68   GCond cond;
69   GMainContext *context;
70   GMainLoop *loop;
71
72   /* protected by lock */
73   GstQueueArray *queue;
74
75   IMFActivate *activate;
76   IMFMediaSource *source;
77   IMFSourceReader *reader;
78
79   GstCaps *supported_caps;
80   GList *media_types;
81   GstMFStreamMediaType *cur_type;
82   GstVideoInfo info;
83
84   gboolean top_down_image;
85
86   gboolean flushing;
87 };
88
89 typedef struct _GstMFSourceReaderSample
90 {
91   IMFSample *sample;
92   GstClockTime clock_time;
93 } GstMFSourceReaderSample;
94
95 static void gst_mf_source_reader_constructed (GObject * object);
96 static void gst_mf_source_reader_finalize (GObject * object);
97
98 static gboolean gst_mf_source_reader_start (GstMFSourceObject * object);
99 static gboolean gst_mf_source_reader_stop (GstMFSourceObject * object);
100 static GstFlowReturn gst_mf_source_reader_fill (GstMFSourceObject * object,
101     GstBuffer * buffer);
102 static GstFlowReturn gst_mf_source_reader_create (GstMFSourceObject * object,
103     GstBuffer ** buffer);
104 static gboolean gst_mf_source_reader_unlock (GstMFSourceObject * object);
105 static gboolean gst_mf_source_reader_unlock_stop (GstMFSourceObject * object);
106 static GstCaps *gst_mf_source_reader_get_caps (GstMFSourceObject * object);
107 static gboolean gst_mf_source_reader_set_caps (GstMFSourceObject * object,
108     GstCaps * caps);
109 static void
110 gst_mf_source_reader_sample_clear (GstMFSourceReaderSample * reader_sample);
111
112 static gboolean gst_mf_source_reader_open (GstMFSourceReader * object,
113     IMFActivate * activate);
114 static gboolean gst_mf_source_reader_close (GstMFSourceReader * object);
115 static gpointer gst_mf_source_reader_thread_func (GstMFSourceReader * self);
116 static gboolean gst_mf_source_enum_device_activate (GstMFSourceReader * self,
117     GstMFSourceType source_type, GList ** device_activates);
118 static void gst_mf_device_activate_free (GstMFDeviceActivate * activate);
119
120 #define gst_mf_source_reader_parent_class parent_class
121 G_DEFINE_TYPE (GstMFSourceReader, gst_mf_source_reader,
122     GST_TYPE_MF_SOURCE_OBJECT);
123
124 static void
125 gst_mf_source_reader_class_init (GstMFSourceReaderClass * klass)
126 {
127   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
128   GstMFSourceObjectClass *source_class = GST_MF_SOURCE_OBJECT_CLASS (klass);
129
130   gobject_class->constructed = gst_mf_source_reader_constructed;
131   gobject_class->finalize = gst_mf_source_reader_finalize;
132
133   source_class->start = GST_DEBUG_FUNCPTR (gst_mf_source_reader_start);
134   source_class->stop = GST_DEBUG_FUNCPTR (gst_mf_source_reader_stop);
135   source_class->fill = GST_DEBUG_FUNCPTR (gst_mf_source_reader_fill);
136   source_class->create = GST_DEBUG_FUNCPTR (gst_mf_source_reader_create);
137   source_class->unlock = GST_DEBUG_FUNCPTR (gst_mf_source_reader_unlock);
138   source_class->unlock_stop =
139       GST_DEBUG_FUNCPTR (gst_mf_source_reader_unlock_stop);
140   source_class->get_caps = GST_DEBUG_FUNCPTR (gst_mf_source_reader_get_caps);
141   source_class->set_caps = GST_DEBUG_FUNCPTR (gst_mf_source_reader_set_caps);
142 }
143
144 static void
145 gst_mf_source_reader_init (GstMFSourceReader * self)
146 {
147   self->queue =
148       gst_queue_array_new_for_struct (sizeof (GstMFSourceReaderSample), 2);
149   gst_queue_array_set_clear_func (self->queue,
150       (GDestroyNotify) gst_mf_source_reader_sample_clear);
151   g_mutex_init (&self->lock);
152   g_cond_init (&self->cond);
153 }
154
155 static void
156 gst_mf_source_reader_constructed (GObject * object)
157 {
158   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
159
160   self->context = g_main_context_new ();
161   self->loop = g_main_loop_new (self->context, FALSE);
162
163   /* Create a new thread to ensure that COM thread can be MTA thread */
164   g_mutex_lock (&self->lock);
165   self->thread = g_thread_new ("GstMFSourceReader",
166       (GThreadFunc) gst_mf_source_reader_thread_func, self);
167   while (!g_main_loop_is_running (self->loop))
168     g_cond_wait (&self->cond, &self->lock);
169   g_mutex_unlock (&self->lock);
170 }
171
172 static gboolean
173 gst_mf_enum_media_type_from_source_reader (IMFSourceReader * source_reader,
174     GList ** media_types)
175 {
176   gint i, j;
177   HRESULT hr;
178   GList *list = nullptr;
179   std::vector < std::string > unhandled_caps;
180
181   g_return_val_if_fail (source_reader != nullptr, FALSE);
182   g_return_val_if_fail (media_types != nullptr, FALSE);
183
184   {
185     /* Retrive only the first video stream. non-first video stream might be
186      * photo stream which doesn't seem to be working propertly in this implementation.
187      *
188      * Note: Chromium seems to be using the first video stream
189      * https://github.com/chromium/chromium/blob/ccd149af47315e4c6f2fc45d55be1b271f39062c/media/capture/video/win/video_capture_device_factory_win.cc
190      */
191     i = MF_SOURCE_READER_FIRST_VIDEO_STREAM;
192     for (j = 0;; j++) {
193       ComPtr < IMFMediaType > media_type;
194
195       hr = source_reader->GetNativeMediaType (i, j, &media_type);
196
197       if (SUCCEEDED (hr)) {
198         GstMFStreamMediaType *mtype;
199         GstCaps *caps = nullptr;
200         GstStructure *s;
201         std::string name;
202
203         caps = gst_mf_media_type_to_caps (media_type.Get ());
204
205         /* unknown format */
206         if (!caps)
207           continue;
208
209         s = gst_caps_get_structure (caps, 0);
210         name = gst_structure_get_name (s);
211         if (name != "video/x-raw" && name != "image/jpeg") {
212           auto it =
213               std::find (unhandled_caps.begin (), unhandled_caps.end (), name);
214           if (it == unhandled_caps.end ()) {
215             GST_FIXME ("Skip not supported format %s", name.c_str ());
216             unhandled_caps.push_back (name);
217           }
218           gst_caps_unref (caps);
219           continue;
220         }
221
222         mtype = g_new0 (GstMFStreamMediaType, 1);
223
224         mtype->media_type = media_type.Detach ();
225         mtype->stream_index = i;
226         mtype->media_type_index = j;
227         mtype->caps = caps;
228
229         GST_DEBUG ("StreamIndex %d, MediaTypeIndex %d, %" GST_PTR_FORMAT,
230             i, j, caps);
231
232         list = g_list_prepend (list, mtype);
233       } else if (hr == MF_E_NO_MORE_TYPES) {
234         /* no more media type in this stream index, try next stream index */
235         break;
236       } else if (hr == MF_E_INVALIDSTREAMNUMBER) {
237         /* no more streams and media types */
238         goto done;
239       } else {
240         /* undefined return */
241         goto done;
242       }
243     }
244   }
245
246 done:
247   if (!list)
248     return FALSE;
249
250   list = g_list_reverse (list);
251   *media_types = list;
252
253   return TRUE;
254 }
255
256 static void
257 gst_mf_stream_media_type_free (GstMFStreamMediaType * media_type)
258 {
259   g_return_if_fail (media_type != nullptr);
260
261   if (media_type->media_type)
262     media_type->media_type->Release ();
263
264   if (media_type->caps)
265     gst_caps_unref (media_type->caps);
266
267   g_free (media_type);
268 }
269
270 static gint
271 compare_caps_func (gconstpointer a, gconstpointer b)
272 {
273   GstMFStreamMediaType *m1, *m2;
274
275   m1 = (GstMFStreamMediaType *) a;
276   m2 = (GstMFStreamMediaType *) b;
277
278   return gst_mf_source_object_caps_compare (m1->caps, m2->caps);
279 }
280
281 static gboolean
282 gst_mf_source_reader_open (GstMFSourceReader * self, IMFActivate * activate)
283 {
284   GList *iter;
285   HRESULT hr;
286   ComPtr < IMFSourceReader > reader;
287   ComPtr < IMFMediaSource > source;
288   ComPtr < IMFAttributes > attr;
289
290   hr = activate->ActivateObject (IID_PPV_ARGS (&source));
291   if (!gst_mf_result (hr))
292     return FALSE;
293
294   hr = MFCreateAttributes (&attr, 2);
295   if (!gst_mf_result (hr))
296     return FALSE;
297
298   hr = attr->SetUINT32 (MF_READWRITE_DISABLE_CONVERTERS, TRUE);
299   if (!gst_mf_result (hr))
300     return FALSE;
301
302   hr = MFCreateSourceReaderFromMediaSource (source.Get (),
303       attr.Get (), &reader);
304   if (!gst_mf_result (hr))
305     return FALSE;
306
307   if (!gst_mf_enum_media_type_from_source_reader (reader.Get (),
308           &self->media_types)) {
309     GST_ERROR_OBJECT (self, "No available media types");
310     source->Shutdown ();
311     return FALSE;
312   }
313
314   self->activate = activate;
315   activate->AddRef ();
316   self->source = source.Detach ();
317   self->reader = reader.Detach ();
318
319   self->media_types = g_list_sort (self->media_types,
320       (GCompareFunc) compare_caps_func);
321
322   self->supported_caps = gst_caps_new_empty ();
323
324   for (iter = self->media_types; iter; iter = g_list_next (iter)) {
325     GstMFStreamMediaType *mtype = (GstMFStreamMediaType *) iter->data;
326
327     gst_caps_append (self->supported_caps, gst_caps_copy (mtype->caps));
328   }
329
330   GST_DEBUG_OBJECT (self, "Available output caps %" GST_PTR_FORMAT,
331       self->supported_caps);
332
333   return TRUE;
334 }
335
336 static gboolean
337 gst_mf_source_reader_close (GstMFSourceReader * self)
338 {
339   gst_clear_caps (&self->supported_caps);
340
341   if (self->activate) {
342     self->activate->ShutdownObject ();
343     self->activate->Release ();
344     self->activate = nullptr;
345   }
346
347   if (self->media_types) {
348     g_list_free_full (self->media_types,
349         (GDestroyNotify) gst_mf_stream_media_type_free);
350     self->media_types = nullptr;
351   }
352
353   if (self->reader) {
354     self->reader->Release ();
355     self->reader = nullptr;
356   }
357
358   if (self->source) {
359     self->source->Shutdown ();
360     self->source->Release ();
361     self->source = nullptr;
362   }
363
364   return TRUE;
365 }
366
367 static void
368 gst_mf_source_reader_finalize (GObject * object)
369 {
370   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
371
372   g_main_loop_quit (self->loop);
373   g_thread_join (self->thread);
374   g_main_loop_unref (self->loop);
375   g_main_context_unref (self->context);
376
377   gst_queue_array_free (self->queue);
378   gst_clear_caps (&self->supported_caps);
379   g_mutex_clear (&self->lock);
380   g_cond_clear (&self->cond);
381
382   G_OBJECT_CLASS (parent_class)->finalize (object);
383 }
384
385 static gboolean
386 gst_mf_source_reader_start (GstMFSourceObject * object)
387 {
388   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
389   HRESULT hr;
390   GstMFStreamMediaType *type;
391
392   if (!self->cur_type) {
393     GST_ERROR_OBJECT (self, "MediaType wasn't specified");
394     return FALSE;
395   }
396
397   type = self->cur_type;
398   self->top_down_image = TRUE;
399
400   if (GST_VIDEO_INFO_FORMAT (&self->info) != GST_VIDEO_FORMAT_ENCODED) {
401     UINT32 stride;
402     INT32 actual_stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0);
403
404     /* This MF_MT_DEFAULT_STRIDE uses UINT32 type but actual value is
405      * INT32, which can be negative in case of RGB image, and negative means
406      * its stored as bottom-up manner */
407     hr = type->media_type->GetUINT32 (MF_MT_DEFAULT_STRIDE, &stride);
408     if (gst_mf_result (hr)) {
409       actual_stride = (INT32) stride;
410       if (actual_stride < 0) {
411         if (!GST_VIDEO_INFO_IS_RGB (&self->info)) {
412           GST_ERROR_OBJECT (self,
413               "Bottom-up image is allowed only for RGB format");
414           return FALSE;
415         }
416
417         GST_DEBUG_OBJECT (self,
418             "Detected bottom-up image, stride %d", actual_stride);
419
420         self->top_down_image = FALSE;
421       }
422     } else {
423       /* If MF_MT_DEFAULT_STRIDE attribute is not specified, we can use our
424        * value */
425       type->media_type->SetUINT32 (MF_MT_DEFAULT_STRIDE,
426           (UINT32) actual_stride);
427     }
428     gst_mf_update_video_info_with_stride (&self->info,
429         std::abs (actual_stride));
430   }
431
432   hr = self->reader->SetStreamSelection (type->stream_index, TRUE);
433   if (!gst_mf_result (hr))
434     return FALSE;
435
436   hr = self->reader->SetCurrentMediaType (type->stream_index,
437       nullptr, type->media_type);
438   if (!gst_mf_result (hr))
439     return FALSE;
440
441   return TRUE;
442 }
443
444 static GstMFSourceReaderSample *
445 gst_mf_source_reader_sample_new (IMFSample * sample, GstClockTime timestamp)
446 {
447   GstMFSourceReaderSample *reader_sample = g_new0 (GstMFSourceReaderSample, 1);
448
449   reader_sample->sample = sample;
450   reader_sample->clock_time = timestamp;
451
452   return reader_sample;
453 }
454
455 static gboolean
456 gst_mf_source_reader_stop (GstMFSourceObject * object)
457 {
458   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
459
460   gst_queue_array_clear (self->queue);
461
462   return TRUE;
463 }
464
465 static GstFlowReturn
466 gst_mf_source_reader_read_sample (GstMFSourceReader * self)
467 {
468   HRESULT hr;
469   DWORD stream_flags = 0;
470   GstMFStreamMediaType *type = self->cur_type;
471   IMFSample *sample = nullptr;
472   GstMFSourceReaderSample reader_sample;
473
474   hr = self->reader->ReadSample (type->stream_index, 0, nullptr, &stream_flags,
475       nullptr, &sample);
476
477   if (!gst_mf_result (hr)) {
478     GST_ERROR_OBJECT (self, "Failed to read sample");
479     return GST_FLOW_ERROR;
480   }
481
482   if ((stream_flags & MF_SOURCE_READERF_ERROR) == MF_SOURCE_READERF_ERROR) {
483     GST_ERROR_OBJECT (self, "Error while reading sample, sample flags 0x%x",
484         stream_flags);
485     return GST_FLOW_ERROR;
486   }
487
488   if (!sample) {
489     GST_WARNING_OBJECT (self, "Empty sample");
490     return GST_FLOW_OK;
491   }
492
493   reader_sample.sample = sample;
494   reader_sample.clock_time =
495       gst_mf_source_object_get_running_time (GST_MF_SOURCE_OBJECT (self));
496
497   gst_queue_array_push_tail_struct (self->queue, &reader_sample);
498
499   return GST_FLOW_OK;
500 }
501
502 static GstFlowReturn
503 gst_mf_source_reader_get_media_buffer (GstMFSourceReader * self,
504     IMFMediaBuffer ** buffer, GstClockTime * timestamp, GstClockTime * duration)
505 {
506   GstFlowReturn ret = GST_FLOW_OK;
507   IMFSample *sample = nullptr;
508   HRESULT hr;
509   DWORD count = 0;
510   LONGLONG mf_timestamp;
511   GstMFSourceReaderSample *reader_sample = nullptr;
512
513   *buffer = nullptr;
514   *timestamp = GST_CLOCK_TIME_NONE;
515   *duration = GST_CLOCK_TIME_NONE;
516
517   while (gst_queue_array_is_empty (self->queue)) {
518     ret = gst_mf_source_reader_read_sample (self);
519     if (ret != GST_FLOW_OK)
520       return ret;
521
522     g_mutex_lock (&self->lock);
523     if (self->flushing) {
524       g_mutex_unlock (&self->lock);
525       return GST_FLOW_FLUSHING;
526     }
527     g_mutex_unlock (&self->lock);
528   }
529
530   reader_sample =
531       (GstMFSourceReaderSample *) gst_queue_array_pop_head_struct (self->queue);
532   sample = reader_sample->sample;
533   g_assert (sample);
534
535   hr = sample->GetBufferCount (&count);
536   if (!gst_mf_result (hr) || count == 0) {
537     GST_WARNING_OBJECT (self, "Empty IMFSample, read again");
538     goto done;
539   }
540
541   /* XXX: read the first buffer and ignore the others for now */
542   hr = sample->GetBufferByIndex (0, buffer);
543   if (!gst_mf_result (hr)) {
544     GST_WARNING_OBJECT (self, "Couldn't get IMFMediaBuffer from sample");
545     goto done;
546   }
547
548   hr = sample->GetSampleDuration (&mf_timestamp);
549   if (!gst_mf_result (hr)) {
550     GST_WARNING_OBJECT (self, "Couldn't get sample duration");
551     *duration = GST_CLOCK_TIME_NONE;
552   } else {
553     /* Media Foundation uses 100 nano seconds unit */
554     *duration = mf_timestamp * 100;
555   }
556
557   *timestamp = reader_sample->clock_time;
558
559 done:
560   gst_mf_source_reader_sample_clear (reader_sample);
561
562   return GST_FLOW_OK;
563 }
564
565 static GstFlowReturn
566 gst_mf_source_reader_fill (GstMFSourceObject * object, GstBuffer * buffer)
567 {
568   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
569   GstFlowReturn ret = GST_FLOW_OK;
570   ComPtr < IMFMediaBuffer > media_buffer;
571   GstVideoFrame frame;
572   BYTE *data;
573   gint i, j;
574   HRESULT hr;
575   GstClockTime timestamp = GST_CLOCK_TIME_NONE;
576   GstClockTime duration = GST_CLOCK_TIME_NONE;
577
578   do {
579     ret = gst_mf_source_reader_get_media_buffer (self,
580         media_buffer.ReleaseAndGetAddressOf (), &timestamp, &duration);
581   } while (ret == GST_FLOW_OK && !media_buffer);
582
583   if (ret != GST_FLOW_OK)
584     return ret;
585
586   hr = media_buffer->Lock (&data, nullptr, nullptr);
587   if (!gst_mf_result (hr)) {
588     GST_ERROR_OBJECT (self, "Failed to lock media buffer");
589     return GST_FLOW_ERROR;
590   }
591
592   if (!gst_video_frame_map (&frame, &self->info, buffer, GST_MAP_WRITE)) {
593     GST_ERROR_OBJECT (self, "Failed to map buffer");
594     media_buffer->Unlock ();
595     return GST_FLOW_ERROR;
596   }
597
598   if (!self->top_down_image) {
599     guint8 *src, *dst;
600     gint src_stride, dst_stride;
601     gint width, height;
602
603     /* must be single plane RGB */
604     width = GST_VIDEO_INFO_COMP_WIDTH (&self->info, 0)
605         * GST_VIDEO_INFO_COMP_PSTRIDE (&self->info, 0);
606     height = GST_VIDEO_INFO_HEIGHT (&self->info);
607
608     src_stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0);
609     dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
610
611     /* This is bottom up image, should copy lines in reverse order */
612     src = data + src_stride * (height - 1);
613     dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
614
615     for (j = 0; j < height; j++) {
616       memcpy (dst, src, width);
617       src -= src_stride;
618       dst += dst_stride;
619     }
620   } else {
621     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->info); i++) {
622       guint8 *src, *dst;
623       gint src_stride, dst_stride;
624       gint width;
625
626       src = data + GST_VIDEO_INFO_PLANE_OFFSET (&self->info, i);
627       dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, i);
628
629       src_stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, i);
630       dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, i);
631       width = GST_VIDEO_INFO_COMP_WIDTH (&self->info, i)
632           * GST_VIDEO_INFO_COMP_PSTRIDE (&self->info, i);
633
634       for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (&self->info, i); j++) {
635         memcpy (dst, src, width);
636         src += src_stride;
637         dst += dst_stride;
638       }
639     }
640   }
641
642   gst_video_frame_unmap (&frame);
643   media_buffer->Unlock ();
644
645   GST_BUFFER_PTS (buffer) = timestamp;
646   GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
647   GST_BUFFER_DURATION (buffer) = duration;
648
649   return GST_FLOW_OK;
650 }
651
652 static GstFlowReturn
653 gst_mf_source_reader_create (GstMFSourceObject * object, GstBuffer ** buffer)
654 {
655   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
656   GstFlowReturn ret = GST_FLOW_OK;
657   ComPtr < IMFMediaBuffer > media_buffer;
658   HRESULT hr;
659   BYTE *data;
660   DWORD len = 0;
661   GstBuffer *buf;
662   GstMapInfo info;
663   GstClockTime timestamp = GST_CLOCK_TIME_NONE;
664   GstClockTime duration = GST_CLOCK_TIME_NONE;
665
666   do {
667     ret = gst_mf_source_reader_get_media_buffer (self,
668         media_buffer.ReleaseAndGetAddressOf (), &timestamp, &duration);
669   } while (ret == GST_FLOW_OK && !media_buffer);
670
671   if (ret != GST_FLOW_OK)
672     return ret;
673
674   hr = media_buffer->Lock (&data, nullptr, &len);
675   if (!gst_mf_result (hr) || len == 0) {
676     GST_ERROR_OBJECT (self, "Failed to lock media buffer");
677     return GST_FLOW_ERROR;
678   }
679
680   buf = gst_buffer_new_and_alloc (len);
681   if (!buf) {
682     GST_ERROR_OBJECT (self, "Cannot allocate buffer");
683     media_buffer->Unlock ();
684     return GST_FLOW_ERROR;
685   }
686
687   gst_buffer_map (buf, &info, GST_MAP_WRITE);
688   memcpy (info.data, data, len);
689   gst_buffer_unmap (buf, &info);
690
691   media_buffer->Unlock ();
692
693   GST_BUFFER_PTS (buf) = timestamp;
694   /* Set DTS since this is compressed format */
695   GST_BUFFER_DTS (buf) = timestamp;
696   GST_BUFFER_DURATION (buf) = duration;
697
698   *buffer = buf;
699
700   return GST_FLOW_OK;
701 }
702
703 static gboolean
704 gst_mf_source_reader_unlock (GstMFSourceObject * object)
705 {
706   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
707
708   g_mutex_lock (&self->lock);
709   self->flushing = TRUE;
710   g_mutex_unlock (&self->lock);
711
712   return TRUE;
713 }
714
715 static gboolean
716 gst_mf_source_reader_unlock_stop (GstMFSourceObject * object)
717 {
718   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
719
720   g_mutex_lock (&self->lock);
721   self->flushing = FALSE;
722   g_mutex_unlock (&self->lock);
723
724   return TRUE;
725 }
726
727 static GstCaps *
728 gst_mf_source_reader_get_caps (GstMFSourceObject * object)
729 {
730   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
731
732   if (self->supported_caps)
733     return gst_caps_ref (self->supported_caps);
734
735   return nullptr;
736 }
737
738 static gboolean
739 gst_mf_source_reader_set_caps (GstMFSourceObject * object, GstCaps * caps)
740 {
741   GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
742   GList *iter;
743   GstMFStreamMediaType *best_type = nullptr;
744
745   for (iter = self->media_types; iter; iter = g_list_next (iter)) {
746     GstMFStreamMediaType *minfo = (GstMFStreamMediaType *) iter->data;
747     if (gst_caps_can_intersect (minfo->caps, caps)) {
748       best_type = minfo;
749       break;
750     }
751   }
752
753   if (!best_type) {
754     GST_ERROR_OBJECT (self,
755         "Could not determine target media type with given caps %"
756         GST_PTR_FORMAT, caps);
757
758     return FALSE;
759   }
760
761   self->cur_type = best_type;
762   gst_video_info_from_caps (&self->info, best_type->caps);
763
764   return TRUE;
765 }
766
767 static gboolean
768 gst_mf_source_reader_main_loop_running_cb (GstMFSourceReader * self)
769 {
770   GST_INFO_OBJECT (self, "Main loop running now");
771
772   g_mutex_lock (&self->lock);
773   g_cond_signal (&self->cond);
774   g_mutex_unlock (&self->lock);
775
776   return G_SOURCE_REMOVE;
777 }
778
779 static gpointer
780 gst_mf_source_reader_thread_func (GstMFSourceReader * self)
781 {
782   GstMFSourceObject *object = GST_MF_SOURCE_OBJECT (self);
783   GSource *source;
784   GList *activate_list = nullptr;
785   GstMFDeviceActivate *target = nullptr;
786   GList *iter;
787
788   CoInitializeEx (nullptr, COINIT_MULTITHREADED);
789
790   g_main_context_push_thread_default (self->context);
791
792   source = g_idle_source_new ();
793   g_source_set_callback (source,
794       (GSourceFunc) gst_mf_source_reader_main_loop_running_cb, self, nullptr);
795   g_source_attach (source, self->context);
796   g_source_unref (source);
797
798   if (!gst_mf_source_enum_device_activate (self,
799           object->source_type, &activate_list)) {
800     GST_WARNING_OBJECT (self, "No available video capture device");
801     goto run_loop;
802   }
803 #ifndef GST_DISABLE_GST_DEBUG
804   for (iter = activate_list; iter; iter = g_list_next (iter)) {
805     GstMFDeviceActivate *activate = (GstMFDeviceActivate *) iter->data;
806
807     GST_DEBUG_OBJECT (self, "device %d, name: \"%s\", path: \"%s\"",
808         activate->index, GST_STR_NULL (activate->name),
809         GST_STR_NULL (activate->path));
810   }
811 #endif
812
813   GST_DEBUG_OBJECT (self,
814       "Requested device index: %d, name: \"%s\", path \"%s\"",
815       object->device_index, GST_STR_NULL (object->device_name),
816       GST_STR_NULL (object->device_path));
817
818   for (iter = activate_list; iter; iter = g_list_next (iter)) {
819     GstMFDeviceActivate *activate = (GstMFDeviceActivate *) iter->data;
820     gboolean match;
821
822     if (object->device_path) {
823       match = g_ascii_strcasecmp (activate->path, object->device_path) == 0;
824     } else if (object->device_name) {
825       match = g_ascii_strcasecmp (activate->name, object->device_name) == 0;
826     } else if (object->device_index >= 0) {
827       match = activate->index == object->device_index;
828     } else {
829       /* pick the first entry */
830       match = TRUE;
831     }
832
833     if (match) {
834       target = activate;
835       break;
836     }
837   }
838
839   if (target) {
840     object->opened = gst_mf_source_reader_open (self, target->handle);
841
842     g_free (object->device_path);
843     object->device_path = g_strdup (target->path);
844
845     g_free (object->device_name);
846     object->device_name = g_strdup (target->name);
847
848     object->device_index = target->index;
849   }
850
851   if (activate_list)
852     g_list_free_full (activate_list,
853         (GDestroyNotify) gst_mf_device_activate_free);
854
855 run_loop:
856   GST_DEBUG_OBJECT (self, "Starting main loop");
857   g_main_loop_run (self->loop);
858   GST_DEBUG_OBJECT (self, "Stopped main loop");
859
860   gst_mf_source_reader_stop (object);
861   gst_mf_source_reader_close (self);
862
863   g_main_context_pop_thread_default (self->context);
864
865   CoUninitialize ();
866
867   return nullptr;
868 }
869
870 static gboolean
871 gst_mf_source_enum_device_activate (GstMFSourceReader * self,
872     GstMFSourceType source_type, GList ** device_sources)
873 {
874   HRESULT hr;
875   GList *ret = nullptr;
876   ComPtr < IMFAttributes > attr;
877   IMFActivate **devices = nullptr;
878   UINT32 i, count = 0;
879
880   hr = MFCreateAttributes (&attr, 1);
881   if (!gst_mf_result (hr)) {
882     return FALSE;
883   }
884
885   switch (source_type) {
886     case GST_MF_SOURCE_TYPE_VIDEO:
887       hr = attr->SetGUID (MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
888           MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
889       break;
890     default:
891       GST_ERROR_OBJECT (self, "Unknown source type %d", source_type);
892       return FALSE;
893   }
894
895   if (!gst_mf_result (hr))
896     return FALSE;
897
898   hr = MFEnumDeviceSources (attr.Get (), &devices, &count);
899   if (!gst_mf_result (hr))
900     return FALSE;
901
902   for (i = 0; i < count; i++) {
903     GstMFDeviceActivate *entry;
904     LPWSTR name;
905     UINT32 name_len;
906     IMFActivate *activate = devices[i];
907
908     switch (source_type) {
909       case GST_MF_SOURCE_TYPE_VIDEO:
910         hr = activate->GetAllocatedString
911             (MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &name,
912             &name_len);
913         break;
914       default:
915         g_assert_not_reached ();
916         goto done;
917     }
918
919     entry = g_new0 (GstMFDeviceActivate, 1);
920     entry->index = i;
921     entry->handle = activate;
922
923     if (gst_mf_result (hr)) {
924       entry->path = g_utf16_to_utf8 ((const gunichar2 *) name,
925           -1, nullptr, nullptr, nullptr);
926       CoTaskMemFree (name);
927     }
928
929     hr = activate->GetAllocatedString (MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
930         &name, &name_len);
931     if (gst_mf_result (hr)) {
932       entry->name = g_utf16_to_utf8 ((const gunichar2 *) name,
933           -1, nullptr, nullptr, nullptr);
934       CoTaskMemFree (name);
935     }
936
937     ret = g_list_prepend (ret, entry);
938   }
939
940 done:
941   CoTaskMemFree (devices);
942
943   if (!ret)
944     return FALSE;
945
946   *device_sources = g_list_reverse (ret);
947
948   return TRUE;
949 }
950
951 static void
952 gst_mf_device_activate_free (GstMFDeviceActivate * activate)
953 {
954   g_return_if_fail (activate != nullptr);
955
956   if (activate->handle)
957     activate->handle->Release ();
958
959   g_free (activate->name);
960   g_free (activate->path);
961   g_free (activate);
962 }
963
964 static void
965 gst_mf_source_reader_sample_clear (GstMFSourceReaderSample * reader_sample)
966 {
967   if (!reader_sample)
968     return;
969
970   if (reader_sample->sample)
971     reader_sample->sample->Release ();
972
973   reader_sample->sample = nullptr;
974   reader_sample->clock_time = GST_CLOCK_TIME_NONE;
975 }
976
977 GstMFSourceObject *
978 gst_mf_source_reader_new (GstMFSourceType type, gint device_index,
979     const gchar * device_name, const gchar * device_path)
980 {
981   GstMFSourceObject *self;
982
983   /* TODO: add more type */
984   g_return_val_if_fail (type == GST_MF_SOURCE_TYPE_VIDEO, nullptr);
985
986   self = (GstMFSourceObject *) g_object_new (GST_TYPE_MF_SOURCE_READER,
987       "source-type", type, "device-index", device_index, "device-name",
988       device_name, "device-path", device_path, nullptr);
989
990   gst_object_ref_sink (self);
991
992   if (!self->opened) {
993     GST_WARNING_OBJECT (self, "Couldn't open device");
994     gst_object_unref (self);
995     return nullptr;
996   }
997
998   return self;
999 }