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