From d01234419b615792cd6447cb925ba16cc12ef32e Mon Sep 17 00:00:00 2001 From: rogerdpack Date: Fri, 23 Jan 2015 05:34:30 -0700 Subject: dshow: allow selecting devices by an alternative name (workaround for devices with symbols in them), allow specifying capture pins by name and alternative (unique) name Signed-off-by: rogerdpack --- doc/indevs.texi | 14 ++++++++- libavdevice/dshow.c | 88 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/doc/indevs.texi b/doc/indevs.texi index fa6facf96e..00820af246 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -167,7 +167,7 @@ The input name should be in the format: @end example where @var{TYPE} can be either @var{audio} or @var{video}, -and @var{NAME} is the device's name. +and @var{NAME} is the device's name or alternative name.. @subsection Options @@ -220,6 +220,12 @@ Setting this value too low can degrade performance. See also @url{http://msdn.microsoft.com/en-us/library/windows/desktop/dd377582(v=vs.85).aspx} +@item video_pin_name +Select video capture pin to use by name or alternative name. + +@item audio_pin_name +Select audio capture pin to use by name or alternative name. + @end table @subsection Examples @@ -256,6 +262,12 @@ Print the list of supported options in selected device and exit: $ ffmpeg -list_options true -f dshow -i video="Camera" @end example +@item +Specify pin names to capture by name or alternative name, specify alternative device name: +@example +$ ffmpeg -f dshow -audio_pin_name "Audio Out" -video_pin_name 2 -i video=video="@device_pnp_\\?\pci#ven_1a0a&dev_6200&subsys_62021461&rev_01#4&e2c7dd6&0&00e1#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{ca465100-deb0-4d59-818f-8c477184adf6}":audio="Microphone" +@end example + @end itemize @section dv1394 diff --git a/libavdevice/dshow.c b/libavdevice/dshow.c index f326ca2bdc..0497e09456 100644 --- a/libavdevice/dshow.c +++ b/libavdevice/dshow.c @@ -40,6 +40,8 @@ struct dshow_ctx { int list_options; int list_devices; int audio_buffer_size; + char *video_pin_name; + char *audio_pin_name; IBaseFilter *device_filter[2]; IPin *device_pin[2]; @@ -269,8 +271,31 @@ dshow_cycle_devices(AVFormatContext *avctx, ICreateDevEnum *devenum, while (!device_filter && IEnumMoniker_Next(classenum, 1, &m, NULL) == S_OK) { IPropertyBag *bag = NULL; - char *buf = NULL; + char *friendly_name = NULL; + char *unique_name = NULL; VARIANT var; + IBindCtx *bind_ctx = NULL; + LPOLESTR olestr = NULL; + LPMALLOC co_malloc = NULL; + int i; + + r = CoGetMalloc(1, &co_malloc); + if (r = S_OK) + goto fail1; + r = CreateBindCtx(0, &bind_ctx); + if (r != S_OK) + goto fail1; + /* GetDisplayname works for both video and audio, DevicePath doesn't */ + r = IMoniker_GetDisplayName(m, bind_ctx, NULL, &olestr); + if (r != S_OK) + goto fail1; + unique_name = dup_wchar_to_utf8(olestr); + /* replace ':' with '_' since we use : to delineate between sources */ + for (i = 0; i < strlen(unique_name); i++) { + if (unique_name[i] == ':') + unique_name[i] = '_'; + } + r = IMoniker_BindToStorage(m, 0, 0, &IID_IPropertyBag, (void *) &bag); if (r != S_OK) @@ -281,23 +306,35 @@ dshow_cycle_devices(AVFormatContext *avctx, ICreateDevEnum *devenum, if (r != S_OK) goto fail1; - buf = dup_wchar_to_utf8(var.bstrVal); + friendly_name = dup_wchar_to_utf8(var.bstrVal); - if (pfilter) { - if (strcmp(device_name, buf)) + if (pfilter) { + if (strcmp(device_name, friendly_name) && strcmp(device_name, unique_name)) goto fail1; - if (!skip--) - IMoniker_BindToObject(m, 0, 0, &IID_IBaseFilter, (void *) &device_filter); + if (!skip--) { + r = IMoniker_BindToObject(m, 0, 0, &IID_IBaseFilter, (void *) &device_filter); + if (r != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Unable to BindToObject for %s\n", device_name); + goto fail1; + } + } } else { - av_log(avctx, AV_LOG_INFO, " \"%s\"\n", buf); + av_log(avctx, AV_LOG_INFO, " \"%s\"\n", friendly_name); + av_log(avctx, AV_LOG_INFO, " Alternative name \"%s\"\n", unique_name); } fail1: - av_free(buf); + if (olestr && co_malloc) + IMalloc_Free(co_malloc, olestr); + if (bind_ctx) + IBindCtx_Release(bind_ctx); + av_free(friendly_name); + av_free(unique_name); if (bag) IPropertyBag_Release(bag); IMoniker_Release(m); + } IEnumMoniker_Release(classenum); @@ -550,6 +587,10 @@ dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype, AM_MEDIA_TYPE *type; GUID category; DWORD r2; + char *name_buf = NULL; + wchar_t *pin_id = NULL; + char *pin_buf = NULL; + char *desired_pin_name = devtype == VideoDevice ? ctx->video_pin_name : ctx->audio_pin_name; IPin_QueryPinInfo(pin, &info); IBaseFilter_Release(info.pFilter); @@ -563,14 +604,29 @@ dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype, goto next; if (!IsEqualGUID(&category, &PIN_CATEGORY_CAPTURE)) goto next; + name_buf = dup_wchar_to_utf8(info.achName); + + r = IPin_QueryId(pin, &pin_id); + if (r != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not query pin id\n"); + return AVERROR(EIO); + } + pin_buf = dup_wchar_to_utf8(pin_id); + if (!ppin) { - char *buf = dup_wchar_to_utf8(info.achName); - av_log(avctx, AV_LOG_INFO, " Pin \"%s\"\n", buf); - av_free(buf); + av_log(avctx, AV_LOG_INFO, " Pin \"%s\" (alternative pin name \"%s\")\n", name_buf, pin_buf); dshow_cycle_formats(avctx, devtype, pin, NULL); goto next; } + if (desired_pin_name) { + if(strcmp(name_buf, desired_pin_name) && strcmp(pin_buf, desired_pin_name)) { + av_log(avctx, AV_LOG_DEBUG, "skipping pin \"%s\" (\"%s\") != requested \"%s\"\n", + name_buf, pin_buf, desired_pin_name); + goto next; + } + } + if (set_format) { dshow_cycle_formats(avctx, devtype, pin, &format_set); if (!format_set) { @@ -590,6 +646,7 @@ dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype, while (!device_pin && IEnumMediaTypes_Next(types, 1, &type, NULL) == S_OK) { if (IsEqualGUID(&type->majortype, mediatype[devtype])) { device_pin = pin; + av_log(avctx, AV_LOG_DEBUG, "Selecting pin %s on %s\n", name_buf, devtypename); goto next; } CoTaskMemFree(type); @@ -602,6 +659,11 @@ next: IKsPropertySet_Release(p); if (device_pin != pin) IPin_Release(pin); + av_free(name_buf); + av_free(pin_buf); + if (pin_id) + CoTaskMemFree(pin_id); + } IEnumPins_Release(pins); @@ -1066,6 +1128,7 @@ static const AVOption options[] = { { "sample_rate", "set audio sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, { "sample_size", "set audio sample size", OFFSET(sample_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 16, DEC }, { "channels", "set number of audio channels, such as 1 or 2", OFFSET(channels), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, + { "audio_buffer_size", "set audio device buffer latency size in milliseconds (default is the device's default)", OFFSET(audio_buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, { "list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, DEC, "list_devices" }, { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, DEC, "list_devices" }, { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_devices" }, @@ -1074,7 +1137,8 @@ static const AVOption options[] = { { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_options" }, { "video_device_number", "set video device number for devices with same name (starts at 0)", OFFSET(video_device_number), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, { "audio_device_number", "set audio device number for devices with same name (starts at 0)", OFFSET(audio_device_number), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, - { "audio_buffer_size", "set audio device buffer latency size in milliseconds (default is the device's default)", OFFSET(audio_buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, + { "video_pin_name", "select video capture pin by name", OFFSET(video_pin_name),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, + { "audio_pin_name", "select audio capture pin by name", OFFSET(audio_pin_name),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; -- cgit v1.2.3