[PATCH 0/4] Probe device API
This is probing API implementation which started from thread http://ffmpeg.org/pipermail/ffmpeg-devel/2014-January/153614.html It uses AVOptions API with some kind of abstraction layer. I wonder if adding pointer option safe? Lukasz Marek (4): [RFC]lavu/opt: add pointer option lavd: add device capabilities API lavd/opengl_enc: implement query capabilities API examples: opengl_device_settings doc/examples/Makefile | 1 + doc/examples/opengl_device_settings.c | 176 ++++++++++++++++++ libavdevice/avdevice.c | 191 ++++++++++++++++++++ libavdevice/avdevice.h | 238 +++++++++++++++++++++++++ libavdevice/opengl_enc.c | 326 +++++++++++++++++++++++++++++++++- libavdevice/version.h | 2 +- libavformat/avformat.h | 12 ++ libavformat/version.h | 2 +- libavutil/opt.c | 16 ++ libavutil/opt.h | 2 + 10 files changed, 959 insertions(+), 7 deletions(-) create mode 100644 doc/examples/opengl_device_settings.c -- 1.8.3.2
Signed-off-by: Lukasz Marek <lukasz.m.luki@gmail.com> --- libavutil/opt.c | 16 ++++++++++++++++ libavutil/opt.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/libavutil/opt.c b/libavutil/opt.c index 6ecc14e..77241e4 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -614,6 +614,22 @@ int av_opt_set_channel_layout(void *obj, const char *name, int64_t cl, int searc return 0; } +int av_opt_set_pointer(void *obj, const char *name, void *ptr, int search_flags) +{ + void *target_obj; + const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj); + + if (!o || !target_obj) + return AVERROR_OPTION_NOT_FOUND; + if (o->type != AV_OPT_TYPE_POINTER) { + av_log(obj, AV_LOG_ERROR, + "The value set by option '%s' is not a pointer.\n", o->name); + return AVERROR(EINVAL); + } + *(void **)(((uint8_t *)target_obj) + o->offset) = ptr; + return 0; +} + #if FF_API_OLD_AVOPTIONS /** * diff --git a/libavutil/opt.h b/libavutil/opt.h index 14faa6e..27c6a47 100644 --- a/libavutil/opt.h +++ b/libavutil/opt.h @@ -234,6 +234,7 @@ enum AVOptionType{ AV_OPT_TYPE_DURATION = MKBETAG('D','U','R',' '), AV_OPT_TYPE_COLOR = MKBETAG('C','O','L','R'), AV_OPT_TYPE_CHANNEL_LAYOUT = MKBETAG('C','H','L','A'), + AV_OPT_TYPE_POINTER = MKBETAG('P','T','R',' '), #if FF_API_OLD_AVOPTIONS FF_OPT_TYPE_FLAGS = 0, FF_OPT_TYPE_INT, @@ -659,6 +660,7 @@ int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, i int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags); int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags); int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags); +int av_opt_set_pointer(void *obj, const char *name, void *ptr, int search_flags); /** * Set a binary option to an integer list. -- 1.8.3.2
Signed-off-by: Lukasz Marek <lukasz.m.luki@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, \ -- 1.8.3.2
Le quintidi 15 pluviôse, an CCXXII, Lukasz Marek a écrit :
Signed-off-by: Lukasz Marek <lukasz.m.luki@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(-)
I do not have a full view of how you want the API to work yet, but I can still produce a few remarks that will, hopefully, help clarify things.
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
Any reason to use such long names for purely local macros? Other files with similar features use "D", "E", etc. A macro for the repeated offsetof would be advisable too.
+ +const AVOption av_device_capabilities[] = {
+ { "__device_name", "device name", offsetof(AVDeviceCapabilities, device_name), AV_OPT_TYPE_STRING, + {.str = NULL}, 0, 0, AVDEVICE_ALL_PARAM },
Are you sure about the "__" prefix as a namespace isolation? Something more explicit, like, maybe "device." or "device/", or "devcap.", seems nicer.
+ { "__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 },
Should be INT64, no?
+ + { "__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 },
How does it handle situations where a small number frame sizes are possible?
+ { "__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);
Why the cast to void?
+} +
+void avdevice_free_device_capabilities(AVDeviceCapabilities **spec) +{ + if (!spec || !(*spec)) + return; + av_free((*spec)->device_name); + av_freep(spec); +}
I do not see the corresponding alloc function, is it normal?
+ +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);
IMHO, since these errors are obviously invalid use of the API, an assert failure is more adapted.
+ return av_opt_query_ranges(allowed_values, s->priv_data, opt_name, AV_OPT_SEARCH_CHILDREN);
Here and in the following functions, it seems you are just wrapping / duplicating the options code. Why not just allow to use the options API directly? Less code for you, less new API to learn for the others. Maybe I am missing something, but the API could be something like that: AVDeviceCapability *cap; avdevice_capability_create(&cap, fmt_ctx, options); av_opt_set_type(cap, "fps", 30); avdevice_capability_compute(cap); av_opt_query_ranges(cap, "frame_width", &width); avdevice_capability_free(&cap);
+} + +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);
Unless I am mistaken, you are duplicating checks that are already done by the options API. (On the other hand, since you are relying on enums, calling set_int on a non-integer option is an obviously invalid use of the API and deserves an assert failure.)
+} + +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)
This part and the related changes could go in a separate patch. It could probably be applied much faster.
+{ + 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 */
... "or -1 if no default"?
+} AVDeviceInfoList; + +/**
+ * List available devices.
Please remember to explain that some devices can accept both predefined names and synthetic ones, and this function will only list the former.
+ * + * @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
You probably should just note "TODO minor bump" somewhere, and actually add it when the patch is almost ready for inclusion. In particular, this hunk could silently disappear during a rebase if another patch did exactly the same.
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
Regards, -- Nicolas George
+const AVOption av_device_capabilities[] = {
+ { "__device_name", "device name", offsetof(AVDeviceCapabilities, device_name), AV_OPT_TYPE_STRING, + {.str = NULL}, 0, 0, AVDEVICE_ALL_PARAM },
Are you sure about the "__" prefix as a namespace isolation? Something more explicit, like, maybe "device." or "device/", or "devcap.", seems nicer.
Ok, I will change to devcap.
+ { "__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 },
Should be INT64, no?
Yes, thx.
+ + { "__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 },
How does it handle situations where a small number frame sizes are possible?
I don't understand.
+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);
Why the cast to void?
I used void** in lavf to not forward declare structs from lavd, and I got a warning here as I remeber. I will recheck it later.
+void avdevice_free_device_capabilities(AVDeviceCapabilities **spec) +{ + if (!spec || !(*spec)) + return; + av_free((*spec)->device_name); + av_freep(spec); +}
I do not see the corresponding alloc function, is it normal?
It is allocated by AVOption API.
+ +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);
IMHO, since these errors are obviously invalid use of the API, an assert failure is more adapted.
+ return av_opt_query_ranges(allowed_values, s->priv_data, opt_name, AV_OPT_SEARCH_CHILDREN);
Here and in the following functions, it seems you are just wrapping / duplicating the options code. Why not just allow to use the options API directly? Less code for you, less new API to learn for the others.
Maybe I am missing something, but the API could be something like that:
AVDeviceCapability *cap; avdevice_capability_create(&cap, fmt_ctx, options); av_opt_set_type(cap, "fps", 30); avdevice_capability_compute(cap); av_opt_query_ranges(cap, "frame_width", &width); avdevice_capability_free(&cap);
I'm not sure what avdevice_capability_compute should do? In this case calling av_opt_set_ is pointless. You can just set fps in cap structure directly. I had something similar earlier in mind, but there are at least 2 disadvantages: 1. query_ranges implementation require device's AVFormatContext (for options, for control message API app callback, maybe other reasons). In your solution user may free this context and still use avdevice_capablity* which will usually lead to use freed pointer. All function in the sample above may need context, but it is not visible in code. 2. I'm not sure extracting variable names as strings in public API is good idea. Typo in its name may be hard to notice, when you have type in enum you get compilation error. I also wanted to add possibility to set up "any" configuration and use API to adjust it or just validate some configuration. I will rethink it, but I still prefer mine solution over this. -- Best Regards, Lukasz Marek Microsoft isn't evil, they just make really crappy operating systems. - Linus Torvalds
+int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list)
This part and the related changes could go in a separate patch. It could probably be applied much faster.
Good idea, I've sent extracted hunks in separate thread, I will return to the rest of this one later.
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Cc: "Lukasz Marek" <lukasz.m.luki@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@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.
On 05.02.2014 02:35, Don Moir wrote:
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Cc: "Lukasz Marek" <lukasz.m.luki@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@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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name. The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries And I don't think it is too much complicated. -- Best Regards, Lukasz Marek If you can't explain it simply, you don't understand it well enough. - Albert Einstein
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 3:54 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
On 05.02.2014 02:35, Don Moir wrote:
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Cc: "Lukasz Marek" <lukasz.m.luki@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@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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other. Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least. Some devices don't spin up that quickly so good to keep enumeration to a minimum. I would rather use ffmpeg to enumerate the devices and good to see someone is looking at this. At this point though, I may just keep doing what I am doing.
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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point. Basically the resulting structure would need to be more complex, in case you want to get all possible configuration at once and Michael suggested to use AVOption API becuase it solves all these issues You may read this thread, because it is where this idea started http://ffmpeg.org/pipermail/ffmpeg-devel/2014-January/153648.html -- Best Regards, Lukasz Marek Royale with Cheese.
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 6:59 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point.
It probably can be met for any true hardware device which is what I am interested in. SDL and OpenGL and the like, to me fall more in the line of applications issues.
Basically the resulting structure would need to be more complex,
Better to have a more complex structure than to have a complex interface to it. Probably leads to less usage of the thing you are spending time on.
----- Original Message ----- From: "Don Moir" <donmoir@comcast.net> To: "FFmpeg development discussions and patches" <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 7:18 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 6:59 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point.
It probably can be met for any true hardware device which is what I am interested in. SDL and OpenGL and the like, to me fall more in the line of applications issues.
Basically the resulting structure would need to be more complex,
Better to have a more complex structure than to have a complex interface to it. Probably leads to less usage of the thing you are spending time on.
Might be good to separate this out some to simplify it. A lot of people are interested in knowing only about capture devices and could care less about things like SDL and OpenGL in ffmpeg. Could be a simple interface for ennumerating capture devices. Like I said before, you don't really want to walk thru the ennumeration possibly several times for some devices. The ennumeration can cause load and unload of resources and you never know what a device might be initializing. Some do this quickly and some slowly.
On 06.02.2014 03:57, Don Moir wrote:
----- Original Message ----- From: "Don Moir" <donmoir@comcast.net> To: "FFmpeg development discussions and patches" <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 7:18 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 6:59 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point.
It probably can be met for any true hardware device which is what I am interested in. SDL and OpenGL and the like, to me fall more in the line of applications issues.
Basically the resulting structure would need to be more complex,
Better to have a more complex structure than to have a complex interface to it. Probably leads to less usage of the thing you are spending time on.
Might be good to separate this out some to simplify it. A lot of people are interested in knowing only about capture devices and could care less about things like SDL and OpenGL in ffmpeg.
Could be a simple interface for ennumerating capture devices. Like I said before, you don't really want to walk thru the ennumeration possibly several times for some devices. The ennumeration can cause load and unload of resources and you never know what a device might be initializing. Some do this quickly and some slowly.
Solution you suggest is the same I proposed before and was rejected. So I give up any further work on it until you figure out what should it look like. -- Best Regards, Lukasz Marek You can avoid reality, but you cannot avoid the consequences of avoiding reality. - Ayn Rand
On Thu, Feb 06, 2014 at 01:06:03PM +0100, Lukasz Marek wrote:
On 06.02.2014 03:57, Don Moir wrote:
----- Original Message ----- From: "Don Moir" <donmoir@comcast.net> To: "FFmpeg development discussions and patches" <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 7:18 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 6:59 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
>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.
To get dev cap you need context for options for example. In implementation you need to "open" device to check if configuration is really working or list properties ranges. For example pulse audio allows to play on remote server. You need to know that user wants to test remote server and its done by device options.
>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.
Make sure you distinguish device at lavd level (pulseaudio, alsa, oss for audio and fbdev, xv, opengl, sdl for video) and device names for each of them (sound outputs, sound cards etc). This function list the second ones for given lavd device. Maybe function name should be changed to not confuse.
>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.
It was already discussed. I started with something similar, but unfortunately it is not suitable for all cases. You cannot just return list of capabilities because they can interact with each other and they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
The simple flow I see for video output is: pick lavd device. list device names. pick device name start cap query set frame_width/height query codecs set codec query formats set valid format in filterchain sink finish cap queries
And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During
isnt that a purely cosmetical difference?
the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point.
It probably can be met for any true hardware device which is what I am interested in. SDL and OpenGL and the like, to me fall more in the line of applications issues.
true hw devices have complex limitations, for example look at any high speed camera, chances are the 1000fps will be at a significantly lower resolution than lower frame rates. Have you considered that the reason why you dont see complex limitations is not because they dont exist but rather because you dont look at the hw but rather a high level interface on mac/windows? also about format, its quite likely that hw that can do realtime encoding to h264 and mjpeg will support higher resolutions or framerates in the computationally simpler encoder.
Basically the resulting structure would need to be more complex,
Better to have a more complex structure than to have a complex interface to it.
the complex structure would, if it supports all cases probably be quite unwieldy and hard to use. Why i think that, nothing posted came close to a structure that supports all cases and some already where somewhat complex nothing like a single flat structure like you seem to imagine.
Probably leads to less usage of the thing you are spending time on.
Might be good to separate this out some to simplify it. A lot of people are interested in knowing only about capture devices and could care less about things like SDL and OpenGL in ffmpeg.
Could be a simple interface for ennumerating capture devices. Like I said before, you don't really want to walk thru the ennumeration possibly several times for some devices. The ennumeration can cause load and unload of resources and you never know what a device might be initializing. Some do this quickly and some slowly.
gathering the information about the hardware or API and presenting it can be 2 different steps. The 5 calls could easily read from the cached output from the hw or API wraper over the hw.
Solution you suggest is the same I proposed before and was rejected. So I give up any further work on it until you figure out what should it look like.
Maybe a solution is to do both ? have a very simple flat structure that lists limitations but would not be able to repesent complex real hw so for example like these: http://gopro.com/product-comparison-hero3-cameras so it would then possibly list 30fps and 1080p as maximum while the AVOption interface would list that it also can do 4K at 15fps and 960p at 100fps ans wvga at 240fps [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB If a bugfix only changes things apparently unrelated to the bug with no further explanation, that is a good sign that the bugfix is wrong.
On Thu, Feb 06, 2014 at 05:12:14PM +0100, Michael Niedermayer wrote:
On Thu, Feb 06, 2014 at 01:06:03PM +0100, Lukasz Marek wrote:
On 06.02.2014 03:57, Don Moir wrote:
----- Original Message ----- From: "Don Moir" <donmoir@comcast.net> To: "FFmpeg development discussions and patches" <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 7:18 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
----- Original Message ----- From: "Lukasz Marek" <lukasz.m.luki@gmail.com> To: <ffmpeg-devel@ffmpeg.org> Sent: Wednesday, February 05, 2014 6:59 PM Subject: Re: [FFmpeg-devel] [PATCH 2/4] lavd: add device capabilities API
>>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. > >To get dev cap you need context for options for example. In >implementation you need to "open" device to check if configuration is >really working or list properties ranges. For example pulse audio >allows to play on remote server. You need to know that user wants to >test remote server and its done by device options. > >>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. > >Make sure you distinguish device at lavd level (pulseaudio, alsa, oss >for audio and fbdev, xv, opengl, sdl for video) and device names for >each of them (sound outputs, sound cards etc). >This function list the second ones for given lavd device. Maybe >function name should be changed to not confuse. > >>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. > >It was already discussed. I started with something similar, but >unfortunately it is not suitable for all cases. You cannot just return >list of capabilities because they can interact with each other and >they may differ for each device name.
I guess I don't understand how devices interact with each other.
not devices interact with each other, but caps of the device. When you set one param, it may affect others.
Each device I know of have unique names and capabilites. Could be audio and or video. I don't consider Opengl and SDL to be true devices is that is where you are coming from.
I don't know what you mean by "true devices". Yes, opengl nor SDL are not a hardware, but they are "devices" that do that (quote from documentation) "The libavdevice library provides the same interface as libavformat. Namely, an input device is considered like a demuxer, and an output device like a muxer, and the interface and generic device options are the same provided by libavformat (see the ffmpeg-formats manual)."
>The simple flow I see for video output is: >pick lavd device. >list device names. >pick device name >start cap query >set frame_width/height >query codecs >set codec >query formats >set valid format in filterchain sink >finish cap queries > >And I don't think it is too much complicated.
I am developing for windows and then mac. So for windows interested in dshow devices. Currently, I enumerate the devices, names and their capabilites in one step rather than the 10 steps you suggest. During
isnt that a purely cosmetical difference?
the enumeration of the devices I am there so good to get the capabilites in one step. It appears that your steps may cause the dshow code in ffmpeg to go thru the same code multiple times. There is no need to set a frame width / height to query the formats as this is all in the same structure for dshow at least.
You put example of dshow, but I don't want to make interface for supporting dshow, but generic one, for all already implemented and future devs. In many cases it would be possible to return all at once, yes, but it is assumption that can be not met at some point.
It probably can be met for any true hardware device which is what I am interested in. SDL and OpenGL and the like, to me fall more in the line of applications issues.
true hw devices have complex limitations, for example look at any high speed camera, chances are the 1000fps will be at a significantly lower resolution than lower frame rates. Have you considered that the reason why you dont see complex limitations is not because they dont exist but rather because you dont look at the hw but rather a high level interface on mac/windows?
also about format, its quite likely that hw that can do realtime encoding to h264 and mjpeg will support higher resolutions or framerates in the computationally simpler encoder.
Basically the resulting structure would need to be more complex,
Better to have a more complex structure than to have a complex interface to it.
the complex structure would, if it supports all cases probably be quite unwieldy and hard to use. Why i think that, nothing posted came close to a structure that supports all cases and some already where somewhat complex nothing like a single flat structure like you seem to imagine.
Probably leads to less usage of the thing you are spending time on.
Might be good to separate this out some to simplify it. A lot of people are interested in knowing only about capture devices and could care less about things like SDL and OpenGL in ffmpeg.
Could be a simple interface for ennumerating capture devices. Like I said before, you don't really want to walk thru the ennumeration possibly several times for some devices. The ennumeration can cause load and unload of resources and you never know what a device might be initializing. Some do this quickly and some slowly.
gathering the information about the hardware or API and presenting it can be 2 different steps. The 5 calls could easily read from the cached output from the hw or API wraper over the hw.
Solution you suggest is the same I proposed before and was rejected. So I give up any further work on it until you figure out what should it look like.
Maybe a solution is to do both ? have a very simple flat structure that lists limitations but would not be able to repesent complex real hw so for example like these: http://gopro.com/product-comparison-hero3-cameras
so it would then possibly list 30fps and 1080p as maximum
this was supposed to be 60fps
while the AVOption interface would list that it also can do 4K at 15fps and 960p at 100fps ans wvga at 240fps
[...]
-- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
If a bugfix only changes things apparently unrelated to the bug with no further explanation, that is a good sign that the bugfix is wrong.
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
-- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB Awnsering whenever a program halts or runs forever is On a turing machine, in general impossible (turings halting problem). On any real computer, always possible as a real computer has a finite number of states N, and will either halt in less than N cycles or never halt.
Solution you suggest is the same I proposed before and was rejected. So I give up any further work on it until you figure out what should it look like.
Maybe a solution is to do both ? have a very simple flat structure that lists limitations but would not be able to repesent complex real hw so for example like these: http://gopro.com/product-comparison-hero3-cameras
so it would then possibly list 30fps and 1080p as maximum while the AVOption interface would list that it also can do 4K at 15fps and 960p at 100fps ans wvga at 240fps
maybe somehting like that: struct FlatConfigutation { .... //will return lists of parameter ranges that are always valid. } typedef struct AVDeviceInfo { char *device_name; char *device_description; struct *FlastConfiguration; } AVDeviceInfo; typedef struct AVDeviceInfoList { AVDeviceInfo *devices; int nb_devices; int default_device; } AVDeviceInfoList; int avdevice_list_devices( AVFormatContext *s, AVDeviceInfoList **device_list int *have_complex_configuration) have_complex_configuration would inform user that returned flat configuration doesnt cover all posibilities. In cases where have_complex_configuration is not set, AVOption API could be not implemented. I just wonder if it is worth the effort. Making few calls with AVOption API is not really complex. Solid app should always consider the case when have_complex_configuration is set and the AVOption code should be added anyway. -- Best Regards, Lukasz Marek If you can't explain it simply, you don't understand it well enough. - Albert Einstein
On Thu, Feb 06, 2014 at 05:40:52PM +0100, Lukasz Marek wrote:
Solution you suggest is the same I proposed before and was rejected. So I give up any further work on it until you figure out what should it look like.
Maybe a solution is to do both ? have a very simple flat structure that lists limitations but would not be able to repesent complex real hw so for example like these: http://gopro.com/product-comparison-hero3-cameras
so it would then possibly list 30fps and 1080p as maximum while the AVOption interface would list that it also can do 4K at 15fps and 960p at 100fps ans wvga at 240fps
maybe somehting like that:
struct FlatConfigutation { .... //will return lists of parameter ranges that are always valid. }
typedef struct AVDeviceInfo { char *device_name; char *device_description; struct *FlastConfiguration; } AVDeviceInfo;
typedef struct AVDeviceInfoList { AVDeviceInfo *devices; int nb_devices; int default_device; } AVDeviceInfoList;
int avdevice_list_devices( AVFormatContext *s, AVDeviceInfoList **device_list int *have_complex_configuration)
have_complex_configuration would inform user that returned flat configuration doesnt cover all posibilities.
In cases where have_complex_configuration is not set, AVOption API could be not implemented.
I just wonder if it is worth the effort. Making few calls with AVOption API is not really complex. Solid app should always consider the case when have_complex_configuration is set and the AVOption code should be added anyway.
i agree, its up to you, we have 1 user who wants it could be he is the only one, could be there are millions like him i dont know it also i dont insist on any API, if you want to implement another i just would like to have something that can handle the actual limitations of real world hardware. [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB In fact, the RIAA has been known to suggest that students drop out of college or go to community college in order to be able to afford settlements. -- The RIAA
maybe somehting like that:
struct FlatConfigutation { .... //will return lists of parameter ranges that are always valid. }
typedef struct AVDeviceInfo { char *device_name; char *device_description; struct *FlastConfiguration; } AVDeviceInfo;
typedef struct AVDeviceInfoList { AVDeviceInfo *devices; int nb_devices; int default_device; } AVDeviceInfoList;
int avdevice_list_devices( AVFormatContext *s, AVDeviceInfoList **device_list int *have_complex_configuration)
have_complex_configuration would inform user that returned flat configuration doesnt cover all posibilities.
In cases where have_complex_configuration is not set, AVOption API could be not implemented.
I just wonder if it is worth the effort. Making few calls with AVOption API is not really complex. Solid app should always consider the case when have_complex_configuration is set and the AVOption code should be added anyway.
i agree, its up to you, we have 1 user who wants it could be he is the only one, could be there are millions like him i dont know it
also i dont insist on any API, if you want to implement another i just would like to have something that can handle the actual limitations of real world hardware.
I pushed AVOption version updated according to Nicolas' comments. I think the second approach may be useful in some cases, but this one covers all raised issues and I personally prefer to focus on this one. Someone else may add the second option or I can get back to it, but not in near future. -- Best Regards, Lukasz Marek A question that sometimes drives me hazy: am I or are the others crazy? - Albert Einstein
Le nonidi 19 pluviôse, an CCXXII, Lukasz Marek a écrit :
I pushed AVOption version updated according to Nicolas' comments. I think the second approach may be useful in some cases, but this one covers all raised issues and I personally prefer to focus on this one.
Thanks, I will look at it when time permits (hopefully tomorrow).
Someone else may add the second option or I can get back to it, but not in near future.
Unless I am mistaken, the "list everything roughly at once" API can trivially be implemented on top of the fine-grained API. Regards, -- Nicolas George
Le septidi 17 pluviôse, an CCXXII, Don Moir a écrit :
It probably can be met for any true hardware device which is what I am interested in.
For ALSA, it is not possible, since device names can specify options. I do not know whether you consider composite ALSA PCM names "true hardware devices", but this interface must be able to handle them because they are what count for ALSA. By the way, could you remember to trim a little your replies? Scrolling through hundreds of lines of quintuple quotation for just a few lines is annoying. Thanks. Regards, -- Nicolas George
On 2/2/14, Lukasz Marek <lukasz.m.luki@gmail.com> wrote:
Signed-off-by: Lukasz Marek <lukasz.m.luki@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(-)
(Sorry, late comment, I know this was already committed). Might be nice to also add to the cmdutils.c "show_sources" method a usage of this, output to the logger. Nice to have on the command line, but also as as nice example usage. -roger-
--- libavdevice/opengl_enc.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 321 insertions(+), 5 deletions(-) diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c index 9fdaa43..876c371 100644 --- a/libavdevice/opengl_enc.c +++ b/libavdevice/opengl_enc.c @@ -171,6 +171,10 @@ static GLushort g_index[6] = typedef struct OpenGLContext { AVClass *class; ///< class for private options + AVDeviceCapabilities *device_configuration; + enum AVPixelFormat *supported_formats; + int nb_supported_formats; + #if HAVE_SDL SDL_Surface *surface; #endif @@ -402,7 +406,8 @@ static int av_cold opengl_sdl_create_window(AVFormatContext *h) av_log(opengl, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError()); return AVERROR_EXTERNAL; } - if ((ret = opengl_sdl_recreate_window(opengl, opengl->width, opengl->height)) < 0) + if ((ret = opengl_sdl_recreate_window(opengl, opengl->window_width, + opengl->window_height)) < 0) return ret; av_log(opengl, AV_LOG_INFO, "SDL driver: '%s'.\n", SDL_VideoDriverName(buffer, sizeof(buffer))); message.width = opengl->surface->w; @@ -664,8 +669,18 @@ static void opengl_compute_display_area(AVFormatContext *s) { AVRational sar, dar; /* sample and display aspect ratios */ OpenGLContext *opengl = s->priv_data; - AVStream *st = s->streams[0]; - AVCodecContext *encctx = st->codec; + AVStream *st; + AVCodecContext *encctx; + + if (s->nb_streams) { + st = s->streams[0]; + encctx = st->codec; + } else { + //this may happen when capabilities are probed. + opengl->picture_width = opengl->window_width; + opengl->picture_height = opengl->window_height; + return; + } /* compute overlay width and height from the codec context information */ sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 }; @@ -1056,6 +1071,10 @@ static av_cold int opengl_write_header(AVFormatContext *h) opengl->width = st->codec->width; opengl->height = st->codec->height; opengl->pix_fmt = st->codec->pix_fmt; + if (!opengl->window_width) + opengl->window_width = opengl->width; + if (!opengl->window_height) + opengl->window_height = opengl->height; if (!opengl->window_title && !opengl->no_window) opengl->window_title = av_strdup(h->filename); @@ -1212,9 +1231,301 @@ static int opengl_write_packet(AVFormatContext *h, AVPacket *pkt) return opengl_draw(h, pkt, 0); } +static int opengl_is_format_supported(OpenGLContext *opengl, enum AVPixelFormat format) +{ + int i, cnt = opengl->nb_supported_formats; + for (i = 0; i < cnt; i++) { + if (opengl->supported_formats[i] == format) + return 1; + } + return 0; +} + +static int opengl_read_probe_data(AVFormatContext *h) +{ + int ret, i; + OpenGLContext *opengl = h->priv_data; + enum AVPixelFormat working_fmts[FF_ARRAY_ELEMS(opengl_format_desc) - 1]; + + /* check if already probed */ + if (opengl->nb_supported_formats) + return 0; + + if ((ret = opengl_create_window(h)) < 0) + return ret; + if ((ret = opengl_read_limits(opengl)) < 0) + goto fail; + if ((ret = opengl_load_procedures(opengl)) < 0) + goto fail; + + for (i = 0; i < FF_ARRAY_ELEMS(opengl_format_desc) - 1; i++) { + glGetError(); //make sure there is no error before testing format + opengl->pix_fmt = opengl_format_desc[i].fixel_format; + opengl_fill_color_map(opengl); + opengl_get_texture_params(opengl); + if ((ret = opengl_init_context(opengl)) < 0) + goto format_fail; + if ((ret = opengl_prepare_vertex(h)) < 0) + goto format_fail; + working_fmts[opengl->nb_supported_formats++] = opengl->pix_fmt; + opengl_deinit_context(opengl); + continue; + format_fail: + opengl_deinit_context(opengl); + av_log(opengl, AV_LOG_INFO, "Pixel format is not supported on current platform: %s\n", + av_get_pix_fmt_name(opengl->pix_fmt)); + } + + opengl->supported_formats = + av_memdup(working_fmts, opengl->nb_supported_formats * sizeof(enum AVPixelFormat)); + + ret = 0; + fail: + opengl_release_window(h); + return ret; +} + +static void opengl_write_range_int(AVOptionRange *range, int existing, int min, int max) +{ + if (existing > -1) { + range->is_range = 0; + range->value_max = range->value_min = existing; + } else { + range->is_range = (min != max); + range->value_min = min; + range->value_max = max; + } +} + +static int opengl_opts_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags) +{ + AVDeviceCapabilities *configuration = obj; + AVFormatContext *h = configuration->device_context; + OpenGLContext *opengl = h->priv_data; + AVOptionRanges *ranges; + AVOptionRange **range_array; + int i, ret, range_count = 1; + + if ((ret = opengl_read_probe_data(h)) < 0) + return ret; + + if (!strcmp(key, "__format") && configuration->format < 0) + range_count = opengl->nb_supported_formats; + + ranges = av_mallocz(sizeof(*ranges)); + range_array = av_mallocz(range_count * sizeof(void*)); + + if (!ranges || !range_array) { + *ranges_arg = NULL; + av_free(ranges); + av_free(range_array); + return AVERROR(ENOMEM); + } + + ranges->range = range_array; + ranges->nb_ranges = range_count; + + for (i = 0; i < range_count; i++) { + ranges->range[i] = av_mallocz(sizeof(AVOptionRange)); + if (!ranges->range[i]) { + for (i = 0; i < range_count; i++) + av_free(ranges->range[i]); + av_free(ranges); + av_free(range_array); + return AVERROR(ENOMEM); + } + } + + if (!strcmp(key, "__window_width")) + opengl_write_range_int(ranges->range[0], configuration->window_width, + 0, opengl->max_viewport_width); + else if (!strcmp(key, "__window_height")) + opengl_write_range_int(ranges->range[0], configuration->window_height, + 0, opengl->max_viewport_height); + else if (!strcmp(key, "__frame_width")) + opengl_write_range_int(ranges->range[0], configuration->frame_width, + 0, opengl->max_texture_size); + else if (!strcmp(key, "__frame_height")) + opengl_write_range_int(ranges->range[0], configuration->frame_height, + 0, opengl->max_texture_size); + else if (!strcmp(key, "__codec")) + opengl_write_range_int(ranges->range[0], configuration->codec, + AV_CODEC_ID_RAWVIDEO, AV_CODEC_ID_RAWVIDEO); + else if (!strcmp(key, "__fps")) { + if (av_q2d(configuration->fps) < 0) { + av_log(opengl, AV_LOG_VERBOSE, + "OpenGL device cannot determine maximum fps, " + "but it is limited to screen's refresh rate.\n"); + ranges->range[0]->is_range = 1; + ranges->range[0]->value_max = INT_MAX; + ranges->range[0]->value_min = 0; + } else { + ranges->range[0]->is_range = 0; + ranges->range[0]->value_max = ranges->range[0]->value_min = av_q2d(configuration->fps); + } + } else if (!strcmp(key, "__format")) { + if (configuration->format > -1) + opengl_write_range_int(ranges->range[0], configuration->format, + configuration->format, configuration->format); + else { + for (i = 0; i < range_count; i++) + opengl_write_range_int(ranges->range[i], -1, + opengl->supported_formats[i], opengl->supported_formats[i]); + } + } else { + av_free(ranges->range[i]); + av_free(ranges); + av_free(range_array); + return av_opt_query_ranges_default(ranges_arg, obj, key, flags); + } + + *ranges_arg = ranges; + return 0; +} + +const AVClass opengl_options_class = { + .class_name = "opengl options", + .item_name = av_default_item_name, + .option = av_device_capabilities, + .version = LIBAVUTIL_VERSION_INT, + .query_ranges = opengl_opts_query_ranges +}; + +static void opengl_alloc_configuration(OpenGLContext *opengl) +{ + opengl->device_configuration = av_mallocz(sizeof(AVDeviceCapabilities)); + if (opengl->device_configuration) { + opengl->device_configuration->class = &opengl_options_class; + av_opt_set_defaults(opengl->device_configuration); + } +} + +static void* opengl_child_next(void *obj, void *prev) +{ + OpenGLContext *opengl = obj; + if (prev) + return NULL; + if (!opengl->device_configuration) + opengl_alloc_configuration(opengl); + return opengl->device_configuration; +} + +static const AVClass* opengl_child_class_next(const AVClass *prev) +{ + return prev ? NULL : &opengl_options_class; +} + +static int opengl_query_ranges(AVOptionRanges **ranges_arg, void *obj, + const char *key, int flags) +{ + OpenGLContext *opengl = obj; + if (flags & AV_OPT_SEARCH_CHILDREN) { + if (flags & AV_OPT_SEARCH_FAKE_OBJ) + return av_opt_query_ranges_default(ranges_arg, (void *)&opengl_options_class, key, flags); + else { + if (!opengl->device_configuration) + opengl_alloc_configuration(opengl); + return av_opt_query_ranges(ranges_arg, opengl->device_configuration, key, flags); + } + } + return av_opt_query_ranges_default(ranges_arg, obj, key, flags); +} + +static int opengl_get_device_list(struct AVFormatContext *h, void **device_list) +{ + OpenGLContext *opengl = h->priv_data; + AVDeviceInfoList *list; + AVDeviceInfo *dev; + list = av_mallocz(sizeof(AVDeviceInfoList)); + dev = av_mallocz(sizeof(AVDeviceInfo)); + if (!list || !dev) + goto fail; + list->devices = dev; + list->nb_devices = 1; + list->default_device = 0; + if (opengl->no_window) + dev->device_description = av_strdup("OpenGL"); + else + dev->device_description = av_strdup("OpenGL via SDL window"); + if (!dev->device_description) + goto fail; + *device_list = list; + return 0; + fail: + av_free(list); + if (dev) { + av_free((dev->device_description)); + av_free(dev); + } + *device_list = NULL; + return AVERROR(ENOMEM); +} + +static int opengl_apply_configuration(struct AVFormatContext *h, + void **configuration, int strategy) +{ + int abandon = 0; + OpenGLContext *opengl = h->priv_data; + AVDeviceCapabilities *conf = opengl->device_configuration; + + /* restore default context state */ + av_opt_set_defaults(opengl); + + switch ((enum AVDeviceApplyStrategy)strategy) { + case AVDeviceApplyStrategyAbandon: + abandon = 1; + break; + case AVDeviceApplyStrategyAbandonNotValid: + if ((conf->codec > -1 && conf->codec != AV_CODEC_ID_RAWVIDEO) || + (conf->format > -1 && !opengl_is_format_supported(opengl, conf->format)) || + conf->window_width > opengl->max_viewport_width || + conf->window_height > opengl->max_viewport_height || + conf->frame_width > opengl->max_texture_size || + conf->frame_height > opengl->max_texture_size) + abandon = 1; + break; + case AVDeviceApplyFixToTheBestValidValue: + case AVDeviceApplyFixToTheNearestValidValue: + conf->codec = AV_CODEC_ID_RAWVIDEO; + if (conf->format > -1 && !opengl_is_format_supported(opengl, conf->format)) + conf->format = AV_PIX_FMT_RGBA; //TODO: the nearest cannot be constant + conf->window_width = FFMIN(conf->window_width, opengl->max_viewport_width); + conf->window_height = FFMIN(conf->window_height, opengl->max_viewport_height); + conf->frame_width = FFMIN(conf->frame_width, opengl->max_texture_size); + conf->frame_height = FFMIN(conf->frame_height, opengl->max_texture_size); + break; + default: + av_log(opengl, AV_LOG_WARNING, "Not supported strategy\n"); + abandon = 1; + break; + } + + if (!abandon) { + //TODO: At this moment window_width/height is ignored anyway. + // It requires addiotional control message to send dimensions to app. + // SDL based version can be fixed right now. + /* opengl can only store window size as configuration. + format and codec is up to application to provide proper one. */ + if (conf->window_width > -1) + opengl->window_width = conf->window_width; + if (conf->window_height > -1) + opengl->window_height = conf->window_height; + if (configuration) { + *configuration = conf; + opengl->device_configuration = NULL; + } + } else if (configuration) + *configuration = NULL; + + av_freep(&opengl->device_configuration); + av_freep(&opengl->supported_formats); + opengl->nb_supported_formats = 0; + return 0; +} + #define OFFSET(x) offsetof(OpenGLContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM -static const AVOption options[] = { +static const AVOption opengl_options[] = { { "background", "set background color", OFFSET(background), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, ENC }, { "no_window", "disable default window", OFFSET(no_window), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, ENC }, { "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, ENC }, @@ -1224,8 +1535,11 @@ static const AVOption options[] = { static const AVClass opengl_class = { .class_name = "opengl outdev", .item_name = av_default_item_name, - .option = options, + .option = opengl_options, .version = LIBAVUTIL_VERSION_INT, + .child_next = opengl_child_next, + .child_class_next = opengl_child_class_next, + .query_ranges = opengl_query_ranges }; AVOutputFormat ff_opengl_muxer = { @@ -1238,6 +1552,8 @@ AVOutputFormat ff_opengl_muxer = { .write_packet = opengl_write_packet, .write_trailer = opengl_write_trailer, .control_message = opengl_control_message, + .get_device_list = opengl_get_device_list, + .apply_configuration = opengl_apply_configuration, .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, .priv_class = &opengl_class, }; -- 1.8.3.2
I've found some bugs in opengl_opts_query_ranges, so new version attached.
Just a testing tool. Do not merge. --- doc/examples/Makefile | 1 + doc/examples/opengl_device_settings.c | 176 ++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 doc/examples/opengl_device_settings.c diff --git a/doc/examples/Makefile b/doc/examples/Makefile index f4f6c70..73b07da 100644 --- a/doc/examples/Makefile +++ b/doc/examples/Makefile @@ -22,6 +22,7 @@ EXAMPLES= avio_reading \ resampling_audio \ scaling_video \ transcode_aac \ + opengl_device_settings \ OBJS=$(addsuffix .o,$(EXAMPLES)) diff --git a/doc/examples/opengl_device_settings.c b/doc/examples/opengl_device_settings.c new file mode 100644 index 0000000..3debcc5 --- /dev/null +++ b/doc/examples/opengl_device_settings.c @@ -0,0 +1,176 @@ + +#include <SDL/SDL.h> +#include <libavutil/log.h> +#include <libavutil/pixdesc.h> +#include <libavutil/opt.h> +#include <libavformat/avformat.h> +#include <libavdevice/avdevice.h> + +SDL_Surface *g_surface = NULL; + +static int av_cold create_sdl_window(int width, int height) +{ + if (SDL_Init(SDL_INIT_VIDEO)) { + av_log(NULL, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError()); + return AVERROR_EXTERNAL; + } + g_surface = SDL_SetVideoMode(width, height, 32, SDL_OPENGL); + if (!g_surface) { + av_log(NULL, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError()); + return AVERROR_EXTERNAL; + } + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + return 0; +} + +static int message_from_device_handler(struct AVFormatContext *s, int type, + void *data, size_t data_size) +{ + switch ((enum AVDevToAppMessageType) type) { + case AV_DEV_TO_APP_CREATE_WINDOW_BUFFER: + if (!g_surface) + return create_sdl_window(1, 1); + return 0; + case AV_DEV_TO_APP_DESTROY_WINDOW_BUFFER: + SDL_Quit(); + g_surface = NULL; + return 0; + case AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER: + //SDL 1.2 doesn't required it (no such API), + //but you need to make OpenGL context current here. + return 0; + case AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER: + SDL_GL_SwapBuffers(); + return 0; + default: + break; + } + return AVERROR(ENOSYS); +} + +static int print_ranges(AVFormatContext *oc, enum AVDeviceCapability cap, + const char *cap_name) +{ + int i; + AVOptionRanges *ranges; + AVOptionRange *range; + + if ((i = avdevice_get_device_capability(oc, cap, &ranges)) < 0) { + av_log(oc, AV_LOG_ERROR, "Cannot query range of %s\n", cap_name); + return i; + } + av_log(oc, AV_LOG_INFO, "%-14s: allowed values: ", cap_name); + for(i = 0; i < ranges->nb_ranges; i++) { + range = ranges->range[i]; + if (i) + av_log(oc, AV_LOG_INFO, ", "); + if (range->is_range) + if (cap == AV_DEV_CAP_FPS) + av_log(oc, AV_LOG_INFO, "%.5f - %.5f", range->value_min, range->value_max); + else + av_log(oc, AV_LOG_INFO, "%d - %d", (int)range->value_min, (int)range->value_max); + else { + if (cap == AV_DEV_CAP_FPS) + av_log(oc, AV_LOG_INFO, "%.5f", range->value_min); + if (cap == AV_DEV_CAP_FORMAT) + av_log(oc, AV_LOG_INFO, "%s", av_get_pix_fmt_name((int)range->value_min)); + else if (cap == AV_DEV_CAP_CODEC_ID) + av_log(oc, AV_LOG_INFO, "%s", avcodec_get_name((int)range->value_min)); + else + av_log(oc, AV_LOG_INFO, "%d", (int)range->value_min); + } + } + av_log(oc, AV_LOG_INFO, "\n"); + av_opt_freep_ranges(&ranges); + return 0; +} + +void print_all(AVFormatContext *oc) +{ + print_ranges(oc, AV_DEV_CAP_WINDOW_WIDTH, "window width"); + print_ranges(oc, AV_DEV_CAP_WINDOW_HEIGHT, "window height"); + print_ranges(oc, AV_DEV_CAP_FRAME_WIDTH, "frame width"); + print_ranges(oc, AV_DEV_CAP_FRAME_HEIGHT, "frame height"); + print_ranges(oc, AV_DEV_CAP_FPS, "fps"); + print_ranges(oc, AV_DEV_CAP_CODEC_ID, "codec"); + print_ranges(oc, AV_DEV_CAP_FORMAT, "format"); +} + +int set_video_configuration(AVFormatContext *oc) +{ + int ret; + AVDictionary *opts = NULL; + AVDeviceCapabilities *spec; + + av_dict_set(&opts, "no_window", "1", 0); + ret = avdevice_init_device_capabilities(oc, &opts); + av_dict_free(&opts); + if (ret < 0) { + if (ret == AVERROR(ENOSYS)) + av_log(oc, AV_LOG_ERROR, "Device doesn't provide this API.\n"); + else + av_log(oc, AV_LOG_ERROR, "Error occurred.\n"); + return ret; + } + + av_log(oc, AV_LOG_INFO, "Parameter ranges accepted by OpenGL device are:\n"); + print_all(oc); + + avdevice_set_device_capability_int(oc, AV_DEV_CAP_WINDOW_WIDTH, 256); + avdevice_set_device_capability_int(oc, AV_DEV_CAP_WINDOW_HEIGHT, 256); + avdevice_set_device_capability_int(oc, AV_DEV_CAP_FRAME_WIDTH, 256); + avdevice_set_device_capability_int(oc, AV_DEV_CAP_FRAME_HEIGHT, 256); + //Set codec to raw video + avdevice_set_device_capability_int(oc, AV_DEV_CAP_CODEC_ID, AV_CODEC_ID_RAWVIDEO); + //60 frames per second, fps is ignored by OpenGL device, but it can be set + avdevice_set_device_capability_q(oc, AV_DEV_CAP_FPS, (AVRational){60, 1}); + //set RGB24 format: each component is 1 byte long and no alpha + avdevice_set_device_capability_int(oc, AV_DEV_CAP_FORMAT, AV_PIX_FMT_RGB24); + + av_log(oc, AV_LOG_INFO, "\n"); + av_log(oc, AV_LOG_INFO, "Current OpenGL device configuration:\n"); + print_all(oc); + + avdevice_finish_device_capabilities(oc, &spec, AVDeviceApplyStrategyAbandonNotValid); + if (!spec) { + av_log(oc, AV_LOG_ERROR, "Provided configuration is wrong!\n"); + return -1; + } else + av_log(oc, AV_LOG_INFO, "Configuration accepted!\n"); + + avdevice_free_device_capabilities(&spec); + + return 0; +} + +int main(int argc, char **argv) +{ + int ret; + AVFormatContext *oc; + + //av_log_set_level(AV_LOG_DEBUG); + + av_register_all(); + avdevice_register_all(); + + /* allocate the output media context */ + ret = avformat_alloc_output_context2(&oc, NULL, "opengl", NULL); + if (ret < 0 || !oc) { + av_log(NULL, AV_LOG_ERROR, "Could not allocate output format context.\n"); + return 1; + } + + av_format_set_control_message_cb(oc, message_from_device_handler); + + set_video_configuration(oc); + + av_log(oc, AV_LOG_INFO, "To be continued...\n"); + + avformat_free_context(oc); + + return 0; +} -- 1.8.3.2
On Mon, Feb 03, 2014 at 01:02:47AM +0100, Lukasz Marek wrote:
This is probing API implementation which started from thread http://ffmpeg.org/pipermail/ffmpeg-devel/2014-January/153614.html
It uses AVOptions API with some kind of abstraction layer. I wonder if adding pointer option safe?
its safe to add, a function thats not called/referenced by anything cant do harm but we maybe should be carefull about through what means it can be used generic "set option from string" code should probably not accept pointers [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB The real ebay dictionary, page 3 "Rare item" - "Common item with rare defect or maybe just a lie" "Professional" - "'Toy' made in china, not functional except as doorstop" "Experts will know" - "The seller hopes you are not an expert"
participants (6)
-
Don Moir -
Lukasz M -
Lukasz Marek -
Michael Niedermayer -
Nicolas George -
Roger Pack