]> git.sesse.net Git - ffmpeg/blobdiff - libavdevice/v4l2.c
Merge commit 'b9ba5253dd1232be4b48cfe61c31ff4b3de3d10a'
[ffmpeg] / libavdevice / v4l2.c
index 4ac69677cd41d87be3ce5625b8e8dcd7e5a4ff44..2e4b3955059d2087945249130b054b6965cfa141 100644 (file)
@@ -112,9 +112,11 @@ struct video_data {
     void **buf_start;
     unsigned int *buf_len;
     char *standard;
+    v4l2_std_id std_id;
     int channel;
     char *pixel_format; /**< Set by a private option. */
     int list_format;    /**< Set by a private option. */
+    int list_standard;  /**< Set by a private option. */
     char *framerate;    /**< Set by a private option. */
 };
 
@@ -359,7 +361,7 @@ static void list_formats(AVFormatContext *ctx, int fd, int type)
                    vfd.description);
         } else if (vfd.flags & V4L2_FMT_FLAG_COMPRESSED &&
                    type & V4L_COMPFORMATS) {
-            AVCodec *codec = avcodec_find_encoder(codec_id);
+            AVCodec *codec = avcodec_find_decoder(codec_id);
             av_log(ctx, AV_LOG_INFO, "Compressed: %9s : %20s :",
                    codec ? codec->name : "Unsupported",
                    vfd.description);
@@ -380,6 +382,30 @@ static void list_formats(AVFormatContext *ctx, int fd, int type)
     }
 }
 
+static void list_standards(AVFormatContext *ctx)
+{
+    int ret;
+    struct video_data *s = ctx->priv_data;
+    struct v4l2_standard standard;
+
+    if (s->std_id == 0)
+        return;
+
+    for (standard.index = 0; ; standard.index++) {
+        ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
+        if (ret < 0) {
+            if (errno == EINVAL)
+                break;
+            else {
+                av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_ENUMSTD): %s\n", strerror(errno));
+                return;
+            }
+        }
+        av_log(ctx, AV_LOG_INFO, "%2d, %16llx, %s\n",
+               standard.index, standard.id, standard.name);
+    }
+}
+
 static int mmap_init(AVFormatContext *ctx)
 {
     int i, res;
@@ -655,15 +681,12 @@ static void mmap_close(struct video_data *s)
 static int v4l2_set_parameters(AVFormatContext *s1)
 {
     struct video_data *s = s1->priv_data;
-    struct v4l2_input input = { 0 };
     struct v4l2_standard standard = { 0 };
     struct v4l2_streamparm streamparm = { 0 };
-    struct v4l2_fract *tpf = &streamparm.parm.capture.timeperframe;
+    struct v4l2_fract *tpf;
     AVRational framerate_q = { 0 };
     int i, ret;
 
-    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
     if (s->framerate &&
         (ret = av_parse_video_rate(&framerate_q, s->framerate)) < 0) {
         av_log(s1, AV_LOG_ERROR, "Could not parse framerate '%s'.\n",
@@ -671,74 +694,88 @@ static int v4l2_set_parameters(AVFormatContext *s1)
         return ret;
     }
 
-    /* set tv video input */
-    input.index = s->channel;
-    if (v4l2_ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) {
-        av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl enum input failed:\n");
-        return AVERROR(EIO);
-    }
+    if (s->standard) {
+        if (s->std_id) {
+            av_log(s1, AV_LOG_DEBUG, "Setting standard: %s\n", s->standard);
+            /* set tv standard */
+            for (i = 0; ; i++) {
+                standard.index = i;
+                ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
+                if (ret < 0 || !av_strcasecmp(standard.name, s->standard))
+                    break;
+            }
+            if (ret < 0) {
+                ret = errno;
+                av_log(s1, AV_LOG_ERROR, "Unknown or unsupported standard '%s'\n", s->standard);
+                return AVERROR(ret);
+            }
 
-    av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set input_id: %d, input: %s\n",
-            s->channel, input.name);
-    if (v4l2_ioctl(s->fd, VIDIOC_S_INPUT, &input.index) < 0) {
-        av_log(s1, AV_LOG_ERROR,
-               "The V4L2 driver ioctl set input(%d) failed\n",
-                s->channel);
-        return AVERROR(EIO);
+            if (v4l2_ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
+                ret = errno;
+                av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_S_STD): %s\n", strerror(errno));
+                return AVERROR(ret);
+            }
+        } else {
+            av_log(s1, AV_LOG_WARNING,
+                   "This device does not support any standard\n");
+        }
     }
 
-    if (s->standard) {
-        av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set standard: %s\n",
-               s->standard);
-        /* set tv standard */
-        for(i=0;;i++) {
+    /* get standard */
+    if (v4l2_ioctl(s->fd, VIDIOC_G_STD, &s->std_id) == 0) {
+        tpf = &standard.frameperiod;
+        for (i = 0; ; i++) {
             standard.index = i;
             ret = v4l2_ioctl(s->fd, VIDIOC_ENUMSTD, &standard);
-            if (ret < 0 || !av_strcasecmp(standard.name, s->standard))
+            if (ret < 0) {
+                ret = errno;
+                av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_ENUMSTD): %s\n", strerror(errno));
+                return AVERROR(ret);
+            }
+            if (standard.id == s->std_id) {
+                av_log(s1, AV_LOG_DEBUG,
+                       "Current standard: %s, id: %"PRIu64", frameperiod: %d/%d\n",
+                       standard.name, (uint64_t)standard.id, tpf->numerator, tpf->denominator);
                 break;
+            }
         }
-        if (ret < 0) {
-            av_log(s1, AV_LOG_ERROR, "Unknown standard '%s'\n", s->standard);
-            return ret;
-        }
+    } else {
+        tpf = &streamparm.parm.capture.timeperframe;
+    }
 
-        av_log(s1, AV_LOG_DEBUG,
-               "The V4L2 driver set standard: %s, id: %"PRIu64"\n",
-               s->standard, (uint64_t)standard.id);
-        if (v4l2_ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) {
-            av_log(s1, AV_LOG_ERROR,
-                   "The V4L2 driver ioctl set standard(%s) failed\n",
-                   s->standard);
-            return AVERROR(EIO);
-        }
+    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    if (v4l2_ioctl(s->fd, VIDIOC_G_PARM, &streamparm) < 0) {
+        ret = errno;
+        av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n", strerror(errno));
+        return AVERROR(ret);
     }
 
     if (framerate_q.num && framerate_q.den) {
-        av_log(s1, AV_LOG_DEBUG, "Setting time per frame to %d/%d\n",
-               framerate_q.den, framerate_q.num);
-        tpf->numerator   = framerate_q.den;
-        tpf->denominator = framerate_q.num;
-
-        if (v4l2_ioctl(s->fd, VIDIOC_S_PARM, &streamparm) != 0) {
-            av_log(s1, AV_LOG_ERROR,
-                   "ioctl set time per frame(%d/%d) failed\n",
+        if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
+            tpf = &streamparm.parm.capture.timeperframe;
+
+            av_log(s1, AV_LOG_DEBUG, "Setting time per frame to %d/%d\n",
                    framerate_q.den, framerate_q.num);
-            return AVERROR(EIO);
-        }
+            tpf->numerator   = framerate_q.den;
+            tpf->denominator = framerate_q.num;
 
-        if (framerate_q.num != tpf->denominator ||
-            framerate_q.den != tpf->numerator) {
-            av_log(s1, AV_LOG_INFO,
-                   "The driver changed the time per frame from "
-                   "%d/%d to %d/%d\n",
-                   framerate_q.den, framerate_q.num,
-                   tpf->numerator, tpf->denominator);
-        }
-    } else {
-        if (v4l2_ioctl(s->fd, VIDIOC_G_PARM, &streamparm) != 0) {
-            av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n",
-                   strerror(errno));
-            return AVERROR(errno);
+            if (v4l2_ioctl(s->fd, VIDIOC_S_PARM, &streamparm) < 0) {
+                ret = errno;
+                av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_S_PARM): %s\n", strerror(errno));
+                return AVERROR(ret);
+            }
+
+            if (framerate_q.num != tpf->denominator ||
+                framerate_q.den != tpf->numerator) {
+                av_log(s1, AV_LOG_INFO,
+                       "The driver changed the time per frame from "
+                       "%d/%d to %d/%d\n",
+                       framerate_q.den, framerate_q.num,
+                       tpf->numerator, tpf->denominator);
+            }
+        } else {
+            av_log(s1, AV_LOG_WARNING,
+                   "The driver does not allow to change time per frame\n");
         }
     }
     s1->streams[0]->avg_frame_rate.num = tpf->denominator;
@@ -747,19 +784,27 @@ static int v4l2_set_parameters(AVFormatContext *s1)
     return 0;
 }
 
-static uint32_t device_try_init(AVFormatContext *s1,
-                                enum AVPixelFormat pix_fmt,
-                                int *width,
-                                int *height,
-                                enum AVCodecID *codec_id)
+static int device_try_init(AVFormatContext *s1,
+                           enum AVPixelFormat pix_fmt,
+                           int *width,
+                           int *height,
+                           uint32_t *desired_format,
+                           enum AVCodecID *codec_id)
 {
-    uint32_t desired_format = fmt_ff2v4l(pix_fmt, s1->video_codec_id);
+    int ret, i;
+
+    *desired_format = fmt_ff2v4l(pix_fmt, s1->video_codec_id);
 
-    if (desired_format == 0 ||
-        device_init(s1, width, height, desired_format) < 0) {
-        int i;
+    if (*desired_format) {
+        ret = device_init(s1, width, height, *desired_format);
+        if (ret < 0) {
+            *desired_format = 0;
+            if (ret != AVERROR(EINVAL))
+                return ret;
+        }
+    }
 
-        desired_format = 0;
+    if (!*desired_format) {
         for (i = 0; i<FF_ARRAY_ELEMS(fmt_conversion_table); i++) {
             if (s1->video_codec_id == AV_CODEC_ID_NONE ||
                 fmt_conversion_table[i].codec_id == s1->video_codec_id) {
@@ -767,21 +812,28 @@ static uint32_t device_try_init(AVFormatContext *s1,
                        avcodec_get_name(fmt_conversion_table[i].codec_id),
                        (char *)av_x_if_null(av_get_pix_fmt_name(fmt_conversion_table[i].ff_fmt), "none"));
 
-                desired_format = fmt_conversion_table[i].v4l2_fmt;
-                if (device_init(s1, width, height, desired_format) >= 0) {
+                *desired_format = fmt_conversion_table[i].v4l2_fmt;
+                ret = device_init(s1, width, height, *desired_format);
+                if (ret >= 0)
                     break;
-                }
-                desired_format = 0;
+                else if (ret != AVERROR(EINVAL))
+                    return ret;
+                *desired_format = 0;
             }
         }
-    }
 
-    if (desired_format != 0) {
-        *codec_id = fmt_v4l2codec(desired_format);
-        av_assert0(*codec_id != AV_CODEC_ID_NONE);
+        if (*desired_format == 0) {
+            av_log(s1, AV_LOG_ERROR, "Cannot find a proper format for "
+                   "codec '%s' (id %d), pixel format '%s' (id %d)\n",
+                   avcodec_get_name(s1->video_codec_id), s1->video_codec_id,
+                   (char *)av_x_if_null(av_get_pix_fmt_name(pix_fmt), "none"), pix_fmt);
+            ret = AVERROR(EINVAL);
+        }
     }
 
-    return desired_format;
+    *codec_id = fmt_v4l2codec(*desired_format);
+    av_assert0(*codec_id != AV_CODEC_ID_NONE);
+    return ret;
 }
 
 static int v4l2_read_header(AVFormatContext *s1)
@@ -792,6 +844,7 @@ static int v4l2_read_header(AVFormatContext *s1)
     uint32_t desired_format;
     enum AVCodecID codec_id = AV_CODEC_ID_NONE;
     enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
+    struct v4l2_input input = { 0 };
 
     st = avformat_new_stream(s1, NULL);
     if (!st)
@@ -801,11 +854,34 @@ static int v4l2_read_header(AVFormatContext *s1)
     if (s->fd < 0)
         return s->fd;
 
+    /* set tv video input */
+    av_log(s1, AV_LOG_DEBUG, "Selecting input_channel: %d\n", s->channel);
+    if (v4l2_ioctl(s->fd, VIDIOC_S_INPUT, &s->channel) < 0) {
+        res = errno;
+        av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_S_INPUT): %s\n", strerror(errno));
+        return AVERROR(res);
+    }
+
+    input.index = s->channel;
+    if (v4l2_ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) {
+        res = errno;
+        av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_ENUMINPUT): %s\n", strerror(errno));
+        return AVERROR(res);
+    }
+    s->std_id = input.std;
+    av_log(s1, AV_LOG_DEBUG, "input_channel: %d, input_name: %s\n",
+           s->channel, input.name);
+
     if (s->list_format) {
         list_formats(s1, s->fd, s->list_format);
         return AVERROR_EXIT;
     }
 
+    if (s->list_standard) {
+        list_standards(s1);
+        return AVERROR_EXIT;
+    }
+
     avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
 
     if (s->pixel_format) {
@@ -842,8 +918,11 @@ static int v4l2_read_header(AVFormatContext *s1)
                "Setting frame size to %dx%d\n", s->width, s->height);
     }
 
-    desired_format = device_try_init(s1, pix_fmt, &s->width, &s->height,
-                                     &codec_id);
+    res = device_try_init(s1, pix_fmt, &s->width, &s->height, &desired_format, &codec_id);
+    if (res < 0) {
+        v4l2_close(s->fd);
+        return res;
+    }
 
     /* If no pixel_format was specified, the codec_id was not known up
      * until now. Set video_codec_id in the context, as codec_id will
@@ -852,15 +931,6 @@ static int v4l2_read_header(AVFormatContext *s1)
     if (codec_id != AV_CODEC_ID_NONE && s1->video_codec_id == AV_CODEC_ID_NONE)
         s1->video_codec_id = codec_id;
 
-    if (desired_format == 0) {
-        av_log(s1, AV_LOG_ERROR, "Cannot find a proper format for "
-               "codec '%s' (id %d), pixel format '%s' (id %d)\n",
-               avcodec_get_name(s1->video_codec_id), s1->video_codec_id,
-               (char *)av_x_if_null(av_get_pix_fmt_name(pix_fmt), "none"), pix_fmt);
-        v4l2_close(s->fd);
-
-        return AVERROR(EIO);
-    }
     if ((res = av_image_check_size(s->width, s->height, 0, s1)) < 0)
         return res;
 
@@ -940,6 +1010,9 @@ static const AVOption options[] = {
     { "raw",          "show only non-compressed formats",                         OFFSET(list_format),  AV_OPT_TYPE_CONST,  {.i64 = V4L_RAWFORMATS  },    0, INT_MAX, DEC, "list_formats" },
     { "compressed",   "show only compressed formats",                             OFFSET(list_format),  AV_OPT_TYPE_CONST,  {.i64 = V4L_COMPFORMATS },    0, INT_MAX, DEC, "list_formats" },
 
+    { "list_standards", "list supported standards and exit",                      OFFSET(list_standard), AV_OPT_TYPE_INT,   {.i64 = 0 },  0, 1, DEC, "list_standards" },
+    { "all",            "show all supported standards",                           OFFSET(list_standard), AV_OPT_TYPE_CONST, {.i64 = 1 },  0, 0, DEC, "list_standards" },
+
     { "timestamps",   "set type of timestamps for grabbed frames",                OFFSET(ts_mode),      AV_OPT_TYPE_INT,    {.i64 = 0 }, 0, 2, DEC, "timestamps" },
     { "ts",           "set type of timestamps for grabbed frames",                OFFSET(ts_mode),      AV_OPT_TYPE_INT,    {.i64 = 0 }, 0, 2, DEC, "timestamps" },
     { "default",      "use timestamps from the kernel",                           OFFSET(ts_mode),      AV_OPT_TYPE_CONST,  {.i64 = V4L_TS_DEFAULT  }, 0, 2, DEC, "timestamps" },