[FFmpeg-devel] [PATCH] v4l2: allow to set device controls.

Nicolas George nicolas.george at normalesup.org
Thu Apr 25 17:45:48 CEST 2013


Address trac ticket #2305.

Somme trickery is necessary to support gcc < 4.6
(used in Debian stable at the time of this commit)
and older versions of the v4l2 API without proper
version macros.

Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 doc/indevs.texi    |    4 ++
 libavdevice/v4l2.c |  142 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 146 insertions(+)

diff --git a/doc/indevs.texi b/doc/indevs.texi
index 5e8d215..2a51a73 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -699,6 +699,10 @@ Force conversion from monotonic to absolute timestamps.
 @end table
 
 Default value is @code{default}.
+
+ at item controls
+Set device controls. The argument is a list of @code{@var{key}=@var{value}}
+pairs separated by @code{:}.
 @end table
 
 @section vfwcap
diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c
index 34e3d9c..553ea79 100644
--- a/libavdevice/v4l2.c
+++ b/libavdevice/v4l2.c
@@ -121,6 +121,7 @@ struct video_data {
     int list_format;    /**< Set by a private option. */
     int list_standard;  /**< Set by a private option. */
     char *framerate;    /**< Set by a private option. */
+    char *controls;     /**< Set by a private option. */
 };
 
 struct buff_data {
@@ -707,6 +708,141 @@ static void mmap_close(struct video_data *s)
     av_free(s->buf_len);
 }
 
+static int add_control(struct v4l2_queryctrl **allctrls, unsigned *nb_ctrls,
+                       struct v4l2_queryctrl *qctrl)
+{
+    char *name, *cur, *var;
+
+    (*nb_ctrls)++;
+    if (!(*nb_ctrls & (*nb_ctrls - 1))) {
+        *allctrls = av_realloc_f(*allctrls, sizeof(**allctrls),
+                                 (*nb_ctrls << 1) - 1);
+        if (!*allctrls)
+            return AVERROR(ENOMEM);
+    }
+    (*allctrls)[*nb_ctrls - 1] = *qctrl;
+    qctrl = &(*allctrls)[*nb_ctrls - 1];
+
+    /* "Control Name" -> "control_name", similar to name2var from v4l-utils */
+    for (name = cur = var = qctrl->name; *cur; cur++) {
+        unsigned c = *cur;
+        if ((c | 0x20) - 'a' < 26 || c - '0' < 10) {
+            *(var++) = av_tolower(*cur);
+        } else {
+            if (var > name && var[-1] != '_')
+                *(var++) = '_';
+        }
+    }
+    while (var > name && var[-1] == '_')
+        var--;
+    *var = 0;
+
+    return 0;
+}
+
+static int set_controls(AVFormatContext *s1)
+{
+    struct video_data *s = s1->priv_data;
+    struct v4l2_queryctrl qctrl, *allctrls = NULL;
+    struct v4l2_ext_control extctrl;
+    struct v4l2_ext_controls extctrls;
+    unsigned nb_ctrls = 0, i;
+    const char *opt = s->controls;
+    char *ctrl, *val, *tail;
+    long long vall;
+    int ret;
+
+    memset(&qctrl, 0, sizeof(qctrl));
+    qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
+    while (1) {
+        if (v4l2_ioctl(s->fd, VIDIOC_QUERYCTRL, &qctrl) < 0)
+            break;
+        if ((ret = add_control(&allctrls, &nb_ctrls, &qctrl)) < 0)
+            goto fail;
+        qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+    }
+
+    while (*opt) {
+        ret = av_opt_get_key_value(&opt, "=", ":", 0, &ctrl, &val);
+        if (ret < 0) {
+            av_log(s1, AV_LOG_ERROR, "Error in controls string near '%s': %s\n",
+                   opt, av_err2str(ret));
+            goto fail;
+        }
+        for (i = 0; i < nb_ctrls; i++)
+            if (!strcmp(ctrl, allctrls[i].name))
+                break;
+        if (i >= nb_ctrls) {
+            av_log(s1, AV_LOG_ERROR, "Unknown control '%s'\n", ctrl);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+        av_log(s1, AV_LOG_VERBOSE, "Setting control %s to '%s'\n", ctrl, val);
+        memset(&extctrl, 0, sizeof(extctrl));
+        extctrl.id = allctrls[i].id;
+        switch (allctrls[i].type) {
+        case V4L2_CTRL_TYPE_INTEGER:
+        case V4L2_CTRL_TYPE_BOOLEAN:
+        case V4L2_CTRL_TYPE_MENU:
+        case V4L2_CTRL_TYPE_BUTTON:
+#ifdef V4L2_CTRL_CLASS_FLASH /* hack V4L2_CTRL_TYPE_BITMASK is an enum,
+                                V4L2_CTRL_CLASS_FLASH was added just after */
+        case V4L2_CTRL_TYPE_BITMASK:
+#endif
+        case V4L2_CTRL_TYPE_INTEGER64:
+            vall = strtoll(val, &tail, 0);
+            if (*tail) {
+                av_log(s1, AV_LOG_ERROR, "Invalid numeric value '%s'\n", val);
+                ret = AVERROR(EINVAL);
+                goto fail;
+            }
+            if (allctrls[i].type == V4L2_CTRL_TYPE_INTEGER64)
+#if AV_GCC_VERSION_AT_LEAST(4,6)
+                extctrl.value64 = vall;
+#else
+                /* gcc < 4.6 does not support unnamed structures declared in
+                   headers in c99 mode, access the field hackishly, hoping
+                   the layout does not change soon */
+                *(__s64 *)(&extctrl.reserved2 + 1) = vall;
+#endif
+            else
+#if AV_GCC_VERSION_AT_LEAST(4,6)
+                extctrl.value   = vall;
+#else
+                *(__s32 *)(&extctrl.reserved2 + 1) = vall;
+#endif
+            break;
+        case V4L2_CTRL_TYPE_STRING:
+            extctrl.size = strlen(val) + 1;
+            extctrl.string = val;
+            break;
+        default:
+            av_log(s1, AV_LOG_ERROR, "Unknown type for control '%s'\n", ctrl);
+            ret = AVERROR(EIO);
+            goto fail;
+        }
+        memset(&extctrls, 0, sizeof(extctrls));
+        extctrls.ctrl_class = 0;
+        extctrls.count      = 1;
+        extctrls.controls   = &extctrl;
+        if (v4l2_ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &extctrls) < 0) {
+            ret = AVERROR(errno);
+            av_log(s1, AV_LOG_ERROR, "Error setting control %s to '%s': %s\n",
+                   ctrl, val, av_err2str(ret));
+            goto fail;
+        }
+        av_freep(&ctrl);
+        av_freep(&val);
+        opt += !!*opt;
+    }
+
+fail:
+    av_freep(&ctrl);
+    av_freep(&val);
+    av_freep(&allctrls);
+    return ret;
+}
+
 static int v4l2_set_parameters(AVFormatContext *s1)
 {
     struct video_data *s = s1->priv_data;
@@ -813,6 +949,10 @@ static int v4l2_set_parameters(AVFormatContext *s1)
     s1->streams[0]->avg_frame_rate.den = tpf->numerator;
     s1->streams[0]->r_frame_rate = s1->streams[0]->avg_frame_rate;
 
+    if (s->controls)
+        if ((ret = set_controls(s1)) < 0)
+            return ret;
+
     return 0;
 }
 
@@ -1072,6 +1212,8 @@ static const AVOption options[] = {
     { "abs",          "use absolute timestamps (wall clock)",                     OFFSET(ts_mode),      AV_OPT_TYPE_CONST,  {.i64 = V4L_TS_ABS      }, 0, 2, DEC, "timestamps" },
     { "mono2abs",     "force conversion from monotonic to absolute timestamps",   OFFSET(ts_mode),      AV_OPT_TYPE_CONST,  {.i64 = V4L_TS_MONO2ABS }, 0, 2, DEC, "timestamps" },
 
+    { "controls",     "set device controls",                                      OFFSET(controls),     AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC },
+
     { NULL },
 };
 
-- 
1.7.10.4



More information about the ffmpeg-devel mailing list