diff options
author | Joshua M. Doe <oss@nvl.army.mil> | 2018-10-16 11:45:15 -0400 |
---|---|---|
committer | Nirbheek Chauhan <nirbheek.chauhan@gmail.com> | 2018-11-09 09:52:40 +0000 |
commit | 912ff02a21acd665b05700aff356519fb6b39f39 (patch) | |
tree | e83e27b5ae48a6479909131e8afbd2fc311f093c | |
parent | e70af38d4ed837c34fb5830f6d7799b1f3f192e9 (diff) | |
download | gstreamer-plugins-bad-912ff02a21acd665b05700aff356519fb6b39f39.tar.gz |
dshowsrcwrapper: refactor device selection, filter creation, and caps retrieval
This allows a future GstDeviceProvider to more easily query devices and caps.
-rw-r--r-- | sys/dshowsrcwrapper/gstdshow.cpp | 215 | ||||
-rw-r--r-- | sys/dshowsrcwrapper/gstdshow.h | 22 | ||||
-rw-r--r-- | sys/dshowsrcwrapper/gstdshowvideosrc.cpp | 201 | ||||
-rw-r--r-- | sys/dshowsrcwrapper/gstdshowvideosrc.h | 5 |
4 files changed, 331 insertions, 112 deletions
diff --git a/sys/dshowsrcwrapper/gstdshow.cpp b/sys/dshowsrcwrapper/gstdshow.cpp index a5aded785..24a241ada 100644 --- a/sys/dshowsrcwrapper/gstdshow.cpp +++ b/sys/dshowsrcwrapper/gstdshow.cpp @@ -23,10 +23,17 @@ #include "gstdshow.h" #include "gstdshowfakesink.h" +#include "gstdshowvideosrc.h" GST_DEBUG_CATEGORY_EXTERN (dshowsrcwrapper_debug); #define GST_CAT_DEFAULT dshowsrcwrapper_debug +gchar * +wchar_to_gchar (WCHAR * w) +{ + return g_utf16_to_utf8 ((const gunichar2 *) w, wcslen (w), NULL, NULL, NULL); +} + const GUID MEDIASUBTYPE_I420 = { 0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71} @@ -238,9 +245,7 @@ gst_dshow_find_filter (CLSID input_majortype, CLSID input_subtype, hres = property_bag->Read (L"FriendlyName", &varFriendlyName, NULL); if (hres == S_OK && varFriendlyName.bstrVal) { - friendly_name = - g_utf16_to_utf8 ((const gunichar2 *) varFriendlyName.bstrVal, - wcslen (varFriendlyName.bstrVal), NULL, NULL, NULL); + friendly_name = wchar_to_gchar (varFriendlyName.bstrVal); if (friendly_name) _strupr (friendly_name); SysFreeString (varFriendlyName.bstrVal); @@ -288,6 +293,196 @@ clean: return ret; } +void +gst_dshow_device_entry_free (DshowDeviceEntry * entry) +{ + if (entry) { + g_free (entry->device); + entry->device = NULL; + g_free (entry->device_name); + entry->device_name = NULL; + if (entry->caps) { + gst_caps_unref (entry->caps); + entry->caps = NULL; + } + if (entry->moniker) { + entry->moniker->Release (); + entry->moniker = NULL; + } + } +} + +void +gst_dshow_device_list_free (GList * devices) +{ + GList *cur; + + for (cur = devices; cur != NULL; cur = cur->next) + gst_dshow_device_entry_free ((DshowDeviceEntry *) cur->data); + + g_list_free (devices); +} + +GList * +gst_dshow_enumerate_devices (const GUID * device_category, gboolean getcaps) +{ + GList *result = NULL; + ICreateDevEnum *devices_enum = NULL; + IEnumMoniker *enum_moniker = NULL; + IMoniker *moniker = NULL; + HRESULT hres = S_FALSE; + ULONG fetched; + gint devidx = -1; + + hres = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, + IID_ICreateDevEnum, (void **) &devices_enum); + if (hres != S_OK) { + GST_ERROR ("Failed to create System Device Enumerator"); + goto clean; + } + + hres = devices_enum->CreateClassEnumerator (*device_category, + &enum_moniker, 0); + if (hres != S_OK || !enum_moniker) { + GST_ERROR ("Failed to create audio/video class device enumerator"); + goto clean; + } + + enum_moniker->Reset (); + + while (enum_moniker->Next (1, &moniker, &fetched) == S_OK) { + IPropertyBag *property_bag = NULL; + hres = moniker->BindToStorage (NULL, NULL, IID_IPropertyBag, + (void **) &property_bag); + if (SUCCEEDED (hres) && property_bag) { + VARIANT varFriendlyName; + VariantInit (&varFriendlyName); + + hres = property_bag->Read (L"FriendlyName", &varFriendlyName, NULL); + if (hres == S_OK && varFriendlyName.bstrVal) { + gchar *friendly_name = wchar_to_gchar (varFriendlyName.bstrVal); + + devidx++; + GST_DEBUG ("Found device idx=%d: device-name='%s'", + devidx, friendly_name); + + WCHAR *wszDisplayName = NULL; + hres = moniker->GetDisplayName (NULL, NULL, &wszDisplayName); + if (hres == S_OK && wszDisplayName) { + DshowDeviceEntry *entry = g_new0 (DshowDeviceEntry, 1); + gchar *device_path = NULL; + GstCaps *caps = NULL; + + device_path = wchar_to_gchar (wszDisplayName); + CoTaskMemFree (wszDisplayName); + + /* getting caps can be slow, so make it optional when enumerating */ + if (getcaps) { + IBindCtx *lpbc = NULL; + hres = CreateBindCtx (0, &lpbc); + if (SUCCEEDED (hres)) { + IBaseFilter *video_cap_filter = NULL; + hres = moniker->BindToObject (lpbc, NULL, IID_IBaseFilter, + (LPVOID *) & video_cap_filter); + if (video_cap_filter) { + caps = gst_dshowvideosrc_getcaps_from_capture_filter (video_cap_filter, NULL); + video_cap_filter->Release (); + } + lpbc->Release (); + } + } + + entry->device = device_path; + entry->device_name = friendly_name; + entry->device_index = devidx; + entry->caps = caps; + entry->moniker = moniker; + moniker = NULL; + result = g_list_append (result, entry); + } else { + g_free (friendly_name); + } + SysFreeString (varFriendlyName.bstrVal); + } + property_bag->Release (); + } + if (moniker) { + moniker->Release (); + } + } + +clean: + if (enum_moniker) { + enum_moniker->Release (); + } + + if (devices_enum) { + devices_enum->Release (); + } + + return result; +} + +DshowDeviceEntry * +gst_dshow_select_device (const GUID * device_category, + const gchar * device, const gchar * device_name, const gint device_index) +{ + GList *devices = NULL; + GList *item = NULL; + DshowDeviceEntry *selected = NULL; + + GST_DEBUG ("Trying to select device-index=%d, device-name='%s', device='%s'", + device_index, device_name, device); + + devices = gst_dshow_enumerate_devices (&CLSID_VideoInputDeviceCategory, FALSE); + + for (item = devices; item != NULL; item = item->next) { + DshowDeviceEntry *entry = (DshowDeviceEntry *) item->data; + + /* device will be used first, then device-name, then device-index */ + if (device && g_strcmp0 (device, entry->device) == 0) { + selected = entry; + break; + } else if (device_name && g_strcmp0 (device_name, entry->device_name) == 0) { + selected = entry; + break; + } else if (device_index == entry->device_index) { + selected = entry; + break; + } + } + + if (selected) { + devices = g_list_remove (devices, selected); + GST_DEBUG ("Selected device-index=%d, device-name='%s', device='%s'", + selected->device_index, selected->device_name, selected->device); + } else { + GST_DEBUG ("No matching device found"); + } + + gst_dshow_device_list_free (devices); + + return selected; +} + +IBaseFilter * +gst_dshow_create_capture_filter (IMoniker *moniker) +{ + HRESULT hres = S_OK; + IBindCtx *lpbc = NULL; + IBaseFilter *video_cap_filter = NULL; + + g_assert (moniker != NULL); + + hres = CreateBindCtx (0, &lpbc); + if (SUCCEEDED (hres)) { + hres = moniker->BindToObject (lpbc, NULL, IID_IBaseFilter, + (LPVOID *) & video_cap_filter); + lpbc->Release (); + } + + return video_cap_filter; +} gchar * gst_dshow_getdevice_from_devicename (const GUID * device_category, @@ -305,14 +500,14 @@ gst_dshow_getdevice_from_devicename (const GUID * device_category, hres = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **) &devices_enum); if (hres != S_OK) { - /*error */ + GST_ERROR ("Failed to create System Device Enumerator"); goto clean; } hres = devices_enum->CreateClassEnumerator (*device_category, &enum_moniker, 0); if (hres != S_OK || !enum_moniker) { - /*error */ + GST_ERROR ("Failed to create audio/video class device enumerator"); goto clean; } @@ -330,9 +525,7 @@ gst_dshow_getdevice_from_devicename (const GUID * device_category, hres = property_bag->Read (L"FriendlyName", &varFriendlyName, NULL); if (hres == S_OK && varFriendlyName.bstrVal) { - gchar *friendly_name = - g_utf16_to_utf8 ((const gunichar2 *) varFriendlyName.bstrVal, - wcslen (varFriendlyName.bstrVal), NULL, NULL, NULL); + gchar *friendly_name = wchar_to_gchar (varFriendlyName.bstrVal); devidx++; GST_DEBUG ("Found device idx=%d: device-name='%s'", @@ -343,13 +536,13 @@ gst_dshow_getdevice_from_devicename (const GUID * device_category, *device_name = g_strdup (friendly_name); } - if ((*device_name && **device_name) && _stricmp (*device_name, friendly_name) == 0) { + if ((*device_name && **device_name) + && _stricmp (*device_name, friendly_name) == 0) { WCHAR *wszDisplayName = NULL; hres = moniker->GetDisplayName (NULL, NULL, &wszDisplayName); if (hres == S_OK && wszDisplayName) { *device_index = devidx; - ret = g_utf16_to_utf8 ((const gunichar2 *) wszDisplayName, - wcslen (wszDisplayName), NULL, NULL, NULL); + ret = wchar_to_gchar (wszDisplayName); CoTaskMemFree (wszDisplayName); } bfound = TRUE; diff --git a/sys/dshowsrcwrapper/gstdshow.h b/sys/dshowsrcwrapper/gstdshow.h index aff5d0af1..2814e372e 100644 --- a/sys/dshowsrcwrapper/gstdshow.h +++ b/sys/dshowsrcwrapper/gstdshow.h @@ -32,6 +32,17 @@ #include <gst/gst.h> #include <gst/video/video.h> +typedef struct _DshowDeviceEntry DshowDeviceEntry; + +struct _DshowDeviceEntry +{ + gchar *device; + gchar *device_name; + gint device_index; + GstCaps *caps; + IMoniker *moniker; +}; + typedef struct _GstCapturePinMediaType { AM_MEDIA_TYPE *mediatype; @@ -102,4 +113,15 @@ GstCaps *gst_dshow_new_video_caps (GstVideoFormat video_format, /* configure the latency of the capture source */ bool gst_dshow_configure_latency (IPin *pCapturePin, guint bufSizeMS); +/* enumerate devices of a given category (i.e., audio or video) */ +void gst_dshow_device_entry_free (DshowDeviceEntry *entry); +void gst_dshow_device_list_free (GList * devices); +GList * gst_dshow_enumerate_devices (const GUID * device_category, gboolean getcaps); + +DshowDeviceEntry * gst_dshow_select_device (const GUID * device_category, + const gchar *device, const gchar *device_name, const gint device_index); + +/* create capture filter from moniker */ +IBaseFilter *gst_dshow_create_capture_filter (IMoniker *moniker); + #endif /* _GSTDSHOW_ */ diff --git a/sys/dshowsrcwrapper/gstdshowvideosrc.cpp b/sys/dshowsrcwrapper/gstdshowvideosrc.cpp index 691df598c..4127d537a 100644 --- a/sys/dshowsrcwrapper/gstdshowvideosrc.cpp +++ b/sys/dshowsrcwrapper/gstdshowvideosrc.cpp @@ -89,13 +89,12 @@ static GstCaps *gst_dshowvideosrc_src_fixate (GstBaseSrc * bsrc, GstCaps * caps) static GstFlowReturn gst_dshowvideosrc_create (GstPushSrc * psrc, GstBuffer ** buf); -static gboolean gst_dshowvideosrc_create_capture_filter(GstDshowVideoSrc * src); - /*utils*/ -static GstCaps *gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * - src, IPin * pin); -static GstCaps *gst_dshowvideosrc_getcaps_from_enum_mediatypes (GstDshowVideoSrc * - src, IPin * pin); +GstCaps *gst_dshowvideosrc_getcaps_from_streamcaps (IPin * pin, + GList ** pins_mediatypes); +GstCaps *gst_dshowvideosrc_getcaps_from_enum_mediatypes (IPin * pin, + GList ** pins_mediatypes); + static gboolean gst_dshowvideosrc_push_buffer (guint8 * buffer, guint size, gpointer src_object, GstClockTime duration); @@ -362,104 +361,62 @@ gst_dshowvideosrc_get_caps (GstBaseSrc * basesrc, GstCaps * filter) return NULL; } -static gboolean -gst_dshowvideosrc_create_capture_filter(GstDshowVideoSrc * src) +GstCaps * +gst_dshowvideosrc_getcaps_from_capture_filter (IBaseFilter * filter, + GList ** pins_mediatypes) { - HRESULT hres = S_OK; - IBindCtx *lpbc = NULL; - IMoniker *videom; - DWORD dwEaten; - gunichar2 *unidevice = NULL; - - /* device will be used first, then device-name, then device-index */ - if (!src->device || src->device[0] == '\0') { - GST_DEBUG_OBJECT (src, "No device set, will enumerate to match device-name or device-index"); - - g_free (src->device); - src->device = - gst_dshow_getdevice_from_devicename (&CLSID_VideoInputDeviceCategory, - &src->device_name, &src->device_index); - if (!src->device) { - GST_ERROR ("No video device found."); - return FALSE; - } - } + IPin *capture_pin = NULL; + IEnumPins *enumpins = NULL; + HRESULT hres; + GstCaps *caps; - GST_DEBUG_OBJECT (src, "Opening device-index=%d, device-name='%s', device='%s'", - src->device_index, src->device_name, src->device); + g_assert (filter); - unidevice = - g_utf8_to_utf16 (src->device, strlen (src->device), NULL, NULL, NULL); + caps = gst_caps_new_empty (); - if (!src->video_cap_filter) { - hres = CreateBindCtx (0, &lpbc); - if (SUCCEEDED (hres)) { + /* get the capture pins supported types */ + hres = filter->EnumPins (&enumpins); + if (SUCCEEDED (hres)) { + while (enumpins->Next (1, &capture_pin, NULL) == S_OK) { + IKsPropertySet *pKs = NULL; hres = - MkParseDisplayName (lpbc, (LPCOLESTR) unidevice, &dwEaten, &videom); - if (SUCCEEDED (hres)) { - hres = videom->BindToObject (lpbc, NULL, IID_IBaseFilter, - (LPVOID *) & src->video_cap_filter); - videom->Release (); - } - lpbc->Release (); - } - } - - g_free (unidevice); + capture_pin->QueryInterface (IID_IKsPropertySet, (LPVOID *) & pKs); + if (SUCCEEDED (hres) && pKs) { + DWORD cbReturned; + GUID pin_category; + RPC_STATUS rpcstatus; - if (!src->caps) { - src->caps = gst_caps_new_empty (); - } - - if (src->video_cap_filter && gst_caps_is_empty (src->caps)) { - /* get the capture pins supported types */ - IPin *capture_pin = NULL; - IEnumPins *enumpins = NULL; - HRESULT hres; - - hres = src->video_cap_filter->EnumPins (&enumpins); - if (SUCCEEDED (hres)) { - while (enumpins->Next (1, &capture_pin, NULL) == S_OK) { - IKsPropertySet *pKs = NULL; hres = - capture_pin->QueryInterface (IID_IKsPropertySet, (LPVOID *) & pKs); - if (SUCCEEDED (hres) && pKs) { - DWORD cbReturned; - GUID pin_category; - RPC_STATUS rpcstatus; - - hres = - pKs->Get (AMPROPSETID_Pin, - AMPROPERTY_PIN_CATEGORY, NULL, 0, &pin_category, sizeof (GUID), - &cbReturned); - - /* we only want capture pins */ - if (UuidCompare (&pin_category, (UUID *) & PIN_CATEGORY_CAPTURE, - &rpcstatus) == 0) { - { - GstCaps *caps = - gst_dshowvideosrc_getcaps_from_streamcaps (src, capture_pin); - if (caps) { - GST_DEBUG_OBJECT (src, "Caps supported by device: %" GST_PTR_FORMAT, caps); - gst_caps_append (src->caps, caps); - } else { - caps = gst_dshowvideosrc_getcaps_from_enum_mediatypes (src, capture_pin); - if (caps) { - GST_DEBUG_OBJECT (src, "Caps supported by device: %" GST_PTR_FORMAT, caps); - gst_caps_append (src->caps, caps); - } - } + pKs->Get (AMPROPSETID_Pin, + AMPROPERTY_PIN_CATEGORY, NULL, 0, &pin_category, sizeof (GUID), + &cbReturned); + + /* we only want capture pins */ + if (UuidCompare (&pin_category, (UUID *) & PIN_CATEGORY_CAPTURE, + &rpcstatus) == 0) { + GstCaps *caps2; + caps2 = gst_dshowvideosrc_getcaps_from_streamcaps (capture_pin, + pins_mediatypes); + if (caps2) { + gst_caps_append (caps, caps2); + } else { + caps2 = gst_dshowvideosrc_getcaps_from_enum_mediatypes ( + capture_pin, pins_mediatypes); + if (caps2) { + gst_caps_append (caps, caps2); } } - pKs->Release (); } - capture_pin->Release (); + pKs->Release (); } - enumpins->Release (); + capture_pin->Release (); } + enumpins->Release (); } - return TRUE; + GST_DEBUG ("Device supports these caps: %" GST_PTR_FORMAT, caps); + + return caps; } static GstStateChangeReturn @@ -509,8 +466,40 @@ gst_dshowvideosrc_start (GstBaseSrc * bsrc) { HRESULT hres = S_FALSE; GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc); - - gst_dshowvideosrc_create_capture_filter (src); + DshowDeviceEntry *device_entry; + IMoniker *moniker = NULL; + + device_entry = gst_dshow_select_device (&CLSID_VideoInputDeviceCategory, + src->device, src->device_name, src->device_index); + if (device_entry == NULL) { + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to find device"), (NULL)); + return FALSE; + } + + g_free (src->device); + g_free (src->device_name); + src->device = g_strdup (device_entry->device); + src->device_name = g_strdup (device_entry->device_name); + src->device_index = device_entry->device_index; + moniker = device_entry->moniker; + device_entry->moniker = NULL; + gst_dshow_device_entry_free (device_entry); + + src->video_cap_filter = gst_dshow_create_capture_filter (moniker); + moniker->Release (); + if (src->video_cap_filter == NULL) { + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("Failed to create capture filter for device"), (NULL)); + return FALSE; + } + + src->caps = gst_dshowvideosrc_getcaps_from_capture_filter ( + src->video_cap_filter, (GList**)&src->pins_mediatypes); + if (gst_caps_is_empty (src->caps)) { + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("Failed to get any caps from devce"), (NULL)); + return FALSE; + } /* The filter graph now is created via the IGraphBuilder Interface @@ -600,6 +589,9 @@ gst_dshowvideosrc_start (GstBaseSrc * bsrc) return TRUE; error: + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("Failed to build filter graph"), (NULL)); + if (src->dshow_fakesink) { src->dshow_fakesink->Release (); src->dshow_fakesink = NULL; @@ -858,7 +850,12 @@ gst_dshowvideosrc_stop (GstBaseSrc * bsrc) g_free (src->device); src->device = NULL; } - + + if (src->video_cap_filter) { + src->video_cap_filter->Release (); + src->video_cap_filter = NULL; + } + return TRUE; } @@ -912,8 +909,8 @@ gst_dshowvideosrc_create (GstPushSrc * psrc, GstBuffer ** buf) return GST_FLOW_OK; } -static GstCaps * -gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin) +GstCaps * +gst_dshowvideosrc_getcaps_from_streamcaps (IPin * pin, GList ** pins_mediatypes) { GstCaps *caps = NULL; HRESULT hres = S_OK; @@ -981,8 +978,9 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin) } if (mediacaps) { - src->pins_mediatypes = - g_list_append (src->pins_mediatypes, pin_mediatype); + if (pins_mediatypes != NULL) { + *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype); + } gst_caps_append (caps, mediacaps); } else { /* failed to convert dshow caps */ @@ -1001,8 +999,8 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin) return caps; } -static GstCaps * -gst_dshowvideosrc_getcaps_from_enum_mediatypes (GstDshowVideoSrc * src, IPin * pin) +GstCaps * +gst_dshowvideosrc_getcaps_from_enum_mediatypes (IPin * pin, GList ** pins_mediatypes) { GstCaps *caps = NULL; IEnumMediaTypes *enum_mediatypes = NULL; @@ -1036,8 +1034,9 @@ gst_dshowvideosrc_getcaps_from_enum_mediatypes (GstDshowVideoSrc * src, IPin * p } if (mediacaps) { - src->pins_mediatypes = - g_list_append (src->pins_mediatypes, pin_mediatype); + if (pins_mediatypes != NULL) { + *pins_mediatypes = g_list_append (*pins_mediatypes, pin_mediatype); + } gst_caps_append (caps, mediacaps); } else { /* failed to convert dshow caps */ diff --git a/sys/dshowsrcwrapper/gstdshowvideosrc.h b/sys/dshowsrcwrapper/gstdshowvideosrc.h index c18271fb8..3faa59989 100644 --- a/sys/dshowsrcwrapper/gstdshowvideosrc.h +++ b/sys/dshowsrcwrapper/gstdshowvideosrc.h @@ -100,5 +100,10 @@ struct _GstDshowVideoSrcClass GType gst_dshowvideosrc_get_type (void); + +GstCaps * gst_dshowvideosrc_getcaps_from_capture_filter (IBaseFilter * filter, + GList ** pins_mediatypes); + + G_END_DECLS #endif /* __GST_DSHOWVIDEOSRC_H__ */ |