dshowsrcwrapper: refactor device selection, filter creation, and caps retrieval

This allows a future GstDeviceProvider to more easily query devices and caps.
This commit is contained in:
Joshua M. Doe 2018-10-16 11:45:15 -04:00 committed by Nirbheek Chauhan
parent e70af38d4e
commit 912ff02a21
4 changed files with 330 additions and 111 deletions

View file

@ -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;

View file

@ -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_ */

View file

@ -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;
IPin *capture_pin = NULL;
IEnumPins *enumpins = NULL;
HRESULT hres;
GstCaps *caps;
/* 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_assert (filter);
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;
}
}
caps = gst_caps_new_empty ();
GST_DEBUG_OBJECT (src, "Opening device-index=%d, device-name='%s', device='%s'",
src->device_index, src->device_name, src->device);
unidevice =
g_utf8_to_utf16 (src->device, strlen (src->device), NULL, NULL, NULL);
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 ();
}
}
capture_pin->QueryInterface (IID_IKsPropertySet, (LPVOID *) & pKs);
if (SUCCEEDED (hres) && pKs) {
DWORD cbReturned;
GUID pin_category;
RPC_STATUS rpcstatus;
g_free (unidevice);
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;
pKs->Get (AMPROPSETID_Pin,
AMPROPERTY_PIN_CATEGORY, NULL, 0, &pin_category, sizeof (GUID),
&cbReturned);
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);
}
}
/* 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 */

View file

@ -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__ */