[FFmpeg-devel] [PATCH 3/4] lavd/opengl_enc: implement query capabilities API
Lukasz Marek
lukasz.m.luki at gmail.com
Fri Feb 7 14:35:02 CET 2014
---
libavdevice/opengl_enc.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 294 insertions(+), 6 deletions(-)
diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c
index 9fdaa43..1099aee 100644
--- a/libavdevice/opengl_enc.c
+++ b/libavdevice/opengl_enc.c
@@ -171,6 +171,9 @@ static GLushort g_index[6] =
typedef struct OpenGLContext {
AVClass *class; ///< class for private options
+ enum AVPixelFormat *supported_formats;
+ int nb_supported_formats;
+
#if HAVE_SDL
SDL_Surface *surface;
#endif
@@ -402,7 +405,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 +668,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 +1070,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 +1230,275 @@ 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 int opengl_is_configuration_valid(OpenGLContext *opengl,
+ AVDeviceCapabilitiesQuery *conf)
+{
+ return ((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);
+}
+
+#define WRITE_RANGE(index, is_set, existing, min, max) \
+{ \
+ AVOptionRange *range = av_mallocz(sizeof(AVOptionRange)); \
+ if (!range) \
+ goto fail; \
+ ranges->range[index] = range; \
+ if (is_set) { \
+ range->is_range = 0; \
+ range->value_max = range->value_min = (existing); \
+ } else { \
+ if (opengl_is_configuration_valid(opengl, configuration)) { \
+ range->is_range = ((min) != (max)); \
+ range->value_min = (min); \
+ range->value_max = (max); \
+ } else { \
+ av_freep(&ranges->range[index]); \
+ ranges->nb_ranges = 0; \
+ } \
+ } \
+}
+
+static int opengl_opts_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)
+{
+ AVDeviceCapabilitiesQuery *configuration = obj;
+ AVFormatContext *h = configuration->device_context;
+ OpenGLContext *opengl = h->priv_data;
+ AVOptionRanges *ranges;
+ AVOptionRange **range_array;
+ int i, range_count = 1;
+
+ if (!strcmp(key, "devcap_format") && (configuration->format == -1))
+ range_count = opengl->nb_supported_formats;
+
+ ranges = av_mallocz(sizeof(AVOptionRanges));
+ range_array = av_mallocz(range_count * sizeof(AVOptionRange *));
+
+ if (!ranges || !range_array)
+ goto fail;
+
+ ranges->range = range_array;
+ ranges->nb_ranges = range_count;
+
+ if (!strcmp(key, "devcap_window_width"))
+ WRITE_RANGE(0, configuration->window_width > -1,
+ configuration->window_width, 0, opengl->max_viewport_width)
+ else if (!strcmp(key, "devcap_window_height"))
+ WRITE_RANGE(0, configuration->window_height > -1,
+ configuration->window_height, 0, opengl->max_viewport_height)
+ else if (!strcmp(key, "devcap_frame_width"))
+ WRITE_RANGE(0, configuration->frame_width > -1,
+ configuration->frame_width, 0, opengl->max_texture_size)
+ else if (!strcmp(key, "devcap_frame_height"))
+ WRITE_RANGE(0, configuration->frame_height > -1,
+ configuration->frame_height, 0, opengl->max_texture_size)
+ else if (!strcmp(key, "devcap_codec"))
+ WRITE_RANGE(0, configuration->codec > -1,
+ configuration->codec, AV_CODEC_ID_RAWVIDEO, AV_CODEC_ID_RAWVIDEO)
+ else if (!strcmp(key, "devcap_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");
+ WRITE_RANGE(0, av_q2d(configuration->fps) >= 0.0, av_q2d(configuration->fps), 0, INT_MAX)
+
+ } else if (!strcmp(key, "devcap_format")) {
+ if (configuration->format > -1) {
+ WRITE_RANGE(0, 1, configuration->format, 0, 0)
+ } else if (opengl_is_configuration_valid(opengl, configuration)) {
+ for (i = 0; i < range_count; i++)
+ WRITE_RANGE(i, 1, opengl->supported_formats[i], 0, 0)
+ } else
+ ranges->nb_ranges = 0;
+ } else {
+ av_log(opengl, AV_LOG_WARNING, "Unknown capability %s.\n", key);
+ av_free(ranges);
+ av_free(range_array);
+ return av_opt_query_ranges_default(ranges_arg, obj, key, flags);
+ }
+
+ *ranges_arg = ranges;
+ return 0;
+ fail:
+ av_log(opengl, AV_LOG_ERROR, "Cannot allocate\n");
+ *ranges_arg = NULL;
+ if (range_array)
+ for (i = 0; i < range_count; i++)
+ av_free(range_array[i]);
+ av_free(ranges);
+ av_free(range_array);
+ return AVERROR(ENOMEM);
+}
+
+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 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_create_device_capabilities(struct AVFormatContext *h,
+ void *data)
+{
+ AVDeviceCapabilitiesQuery *caps = data;
+ int ret;
+
+ caps->class = &opengl_options_class;
+ if ((ret = opengl_read_probe_data(h)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int opengl_apply_device_capabilities(struct AVFormatContext *h, void *data,
+ int strategy)
+{
+ int apply = 0;
+ AVDeviceCapabilitiesQuery *caps = data;
+ OpenGLContext *opengl = h->priv_data;
+
+ switch ((enum AVDeviceSettingsApplyStrategy)strategy) {
+ case AVDeviceApplyStrategyApplyValid:
+ if (opengl_is_configuration_valid(opengl, caps))
+ apply = 1;
+ break;
+ case AVDeviceApplyStrategyApplyFixToTheNearest:
+ case AVDeviceApplyStrategyApplyFixToTheBest:
+ apply = 1;
+ case AVDeviceApplyStrategyFixToTheNearest:
+ case AVDeviceApplyStrategyFixToTheBest:
+ caps->codec = AV_CODEC_ID_RAWVIDEO;
+ if (caps->format > -1 && !opengl_is_format_supported(opengl, caps->format))
+ caps->format = AV_PIX_FMT_RGBA; //TODO: the nearest cannot be constant
+ caps->window_width = FFMIN(caps->window_width, opengl->max_viewport_width);
+ caps->window_height = FFMIN(caps->window_height, opengl->max_viewport_height);
+ caps->frame_width = FFMIN(caps->frame_width, opengl->max_texture_size);
+ caps->frame_height = FFMIN(caps->frame_height, opengl->max_texture_size);
+ break;
+ default:
+ av_log(opengl, AV_LOG_WARNING, "Not supported strategy\n");
+ apply = 0;
+ break;
+ }
+
+ if (apply) {
+ //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 (caps->window_width > -1)
+ opengl->window_width = caps->window_width;
+ if (caps->window_height > -1)
+ opengl->window_height = caps->window_height;
+ }
+ return apply;
+}
+
+static int opengl_free_device_capabilities(struct AVFormatContext *h, void *data)
+{
+ AVDeviceCapabilitiesQuery *caps = data;
+ OpenGLContext *opengl = h->priv_data;
+
+ /* restore default context state */
+ av_opt_set_defaults(opengl);
+
+ av_freep(&opengl->supported_formats);
+ opengl->nb_supported_formats = 0;
+ caps->class = NULL;
+ 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 +1508,8 @@ static const AVOption options[] = {
static const AVClass opengl_class = {
.class_name = "opengl outdev",
.item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
+ .option = opengl_options,
+ .version = LIBAVUTIL_VERSION_INT
};
AVOutputFormat ff_opengl_muxer = {
@@ -1238,6 +1522,10 @@ 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,
+ .create_device_capabilities = opengl_create_device_capabilities,
+ .apply_device_capabilities = opengl_apply_device_capabilities,
+ .free_device_capabilities = opengl_free_device_capabilities,
.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
.priv_class = &opengl_class,
};
--
1.8.3.2
More information about the ffmpeg-devel
mailing list