2 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
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.
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.
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.
25 #include <gst/base/base.h>
26 #include <gst/video/video.h>
27 #include "gstmfsourcereader.h"
35 using namespace Microsoft::WRL;
38 GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug);
39 #define GST_CAT_DEFAULT gst_mf_source_object_debug
41 typedef struct _GstMFStreamMediaType
43 IMFMediaType *media_type;
45 /* the stream index of media type */
48 /* the media index in the stream index */
49 guint media_type_index;
52 } GstMFStreamMediaType;
60 } GstMFDeviceActivate;
62 struct _GstMFSourceReader
64 GstMFSourceObject parent;
69 GMainContext *context;
72 /* protected by lock */
75 IMFActivate *activate;
76 IMFMediaSource *source;
77 IMFSourceReader *reader;
79 GstCaps *supported_caps;
81 GstMFStreamMediaType *cur_type;
84 gboolean top_down_image;
89 typedef struct _GstMFSourceReaderSample
92 GstClockTime clock_time;
93 } GstMFSourceReaderSample;
95 static void gst_mf_source_reader_constructed (GObject * object);
96 static void gst_mf_source_reader_finalize (GObject * object);
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,
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,
110 gst_mf_source_reader_sample_clear (GstMFSourceReaderSample * reader_sample);
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);
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);
125 gst_mf_source_reader_class_init (GstMFSourceReaderClass * klass)
127 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
128 GstMFSourceObjectClass *source_class = GST_MF_SOURCE_OBJECT_CLASS (klass);
130 gobject_class->constructed = gst_mf_source_reader_constructed;
131 gobject_class->finalize = gst_mf_source_reader_finalize;
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);
145 gst_mf_source_reader_init (GstMFSourceReader * self)
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);
156 gst_mf_source_reader_constructed (GObject * object)
158 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
160 self->context = g_main_context_new ();
161 self->loop = g_main_loop_new (self->context, FALSE);
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);
173 gst_mf_enum_media_type_from_source_reader (IMFSourceReader * source_reader,
174 GList ** media_types)
178 GList *list = nullptr;
179 std::vector < std::string > unhandled_caps;
181 g_return_val_if_fail (source_reader != nullptr, FALSE);
182 g_return_val_if_fail (media_types != nullptr, FALSE);
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.
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
191 i = MF_SOURCE_READER_FIRST_VIDEO_STREAM;
193 ComPtr < IMFMediaType > media_type;
195 hr = source_reader->GetNativeMediaType (i, j, &media_type);
197 if (SUCCEEDED (hr)) {
198 GstMFStreamMediaType *mtype;
199 GstCaps *caps = nullptr;
203 caps = gst_mf_media_type_to_caps (media_type.Get ());
209 s = gst_caps_get_structure (caps, 0);
210 name = gst_structure_get_name (s);
211 if (name != "video/x-raw" && name != "image/jpeg") {
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);
218 gst_caps_unref (caps);
222 mtype = g_new0 (GstMFStreamMediaType, 1);
224 mtype->media_type = media_type.Detach ();
225 mtype->stream_index = i;
226 mtype->media_type_index = j;
229 GST_DEBUG ("StreamIndex %d, MediaTypeIndex %d, %" GST_PTR_FORMAT,
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 */
236 } else if (hr == MF_E_INVALIDSTREAMNUMBER) {
237 /* no more streams and media types */
240 /* undefined return */
250 list = g_list_reverse (list);
257 gst_mf_stream_media_type_free (GstMFStreamMediaType * media_type)
259 g_return_if_fail (media_type != nullptr);
261 if (media_type->media_type)
262 media_type->media_type->Release ();
264 if (media_type->caps)
265 gst_caps_unref (media_type->caps);
271 compare_caps_func (gconstpointer a, gconstpointer b)
273 GstMFStreamMediaType *m1, *m2;
275 m1 = (GstMFStreamMediaType *) a;
276 m2 = (GstMFStreamMediaType *) b;
278 return gst_mf_source_object_caps_compare (m1->caps, m2->caps);
282 gst_mf_source_reader_open (GstMFSourceReader * self, IMFActivate * activate)
286 ComPtr < IMFSourceReader > reader;
287 ComPtr < IMFMediaSource > source;
288 ComPtr < IMFAttributes > attr;
290 hr = activate->ActivateObject (IID_PPV_ARGS (&source));
291 if (!gst_mf_result (hr))
294 hr = MFCreateAttributes (&attr, 2);
295 if (!gst_mf_result (hr))
298 hr = attr->SetUINT32 (MF_READWRITE_DISABLE_CONVERTERS, TRUE);
299 if (!gst_mf_result (hr))
302 hr = MFCreateSourceReaderFromMediaSource (source.Get (),
303 attr.Get (), &reader);
304 if (!gst_mf_result (hr))
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");
314 self->activate = activate;
316 self->source = source.Detach ();
317 self->reader = reader.Detach ();
319 self->media_types = g_list_sort (self->media_types,
320 (GCompareFunc) compare_caps_func);
322 self->supported_caps = gst_caps_new_empty ();
324 for (iter = self->media_types; iter; iter = g_list_next (iter)) {
325 GstMFStreamMediaType *mtype = (GstMFStreamMediaType *) iter->data;
327 gst_caps_append (self->supported_caps, gst_caps_copy (mtype->caps));
330 GST_DEBUG_OBJECT (self, "Available output caps %" GST_PTR_FORMAT,
331 self->supported_caps);
337 gst_mf_source_reader_close (GstMFSourceReader * self)
339 gst_clear_caps (&self->supported_caps);
341 if (self->activate) {
342 self->activate->ShutdownObject ();
343 self->activate->Release ();
344 self->activate = nullptr;
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;
354 self->reader->Release ();
355 self->reader = nullptr;
359 self->source->Shutdown ();
360 self->source->Release ();
361 self->source = nullptr;
368 gst_mf_source_reader_finalize (GObject * object)
370 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
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);
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);
382 G_OBJECT_CLASS (parent_class)->finalize (object);
386 gst_mf_source_reader_start (GstMFSourceObject * object)
388 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
390 GstMFStreamMediaType *type;
392 if (!self->cur_type) {
393 GST_ERROR_OBJECT (self, "MediaType wasn't specified");
397 type = self->cur_type;
398 self->top_down_image = TRUE;
400 if (GST_VIDEO_INFO_FORMAT (&self->info) != GST_VIDEO_FORMAT_ENCODED) {
402 INT32 actual_stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0);
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");
417 GST_DEBUG_OBJECT (self,
418 "Detected bottom-up image, stride %d", actual_stride);
420 self->top_down_image = FALSE;
423 /* If MF_MT_DEFAULT_STRIDE attribute is not specified, we can use our
425 type->media_type->SetUINT32 (MF_MT_DEFAULT_STRIDE,
426 (UINT32) actual_stride);
428 gst_mf_update_video_info_with_stride (&self->info,
429 std::abs (actual_stride));
432 hr = self->reader->SetStreamSelection (type->stream_index, TRUE);
433 if (!gst_mf_result (hr))
436 hr = self->reader->SetCurrentMediaType (type->stream_index,
437 nullptr, type->media_type);
438 if (!gst_mf_result (hr))
444 static GstMFSourceReaderSample *
445 gst_mf_source_reader_sample_new (IMFSample * sample, GstClockTime timestamp)
447 GstMFSourceReaderSample *reader_sample = g_new0 (GstMFSourceReaderSample, 1);
449 reader_sample->sample = sample;
450 reader_sample->clock_time = timestamp;
452 return reader_sample;
456 gst_mf_source_reader_stop (GstMFSourceObject * object)
458 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
460 gst_queue_array_clear (self->queue);
466 gst_mf_source_reader_read_sample (GstMFSourceReader * self)
469 DWORD stream_flags = 0;
470 GstMFStreamMediaType *type = self->cur_type;
471 IMFSample *sample = nullptr;
472 GstMFSourceReaderSample reader_sample;
474 hr = self->reader->ReadSample (type->stream_index, 0, nullptr, &stream_flags,
477 if (!gst_mf_result (hr)) {
478 GST_ERROR_OBJECT (self, "Failed to read sample");
479 return GST_FLOW_ERROR;
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",
485 return GST_FLOW_ERROR;
489 GST_WARNING_OBJECT (self, "Empty sample");
493 reader_sample.sample = sample;
494 reader_sample.clock_time =
495 gst_mf_source_object_get_running_time (GST_MF_SOURCE_OBJECT (self));
497 gst_queue_array_push_tail_struct (self->queue, &reader_sample);
503 gst_mf_source_reader_get_media_buffer (GstMFSourceReader * self,
504 IMFMediaBuffer ** buffer, GstClockTime * timestamp, GstClockTime * duration)
506 GstFlowReturn ret = GST_FLOW_OK;
507 IMFSample *sample = nullptr;
510 LONGLONG mf_timestamp;
511 GstMFSourceReaderSample *reader_sample = nullptr;
514 *timestamp = GST_CLOCK_TIME_NONE;
515 *duration = GST_CLOCK_TIME_NONE;
517 while (gst_queue_array_is_empty (self->queue)) {
518 ret = gst_mf_source_reader_read_sample (self);
519 if (ret != GST_FLOW_OK)
522 g_mutex_lock (&self->lock);
523 if (self->flushing) {
524 g_mutex_unlock (&self->lock);
525 return GST_FLOW_FLUSHING;
527 g_mutex_unlock (&self->lock);
531 (GstMFSourceReaderSample *) gst_queue_array_pop_head_struct (self->queue);
532 sample = reader_sample->sample;
535 hr = sample->GetBufferCount (&count);
536 if (!gst_mf_result (hr) || count == 0) {
537 GST_WARNING_OBJECT (self, "Empty IMFSample, read again");
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");
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;
553 /* Media Foundation uses 100 nano seconds unit */
554 *duration = mf_timestamp * 100;
557 *timestamp = reader_sample->clock_time;
560 gst_mf_source_reader_sample_clear (reader_sample);
566 gst_mf_source_reader_fill (GstMFSourceObject * object, GstBuffer * buffer)
568 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
569 GstFlowReturn ret = GST_FLOW_OK;
570 ComPtr < IMFMediaBuffer > media_buffer;
575 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
576 GstClockTime duration = GST_CLOCK_TIME_NONE;
579 ret = gst_mf_source_reader_get_media_buffer (self,
580 media_buffer.ReleaseAndGetAddressOf (), ×tamp, &duration);
581 } while (ret == GST_FLOW_OK && !media_buffer);
583 if (ret != GST_FLOW_OK)
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;
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;
598 if (!self->top_down_image) {
600 gint src_stride, dst_stride;
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);
608 src_stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0);
609 dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
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);
615 for (j = 0; j < height; j++) {
616 memcpy (dst, src, width);
621 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->info); i++) {
623 gint src_stride, dst_stride;
626 src = data + GST_VIDEO_INFO_PLANE_OFFSET (&self->info, i);
627 dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, i);
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);
634 for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (&self->info, i); j++) {
635 memcpy (dst, src, width);
642 gst_video_frame_unmap (&frame);
643 media_buffer->Unlock ();
645 GST_BUFFER_PTS (buffer) = timestamp;
646 GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
647 GST_BUFFER_DURATION (buffer) = duration;
653 gst_mf_source_reader_create (GstMFSourceObject * object, GstBuffer ** buffer)
655 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
656 GstFlowReturn ret = GST_FLOW_OK;
657 ComPtr < IMFMediaBuffer > media_buffer;
663 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
664 GstClockTime duration = GST_CLOCK_TIME_NONE;
667 ret = gst_mf_source_reader_get_media_buffer (self,
668 media_buffer.ReleaseAndGetAddressOf (), ×tamp, &duration);
669 } while (ret == GST_FLOW_OK && !media_buffer);
671 if (ret != GST_FLOW_OK)
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;
680 buf = gst_buffer_new_and_alloc (len);
682 GST_ERROR_OBJECT (self, "Cannot allocate buffer");
683 media_buffer->Unlock ();
684 return GST_FLOW_ERROR;
687 gst_buffer_map (buf, &info, GST_MAP_WRITE);
688 memcpy (info.data, data, len);
689 gst_buffer_unmap (buf, &info);
691 media_buffer->Unlock ();
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;
704 gst_mf_source_reader_unlock (GstMFSourceObject * object)
706 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
708 g_mutex_lock (&self->lock);
709 self->flushing = TRUE;
710 g_mutex_unlock (&self->lock);
716 gst_mf_source_reader_unlock_stop (GstMFSourceObject * object)
718 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
720 g_mutex_lock (&self->lock);
721 self->flushing = FALSE;
722 g_mutex_unlock (&self->lock);
728 gst_mf_source_reader_get_caps (GstMFSourceObject * object)
730 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
732 if (self->supported_caps)
733 return gst_caps_ref (self->supported_caps);
739 gst_mf_source_reader_set_caps (GstMFSourceObject * object, GstCaps * caps)
741 GstMFSourceReader *self = GST_MF_SOURCE_READER (object);
743 GstMFStreamMediaType *best_type = nullptr;
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)) {
754 GST_ERROR_OBJECT (self,
755 "Could not determine target media type with given caps %"
756 GST_PTR_FORMAT, caps);
761 self->cur_type = best_type;
762 gst_video_info_from_caps (&self->info, best_type->caps);
768 gst_mf_source_reader_main_loop_running_cb (GstMFSourceReader * self)
770 GST_INFO_OBJECT (self, "Main loop running now");
772 g_mutex_lock (&self->lock);
773 g_cond_signal (&self->cond);
774 g_mutex_unlock (&self->lock);
776 return G_SOURCE_REMOVE;
780 gst_mf_source_reader_thread_func (GstMFSourceReader * self)
782 GstMFSourceObject *object = GST_MF_SOURCE_OBJECT (self);
784 GList *activate_list = nullptr;
785 GstMFDeviceActivate *target = nullptr;
788 CoInitializeEx (nullptr, COINIT_MULTITHREADED);
790 g_main_context_push_thread_default (self->context);
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);
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");
803 #ifndef GST_DISABLE_GST_DEBUG
804 for (iter = activate_list; iter; iter = g_list_next (iter)) {
805 GstMFDeviceActivate *activate = (GstMFDeviceActivate *) iter->data;
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));
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));
818 for (iter = activate_list; iter; iter = g_list_next (iter)) {
819 GstMFDeviceActivate *activate = (GstMFDeviceActivate *) iter->data;
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;
829 /* pick the first entry */
840 object->opened = gst_mf_source_reader_open (self, target->handle);
842 g_free (object->device_path);
843 object->device_path = g_strdup (target->path);
845 g_free (object->device_name);
846 object->device_name = g_strdup (target->name);
848 object->device_index = target->index;
852 g_list_free_full (activate_list,
853 (GDestroyNotify) gst_mf_device_activate_free);
856 GST_DEBUG_OBJECT (self, "Starting main loop");
857 g_main_loop_run (self->loop);
858 GST_DEBUG_OBJECT (self, "Stopped main loop");
860 gst_mf_source_reader_stop (object);
861 gst_mf_source_reader_close (self);
863 g_main_context_pop_thread_default (self->context);
871 gst_mf_source_enum_device_activate (GstMFSourceReader * self,
872 GstMFSourceType source_type, GList ** device_sources)
875 GList *ret = nullptr;
876 ComPtr < IMFAttributes > attr;
877 IMFActivate **devices = nullptr;
880 hr = MFCreateAttributes (&attr, 1);
881 if (!gst_mf_result (hr)) {
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);
891 GST_ERROR_OBJECT (self, "Unknown source type %d", source_type);
895 if (!gst_mf_result (hr))
898 hr = MFEnumDeviceSources (attr.Get (), &devices, &count);
899 if (!gst_mf_result (hr))
902 for (i = 0; i < count; i++) {
903 GstMFDeviceActivate *entry;
906 IMFActivate *activate = devices[i];
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,
915 g_assert_not_reached ();
919 entry = g_new0 (GstMFDeviceActivate, 1);
921 entry->handle = activate;
923 if (gst_mf_result (hr)) {
924 entry->path = g_utf16_to_utf8 ((const gunichar2 *) name,
925 -1, nullptr, nullptr, nullptr);
926 CoTaskMemFree (name);
929 hr = activate->GetAllocatedString (MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
931 if (gst_mf_result (hr)) {
932 entry->name = g_utf16_to_utf8 ((const gunichar2 *) name,
933 -1, nullptr, nullptr, nullptr);
934 CoTaskMemFree (name);
937 ret = g_list_prepend (ret, entry);
941 CoTaskMemFree (devices);
946 *device_sources = g_list_reverse (ret);
952 gst_mf_device_activate_free (GstMFDeviceActivate * activate)
954 g_return_if_fail (activate != nullptr);
956 if (activate->handle)
957 activate->handle->Release ();
959 g_free (activate->name);
960 g_free (activate->path);
965 gst_mf_source_reader_sample_clear (GstMFSourceReaderSample * reader_sample)
970 if (reader_sample->sample)
971 reader_sample->sample->Release ();
973 reader_sample->sample = nullptr;
974 reader_sample->clock_time = GST_CLOCK_TIME_NONE;
978 gst_mf_source_reader_new (GstMFSourceType type, gint device_index,
979 const gchar * device_name, const gchar * device_path)
981 GstMFSourceObject *self;
983 /* TODO: add more type */
984 g_return_val_if_fail (type == GST_MF_SOURCE_TYPE_VIDEO, nullptr);
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);
990 gst_object_ref_sink (self);
993 GST_WARNING_OBJECT (self, "Couldn't open device");
994 gst_object_unref (self);