[FFmpeg-devel] [PATCH 3/4] lavd/opengl_enc: implement query capabilities API

Lukasz Marek lukasz.m.luki at gmail.com
Mon Feb 3 01:02:50 CET 2014


---
 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



More information about the ffmpeg-devel mailing list