[FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API

Don Moir donmoir at comcast.net
Wed Feb 5 02:35:09 CET 2014


----- Original Message ----- 
From: "Lukasz Marek" <lukasz.m.luki at gmail.com>
To: <ffmpeg-devel at ffmpeg.org>
Cc: "Lukasz Marek" <lukasz.m.luki at gmail.com>
Sent: Sunday, February 02, 2014 7:02 PM
Subject: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API


> Signed-off-by: Lukasz Marek <lukasz.m.luki at gmail.com>
> ---
> libavdevice/avdevice.c | 191 +++++++++++++++++++++++++++++++++++++++
> libavdevice/avdevice.h | 238 +++++++++++++++++++++++++++++++++++++++++++++++++
> libavdevice/version.h  |   2 +-
> libavformat/avformat.h |  12 +++
> libavformat/version.h  |   2 +-
> 5 files changed, 443 insertions(+), 2 deletions(-)
>
> diff --git a/libavdevice/avdevice.c b/libavdevice/avdevice.c
> index 51617fb..fa524a2 100644
> --- a/libavdevice/avdevice.c
> +++ b/libavdevice/avdevice.c
> @@ -17,9 +17,48 @@
>  */
>
> #include "libavutil/avassert.h"
> +#include "libavcodec/avcodec.h"
> #include "avdevice.h"
> #include "config.h"
>
> +#define AVDEVICE_AV_PARAM     AV_OPT_FLAG_AUDIO_PARAM    | AV_OPT_FLAG_VIDEO_PARAM
> +#define AVDEVICE_DECENC_PARAM AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +#define AVDEVICE_ALL_PARAM    AVDEVICE_AV_PARAM          | AVDEVICE_DECENC_PARAM
> +
> +const AVOption av_device_capabilities[] = {
> +    { "__device_name",    "device name", offsetof(AVDeviceCapabilities, device_name), AV_OPT_TYPE_STRING,
> +        {.str = NULL}, 0, 0, AVDEVICE_ALL_PARAM },
> +    { "__device_context",  "device context", offsetof(AVDeviceCapabilities, device_context), AV_OPT_TYPE_POINTER,
> +        {.str = NULL}, 0, 0, AVDEVICE_ALL_PARAM },
> +    { "__codec",          "codec", offsetof(AVDeviceCapabilities, codec), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AVDEVICE_ALL_PARAM },
> +    { "__format",         "format", offsetof(AVDeviceCapabilities, format), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AVDEVICE_ALL_PARAM },
> +
> +    { "__sample_rate",    "sample rate", offsetof(AVDeviceCapabilities, sample_rate), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_AUDIO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__channels",       "channels", offsetof(AVDeviceCapabilities, channels), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_AUDIO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__channel_layout", "channel layout", offsetof(AVDeviceCapabilities, channel_layout), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_AUDIO_PARAM | AVDEVICE_DECENC_PARAM },
> +
> +    { "__window_width",  "window width", offsetof(AVDeviceCapabilities, window_width), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__window_height", "window height", offsetof(AVDeviceCapabilities, window_height), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__frame_width",   "frame width", offsetof(AVDeviceCapabilities, frame_width), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__frame_height",  "frame height", offsetof(AVDeviceCapabilities, frame_height), AV_OPT_TYPE_INT,
> +        {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { "__fps",            "fps", offsetof(AVDeviceCapabilities, fps), AV_OPT_TYPE_RATIONAL,
> +        {.dbl = -1}, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AVDEVICE_DECENC_PARAM },
> +    { NULL }
> +};
> +
> +#undef AVDEVICE_AV_PARAM
> +#undef AVDEVICE_DECENC_PARAM
> +#undef AVDEVICE_ALL_PARAM
> +
> unsigned avdevice_version(void)
> {
>     av_assert0(LIBAVDEVICE_VERSION_MICRO >= 100);
> @@ -52,3 +91,155 @@ int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToA
>         return AVERROR(ENOSYS);
>     return s->control_message_cb(s, type, data, data_size);
> }
> +
> +static const char * get_opt_name_from_cap_enum(enum AVDeviceCapability capability)
> +{
> +    switch (capability) {
> +    case AV_DEV_CAP_DEVICE_NAME:
> +        return "__device_name";
> +    case AV_DEV_CAP_CODEC_ID:
> +        return "__codec";
> +    case AV_DEV_CAP_FORMAT:
> +        return "__format";
> +    case AV_DEV_CAP_SAMPLE_RATE:
> +        return "__sample_rate";
> +    case AV_DEV_CAP_CHANNELS:
> +        return "__channels";
> +    case AV_DEV_CAP_CHANNEL_LAYOUT:
> +        return "__channel_layout";
> +    case AV_DEV_CAP_WINDOW_WIDTH:
> +        return "__window_width";
> +    case AV_DEV_CAP_WINDOW_HEIGHT:
> +        return "__window_height";
> +    case AV_DEV_CAP_FRAME_WIDTH:
> +        return "__frame_width";
> +    case AV_DEV_CAP_FRAME_HEIGHT:
> +        return "__frame_height";
> +    case AV_DEV_CAP_FPS:
> +        return "__fps";
> +    default:
> +        break;
> +    }
> +    return NULL;
> +}
> +int avdevice_init_device_capabilities(AVFormatContext *s, AVDictionary **device_options)
> +{
> +    int ret;
> +    if ((ret = av_opt_set_pointer(s->priv_data, "__device_context", s,
> +                                  AV_OPT_SEARCH_CHILDREN)) < 0)
> +        return (ret == AVERROR_OPTION_NOT_FOUND) ? AVERROR(ENOSYS) : ret;
> +    if ((ret = av_opt_set_dict(s->priv_data, device_options)) < 0)
> +        return ret;
> +    return 0;
> +}
> +
> +int avdevice_finish_device_capabilities(AVFormatContext *s,
> +                                        AVDeviceCapabilities **spec,
> +                                        enum AVDeviceApplyStrategy strategy)
> +{
> +    if (!s->oformat || !s->oformat->apply_configuration)
> +        return AVERROR(ENOSYS);
> +    return s->oformat->apply_configuration(s, (void **)spec, strategy);
> +}
> +
> +void avdevice_free_device_capabilities(AVDeviceCapabilities **spec)
> +{
> +    if (!spec || !(*spec))
> +        return;
> +    av_free((*spec)->device_name);
> +    av_freep(spec);
> +}
> +
> +int avdevice_get_device_capability(AVFormatContext *s, enum AVDeviceCapability capability,
> +                                   AVOptionRanges **allowed_values)
> +{
> +    const char *opt_name;
> +    if (!s || !allowed_values ||
> +        !(opt_name = get_opt_name_from_cap_enum(capability)))
> +        return AVERROR(EINVAL);
> +    return av_opt_query_ranges(allowed_values, s->priv_data, opt_name, AV_OPT_SEARCH_CHILDREN);
> +}
> +
> +int avdevice_set_device_capability_int(AVFormatContext *s,
> +                                       enum AVDeviceCapability capability, int64_t value)
> +{
> +    const char *opt_name;
> +    if (!s || !(opt_name = get_opt_name_from_cap_enum(capability)))
> +        return AVERROR(EINVAL);
> +    switch (capability) {
> +    case AV_DEV_CAP_CODEC_ID:
> +    case AV_DEV_CAP_FORMAT:
> +    case AV_DEV_CAP_SAMPLE_RATE:
> +    case AV_DEV_CAP_CHANNELS:
> +    case AV_DEV_CAP_CHANNEL_LAYOUT:
> +    case AV_DEV_CAP_WINDOW_WIDTH:
> +    case AV_DEV_CAP_WINDOW_HEIGHT:
> +    case AV_DEV_CAP_FRAME_WIDTH:
> +    case AV_DEV_CAP_FRAME_HEIGHT:
> +        return av_opt_set_int(s->priv_data, opt_name, value, AV_OPT_SEARCH_CHILDREN);
> +    default:
> +        break;
> +    }
> +    av_log(s, AV_LOG_ERROR, "Capability %s is not of integer type.\n", opt_name);
> +    return AVERROR(EINVAL);
> +}
> +
> +int avdevice_set_device_capability_string(AVFormatContext *s,
> +                                          enum AVDeviceCapability capability,
> +                                          const char *value)
> +{
> +    const char *opt_name;
> +    if (!s || !(opt_name = get_opt_name_from_cap_enum(capability)))
> +        return AVERROR(EINVAL);
> +    switch (capability) {
> +    case AV_DEV_CAP_DEVICE_NAME:
> +        return av_opt_set(s->priv_data, opt_name, value, AV_OPT_SEARCH_CHILDREN);
> +    default:
> +        break;
> +    }
> +    av_log(s, AV_LOG_ERROR, "Capability %s is not of string type.\n", opt_name);
> +    return AVERROR(EINVAL);
> +}
> +
> +int avdevice_set_device_capability_q(AVFormatContext *s,
> +                                     enum AVDeviceCapability capability,
> +                                     AVRational value)
> +{
> +    const char *opt_name;
> +    if (!s || !(opt_name = get_opt_name_from_cap_enum(capability)))
> +        return AVERROR(EINVAL);
> +    switch (capability) {
> +    case AV_DEV_CAP_FPS:
> +        return av_opt_set_q(s->priv_data, opt_name, value, AV_OPT_SEARCH_CHILDREN);
> +    default:
> +        break;
> +    }
> +    av_log(s, AV_LOG_ERROR, "Capability %s is not of AVRational type.\n", opt_name);
> +    return AVERROR(EINVAL);
> +}
> +
> +int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list)
> +{
> +    if (!s->oformat || !s->oformat->get_device_list)
> +        return AVERROR(ENOSYS);
> +    return s->oformat->get_device_list(s, (void **)device_list);
> +}
> +
> +void avdevice_free_list_devices(AVDeviceInfoList **device_list)
> +{
> +    AVDeviceInfoList *list;
> +    AVDeviceInfo *dev;
> +    int i;
> +
> +    if (!device_list || !(*device_list))
> +        return;
> +    list = *device_list;
> +
> +    for (i = 0; i < list->nb_devices; i++) {
> +        dev = &list->devices[i];
> +        av_free(dev->device_name);
> +        av_free(dev->device_description);
> +        av_free(dev);
> +    }
> +    av_freep(device_list);
> +}
> diff --git a/libavdevice/avdevice.h b/libavdevice/avdevice.h
> index a6408ea..bfcca35 100644
> --- a/libavdevice/avdevice.h
> +++ b/libavdevice/avdevice.h
> @@ -43,6 +43,9 @@
>  * @}
>  */
>
> +#include "libavutil/log.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/dict.h"
> #include "libavformat/avformat.h"
>
> /**
> @@ -186,4 +189,239 @@ int avdevice_dev_to_app_control_message(struct AVFormatContext *s,
>                                         enum AVDevToAppMessageType type,
>                                         void *data, size_t data_size);
>
> +/**
> + * Structure describes device capabilites.
> + *
> + * It is used by devices in conjuntion with av_device_capabilities AVOption table
> + * to to implement capabilities probing API.
> + */
> +typedef struct AVDeviceCapabilities {
> +    const AVClass *class;
> +    char *device_name;
> +    AVFormatContext *device_context;
> +    enum AVCodecID codec;
> +    int format;                          /**< AVSampleFormat or AVPixelFormat */
> +    int sample_rate;
> +    int channels;
> +    int64_t channel_layout;
> +    int window_width;
> +    int window_height;
> +    int frame_width;
> +    int frame_height;
> +    AVRational fps;
> +} AVDeviceCapabilities;
> +
> +extern const AVOption av_device_capabilities[];
> +
> +/**
> + * Enumerates device capabilities that can be probed.
> + */
> +enum AVDeviceCapability {
> +    /**
> +     * Device name.
> +     *
> +     * set:  set the device to read capability of.
> +     * get:  value previously set, use avdevice_list_devices()
> +     *       to get full list of the devices.
> +     * type: string.
> +     */
> +    AV_DEV_CAP_DEVICE_NAME,
> +
> +    /**
> +     * Supported codecs.
> +     *
> +     * set:  limit following queries to configurations supporting the codec.
> +     * get:  list all supported codecs.
> +     * type: int (enum AVCodecID).
> +     */
> +    AV_DEV_CAP_CODEC_ID,
> +
> +    /**
> +     * Supported sample/pixel formats.
> +     *
> +     * set:  limit following queries to configurations supporting the format.
> +     * get:  list all supported formats.
> +     * type: int (enum AVSampleFormat / enum AVPixelFormat).
> +     */
> +    AV_DEV_CAP_FORMAT,
> +
> +    /**
> +     * Supported sample/pixel formats.
> +     *
> +     * set:  limit following queries to configurations supporting the format.
> +     * get:  list all supported formats.
> +     * type: int (enum AVSampleFormat / enum AVPixelFormat).
> +     */
> +    AV_DEV_CAP_SAMPLE_RATE,
> +
> +    /**
> +     * Supported channels count.
> +     *
> +     * set:  limit following queries to configurations supporting the cannels count.
> +     * get:  list all supported channels count.
> +     * type: int.
> +     */
> +    AV_DEV_CAP_CHANNELS,
> +
> +    /**
> +     * Supported cannel layouts.
> +     *
> +     * set:  limit following queries to configurations supporting the cannel layouts.
> +     * get:  list all supported cannel layouts.
> +     * type: int.
> +     */
> +    AV_DEV_CAP_CHANNEL_LAYOUT,
> +
> +    /**
> +     * Supported window width/height.
> +     *
> +     * set:  limit following queries to configurations supporting the window width/height.
> +     * get:  list range of supported window width/height.
> +     * type: int.
> +     */
> +    AV_DEV_CAP_WINDOW_WIDTH,
> +    AV_DEV_CAP_WINDOW_HEIGHT,
> +
> +    /**
> +     * Supported frame width/height.
> +     *
> +     * set:  limit following queries to configurations supporting the frame width/height.
> +     * get:  list range of supported frame width/height.
> +     * type: int.
> +     */
> +    AV_DEV_CAP_FRAME_WIDTH,
> +    AV_DEV_CAP_FRAME_HEIGHT,
> +
> +    /**
> +     * Supported frames per second.
> +     *
> +     * set:  limit following queries to configurations supporting the fps.
> +     * get:  list range of supported fps.
> +     * type: int.
> +     */
> +    AV_DEV_CAP_FPS
> +};
> +
> +enum AVDeviceApplyStrategy {
> +    AVDeviceApplyStrategyAbandon,           /**< don't apply settings to device */
> +    AVDeviceApplyStrategyAbandonNotValid,   /**< don't apply settings to device when invalid */
> +    AVDeviceApplyFixToTheNearestValidValue, /**< adjust values to the nearest valid value */
> +    AVDeviceApplyFixToTheBestValidValue     /**< adjust values to the best valid value */
> +};
> +
> +/**
> + * Function prepares the device to be probed.
> + *
> + * This function must be called before using av_device_get_device_capability()
> + * or av_device_set_device_capability_*().
> + * avdevice_finish_device_capabilities() must be called afterwards.
> + *
> + * @param s              device context.
> + * @param device_options device-specific options.
> + * @return >= 0 on success, negative otherwise.
> + */
> +int avdevice_init_device_capabilities(AVFormatContext *s,
> +                                      AVDictionary **device_options);
> +
> +/**
> + * Apply set parameters to device context and release data allocated
> + * by avdevice_init_device_capabilities().
> + *
> + * All set capabilities are validated and tested. When configuration is not
> + * working then adjustment takes place according to provided strategy.
> + * After potential adjustments, set capabilities are applied and device configuration.
> + * Mapping between capablities and device settings are device-specific.
> + * In particular output device may not apply all parameters to the context,
> + * but use stream properties when avformat_write_header() is called.
> + *
> + * @note: This may be useful to validate if input stream may be passed directly
> + *        to output device, but usually capabilites should be tested one by one
> + *        and correct values should be provided.
> + *
> + * @param s         device context
> + * @param[out] spec returns device configuration. May be NULL. If not NULL
> + *                  then must be freed with avdevice_free_device_capabilities().
> + * @param strategy  fixing values strategy. Ignored when spec is not provided.
> + * @return >= 0 on success, negative otherwise.
> + *         AVERROR(EINVAL) when strategy is not implemented.
> + */
> +int avdevice_finish_device_capabilities(AVFormatContext *s,
> +                                        AVDeviceCapabilities **spec,
> +                                        enum AVDeviceApplyStrategy strategy);
> +
> +/**
> + * Free AVDeviceCapabilities struct and its allocated data.
> + *
> + * @param spec structure to be freed
> + */
> +void avdevice_free_device_capabilities(AVDeviceCapabilities **spec);
> +
> +/**
> + * Return range(s) of valid values for device capability.
> + *
> + * This function allow to get ranges of values for device capability.
> + * Returned ranges takes into account real device capablities and
> + * values previously set by avdevice_set_device_capability_* functions.
> + * For example list of supported formats may dependes on the codec set previously,
> + * fps may depends on resolution or codec set previously.
> + *
> + * @param s           device context
> + * @param capability  capability to be tested.
> + * @param[out] ranges list of allowed ranges for selected capability.
> + * @return >= 0 on success, negative otherwise.
> + */
> +int avdevice_get_device_capability(AVFormatContext *s,
> +                                   enum AVDeviceCapability capability,
> +                                   AVOptionRanges **ranges);
> +
> +/**
> + * Set device capability.
> + *
> + * @param s     device context
> + * @param value new value for capability
> + * @return >= 0 on success, negative otherwise.
> + */
> +int avdevice_set_device_capability_int(AVFormatContext *s,
> +                                       enum AVDeviceCapability capability,
> +                                       int64_t value);
> +int avdevice_set_device_capability_string(AVFormatContext *s,
> +                                          enum AVDeviceCapability capability,
> +                                          const char *value);
> +int avdevice_set_device_capability_q(AVFormatContext *s,
> +                                     enum AVDeviceCapability capability,
> +                                     AVRational value);
> +
> +/**
> + * Structure describes basic parameters of the device.
> + */
> +typedef struct AVDeviceInfo {
> +    char *device_name;                   /**< device name, format depends on device */
> +    char *device_description;            /**< human friendly name */
> +} AVDeviceInfo;
> +
> +/**
> + * List of available devices.
> + */
> +typedef struct AVDeviceInfoList {
> +    AVDeviceInfo *devices;               /**< list of autodetected devices */
> +    int nb_devices;                      /**< number of autodetected devices */
> +    int default_device;                  /**< index of default device */
> +} AVDeviceInfoList;
> +
> +/**
> + * List available devices.
> + *
> + * @param ofmt         device format.
> + * @param[out] devices list of autodetected devices.
> + * @return count of autodetected devices, negative on error.
> + */
> +int avdevice_list_devices(struct AVFormatContext *s, AVDeviceInfoList **device_list);
> +
> +/**
> + * Convinient function to free result of avdevice_list_devices().
> + *
> + * @param devices device list to be freed.
> + */
> +void avdevice_free_list_devices(AVDeviceInfoList **device_list);
> +
> #endif /* AVDEVICE_AVDEVICE_H */
> diff --git a/libavdevice/version.h b/libavdevice/version.h
> index a621775..0dedc73 100644
> --- a/libavdevice/version.h
> +++ b/libavdevice/version.h
> @@ -28,7 +28,7 @@
> #include "libavutil/version.h"
>
> #define LIBAVDEVICE_VERSION_MAJOR  55
> -#define LIBAVDEVICE_VERSION_MINOR   7
> +#define LIBAVDEVICE_VERSION_MINOR   8
> #define LIBAVDEVICE_VERSION_MICRO 100
>
> #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
> diff --git a/libavformat/avformat.h b/libavformat/avformat.h
> index 50b7108..58bed4e 100644
> --- a/libavformat/avformat.h
> +++ b/libavformat/avformat.h
> @@ -458,6 +458,18 @@ typedef struct AVOutputFormat {
>      */
>     int (*control_message)(struct AVFormatContext *s, int type,
>                            void *data, size_t data_size);
> +    /**
> +     * Returns device list with it properties.
> +     * @see avdevice_list_devices() for more details.
> +     */
> +    int (*get_device_list)(struct AVFormatContext *s, void **device_list);
> +    /**
> +     * Allows to apply device configuration via avdevice_set_device_capability_*
> +     * API with posibility to adjust not matching configuration.
> +     * @see avdevice_finish_device_capabilities() for more details.
> +     */
> +    int (*apply_configuration)(struct AVFormatContext *s, void **configuration,
> +                               int strategy);
> } AVOutputFormat;
> /**
>  * @}
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 38945a5..0fcbe60 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -30,7 +30,7 @@
> #include "libavutil/version.h"
>
> #define LIBAVFORMAT_VERSION_MAJOR 55
> -#define LIBAVFORMAT_VERSION_MINOR 29
> +#define LIBAVFORMAT_VERSION_MINOR 30
> #define LIBAVFORMAT_VERSION_MICRO 100
>
> #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
> -- 

Hi Lukasz,

I am not sure about your device list API.

You have:

typedef struct AVDeviceInfoList {
    AVDeviceInfo *devices;               /**< list of autodetected devices */
    int nb_devices;                      /**< number of autodetected devices */
    int default_device;                  /**< index of default device */
} AVDeviceInfoList;

int avdevice_list_devices(struct AVFormatContext *s, AVDeviceInfoList **device_list);

Not sure why I would need an AVFormatContext but I may missing something there.

Just not exactly clear so this is just what makes the most sense to me.

1)  in avdevice_list_devices,  identify type of video and or audio devices.

2)  Provide a list and their capabilites at same time. So maybe:

typedef struct AVDeviceInfo {
    char *device_name;                   /**< device name, format depends on device */
    char *device_description;            /**< human friendly name */
    // either list or count
    int  n_capabilities;
   AVDeviceCapabilities *capabilities;
} AVDeviceInfo;

I know you have ways of doing it, but it seems akward at best and then more work to first find devices and then lookup capabilities 
for each deivce. I see I must init something to get capabilities as well so just don't see how that falls in line well. 



More information about the ffmpeg-devel mailing list