opts: add list device sources/sinks options
authorLukasz Marek <lukasz.m.luki2@gmail.com>
Wed, 6 Aug 2014 22:45:26 +0000 (00:45 +0200)
committerLukasz Marek <lukasz.m.luki2@gmail.com>
Sat, 25 Oct 2014 18:20:31 +0000 (20:20 +0200)
Allows to list sources/sinks of the devices that implement
that functionality.

Signed-off-by: Lukasz Marek <lukasz.m.luki2@gmail.com>
cmdutils.c
cmdutils.h
cmdutils_common_opts.h
doc/fftools-common-opts.texi

index 46bfcca..b777396 100644 (file)
@@ -2055,3 +2055,184 @@ void *grow_array(void *array, int elem_size, int *size, int new_size)
     }
     return array;
 }
+
+#if CONFIG_AVDEVICE
+static int print_device_sources(AVInputFormat *fmt, AVDictionary *opts)
+{
+    int ret, i;
+    AVFormatContext *dev = NULL;
+    AVDeviceInfoList *device_list = NULL;
+    AVDictionary *tmp_opts = NULL;
+
+    if (!fmt || !fmt->priv_class  || !AV_IS_INPUT_DEVICE(fmt->priv_class->category))
+        return AVERROR(EINVAL);
+
+    printf("Audo-detected sources for %s:\n", fmt->name);
+    if (!fmt->get_device_list) {
+        ret = AVERROR(ENOSYS);
+        printf("Cannot list sources. Not implemented.\n");
+        goto fail;
+    }
+
+    /* TODO: avformat_open_input calls read_header callback which is not necessary.
+             Function like avformat_alloc_output_context2 for input could be helpful here. */
+    av_dict_copy(&tmp_opts, opts, 0);
+    if ((ret = avformat_open_input(&dev, NULL, fmt, &tmp_opts)) < 0) {
+        printf("Cannot open device: %s.\n", fmt->name);
+        goto fail;
+    }
+
+    if ((ret = avdevice_list_devices(dev, &device_list)) < 0) {
+        printf("Cannot list sources.\n");
+        goto fail;
+    }
+
+    for (i = 0; i < device_list->nb_devices; i++) {
+        printf("%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
+               device_list->devices[i]->device_name, device_list->devices[i]->device_description);
+    }
+
+  fail:
+    av_dict_free(&tmp_opts);
+    avdevice_free_list_devices(&device_list);
+    avformat_close_input(&dev);
+    return ret;
+}
+
+static int print_device_sinks(AVOutputFormat *fmt, AVDictionary *opts)
+{
+    int ret, i;
+    AVFormatContext *dev = NULL;
+    AVDeviceInfoList *device_list = NULL;
+    AVDictionary *tmp_opts = NULL;
+
+    if (!fmt || !fmt->priv_class  || !AV_IS_OUTPUT_DEVICE(fmt->priv_class->category))
+        return AVERROR(EINVAL);
+
+    printf("Audo-detected sinks for %s:\n", fmt->name);
+    if (!fmt->get_device_list) {
+        ret = AVERROR(ENOSYS);
+        printf("Cannot list sinks. Not implemented.\n");
+        goto fail;
+    }
+
+    if ((ret = avformat_alloc_output_context2(&dev, fmt, NULL, NULL)) < 0) {
+        printf("Cannot open device: %s.\n", fmt->name);
+        goto fail;
+    }
+    av_dict_copy(&tmp_opts, opts, 0);
+    av_opt_set_dict2(dev, &tmp_opts, AV_OPT_SEARCH_CHILDREN);
+
+    if ((ret = avdevice_list_devices(dev, &device_list)) < 0) {
+        printf("Cannot list sinks.\n");
+        goto fail;
+    }
+
+    for (i = 0; i < device_list->nb_devices; i++) {
+        printf("%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
+               device_list->devices[i]->device_name, device_list->devices[i]->device_description);
+    }
+
+  fail:
+    av_dict_free(&tmp_opts);
+    avdevice_free_list_devices(&device_list);
+    avformat_free_context(dev);
+    return ret;
+}
+
+static int show_sinks_sources_parse_arg(const char *arg, char **dev, AVDictionary **opts)
+{
+    int ret;
+    if (arg) {
+        char *opts_str = NULL;
+        av_assert0(dev && opts);
+        *dev = av_strdup(arg);
+        if (!*dev)
+            return AVERROR(ENOMEM);
+        if ((opts_str = strchr(*dev, ','))) {
+            *(opts_str++) = '\0';
+            if (opts_str[0] && ((ret = av_dict_parse_string(opts, opts_str, "=", ":", 0)) < 0)) {
+                av_freep(dev);
+                return ret;
+            }
+        }
+    } else
+        printf("\nDevice name is not provided.\n"
+                "You can pass devicename[,opt1=val1[,opt2=val2...]] as an argument.\n\n");
+    return 0;
+}
+
+int show_sources(void *optctx, const char *opt, const char *arg)
+{
+    AVInputFormat *fmt = NULL;
+    char *dev = NULL;
+    AVDictionary *opts = NULL;
+    int ret = 0;
+    int error_level = av_log_get_level();
+
+    av_log_set_level(AV_LOG_ERROR);
+
+    if ((ret = show_sinks_sources_parse_arg(arg, &dev, &opts)) < 0)
+        goto fail;
+
+    do {
+        fmt = av_input_audio_device_next(fmt);
+        if (fmt) {
+            if (!strcmp(fmt->name, "lavfi"))
+                continue; //it's pointless to probe lavfi
+            if (dev && strcmp(fmt->name, dev))
+                continue;
+            print_device_sources(fmt, opts);
+        }
+    } while (fmt);
+    do {
+        fmt = av_input_video_device_next(fmt);
+        if (fmt) {
+            if (dev && strcmp(fmt->name, dev))
+                continue;
+            print_device_sources(fmt, opts);
+        }
+    } while (fmt);
+  fail:
+    av_dict_free(&opts);
+    av_free(dev);
+    av_log_set_level(error_level);
+    return ret;
+}
+
+int show_sinks(void *optctx, const char *opt, const char *arg)
+{
+    AVOutputFormat *fmt = NULL;
+    char *dev = NULL;
+    AVDictionary *opts = NULL;
+    int ret = 0;
+    int error_level = av_log_get_level();
+
+    av_log_set_level(AV_LOG_ERROR);
+
+    if ((ret = show_sinks_sources_parse_arg(arg, &dev, &opts)) < 0)
+        goto fail;
+
+    do {
+        fmt = av_output_audio_device_next(fmt);
+        if (fmt) {
+            if (dev && strcmp(fmt->name, dev))
+                continue;
+            print_device_sinks(fmt, opts);
+        }
+    } while (fmt);
+    do {
+        fmt = av_output_video_device_next(fmt);
+        if (fmt) {
+            if (dev && strcmp(fmt->name, dev))
+                continue;
+            print_device_sinks(fmt, opts);
+        }
+    } while (fmt);
+  fail:
+    av_dict_free(&opts);
+    av_free(dev);
+    av_log_set_level(error_level);
+    return ret;
+}
+#endif
index 76d11a5..f6ad44c 100644 (file)
@@ -443,6 +443,20 @@ int show_formats(void *optctx, const char *opt, const char *arg);
  */
 int show_devices(void *optctx, const char *opt, const char *arg);
 
+#if CONFIG_AVDEVICE
+/**
+ * Print a listing containing audodetected sinks of the output device.
+ * Device name with options may be passed as an argument to limit results.
+ */
+int show_sinks(void *optctx, const char *opt, const char *arg);
+
+/**
+ * Print a listing containing audodetected sources of the input device.
+ * Device name with options may be passed as an argument to limit results.
+ */
+int show_sources(void *optctx, const char *opt, const char *arg);
+#endif
+
 /**
  * Print a listing containing all the codecs supported by the
  * program.
index 49b5180..758dac1 100644 (file)
@@ -27,3 +27,9 @@
     { "opencl_bench", OPT_EXIT, {.func_arg = opt_opencl_bench}, "run benchmark on all OpenCL devices and show results" },
     { "opencl_options", HAS_ARG, {.func_arg = opt_opencl},      "set OpenCL environment options" },
 #endif
+#if CONFIG_AVDEVICE
+    { "sources"    , OPT_EXIT | HAS_ARG, { .func_arg = show_sources },
+      "list sources of the input device", "device" },
+    { "sinks"      , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks },
+      "list sinks of the output device", "device" },
+#endif
index 299b9de..0e8f849 100644 (file)
@@ -141,6 +141,22 @@ Show channel names and standard channel layouts.
 @item -colors
 Show recognized color names.
 
+@item -sources @var{device}[,@var{opt1}=@var{val1}[,@var{opt2}=@var{val2}]...]
+Show autodetected sources of the intput device.
+Some devices may provide system-dependent source names that cannot be autodetected.
+The returned list cannot be assumed to be always complete.
+@example
+ffmpeg -sources pulse,server=192.168.0.4
+@end example
+
+@item -sinks @var{device}[,@var{opt1}=@var{val1}[,@var{opt2}=@var{val2}]...]
+Show autodetected sinks of the output device.
+Some devices may provide system-dependent sink names that cannot be autodetected.
+The returned list cannot be assumed to be always complete.
+@example
+ffmpeg -sinks pulse,server=192.168.0.4
+@end example
+
 @item -loglevel [repeat+]@var{loglevel} | -v [repeat+]@var{loglevel}
 Set the logging level used by the library.
 Adding "repeat+" indicates that repeated log output should not be compressed