This allows a future GstDeviceProvider to more easily query devices and caps.
#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}
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);
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,
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;
}
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'",
*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;
#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;
/* 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_ */
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);
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
{
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
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;
g_free (src->device);
src->device = NULL;
}
-
+
+ if (src->video_cap_filter) {
+ src->video_cap_filter->Release ();
+ src->video_cap_filter = NULL;
+ }
+
return TRUE;
}
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;
}
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 */
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;
}
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 */
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__ */