v4l2: use C99 struct initializer
[ffmpeg.git] / libavdevice / v4l2.c
index 1e4274c..191decd 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/time.h>
+#include <poll.h>
 #if HAVE_SYS_VIDEOIO_H
 #include <sys/videoio.h>
 #else
 #include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/avstring.h"
+#include "libavutil/mathematics.h"
 
 static const int desired_video_buffers = 256;
 
+#define V4L_ALLFORMATS  3
+#define V4L_RAWFORMATS  1
+#define V4L_COMPFORMATS 2
+
 struct video_data {
     AVClass *class;
     int fd;
     int frame_format; /* V4L2_PIX_FMT_* */
     int width, height;
     int frame_size;
+    int timeout;
     int interlaced;
     int top_field_first;
 
@@ -65,8 +72,10 @@ struct video_data {
     unsigned int *buf_len;
     char *standard;
     int channel;
-    char *video_size; /**< String describing video size, set by a private option. */
+    char *video_size;   /**< String describing video size,
+                             set by a private option. */
     char *pixel_format; /**< Set by a private option. */
+    int list_format;    /**< Set by a private option. */
     char *framerate;    /**< Set by a private option. */
 };
 
@@ -160,14 +169,11 @@ static int device_init(AVFormatContext *ctx, int *width, int *height,
 {
     struct video_data *s = ctx->priv_data;
     int fd = s->fd;
-    struct v4l2_format fmt;
+    struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
     struct v4l2_pix_format *pix = &fmt.fmt.pix;
 
     int res;
 
-    memset(&fmt, 0, sizeof(struct v4l2_format));
-
-    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     pix->width = *width;
     pix->height = *height;
     pix->pixelformat = pix_fmt;
@@ -258,16 +264,81 @@ static enum CodecID fmt_v4l2codec(uint32_t v4l2_fmt)
     return CODEC_ID_NONE;
 }
 
+#if HAVE_STRUCT_V4L2_FRMIVALENUM_DISCRETE
+static void list_framesizes(AVFormatContext *ctx, int fd, uint32_t pixelformat)
+{
+    struct v4l2_frmsizeenum vfse = { .pixel_format = pixelformat };
+
+    while(!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) {
+        switch (vfse.type) {
+        case V4L2_FRMSIZE_TYPE_DISCRETE:
+            av_log(ctx, AV_LOG_INFO, " %ux%u",
+                   vfse.discrete.width, vfse.discrete.height);
+        break;
+        case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+        case V4L2_FRMSIZE_TYPE_STEPWISE:
+            av_log(ctx, AV_LOG_INFO, " {%u-%u, %u}x{%u-%u, %u}",
+                   vfse.stepwise.min_width,
+                   vfse.stepwise.max_width,
+                   vfse.stepwise.step_width,
+                   vfse.stepwise.min_height,
+                   vfse.stepwise.max_height,
+                   vfse.stepwise.step_height);
+        }
+        vfse.index++;
+    }
+}
+#endif
+
+static void list_formats(AVFormatContext *ctx, int fd, int type)
+{
+    struct v4l2_fmtdesc vfd = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
+
+    while(!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) {
+        enum CodecID codec_id = fmt_v4l2codec(vfd.pixelformat);
+        enum PixelFormat pix_fmt = fmt_v4l2ff(vfd.pixelformat, codec_id);
+
+        vfd.index++;
+
+        if (!(vfd.flags & V4L2_FMT_FLAG_COMPRESSED) &&
+            type & V4L_RAWFORMATS) {
+            const char *fmt_name = av_get_pix_fmt_name(pix_fmt);
+            av_log(ctx, AV_LOG_INFO, "R : %9s : %20s :",
+                   fmt_name ? fmt_name : "Unsupported",
+                   vfd.description);
+        } else if (vfd.flags & V4L2_FMT_FLAG_COMPRESSED &&
+                   type & V4L_COMPFORMATS) {
+            AVCodec *codec = avcodec_find_encoder(codec_id);
+            av_log(ctx, AV_LOG_INFO, "C : %9s : %20s :",
+                   codec ? codec->name : "Unsupported",
+                   vfd.description);
+        } else {
+            continue;
+        }
+
+#ifdef V4L2_FMT_FLAG_EMULATED
+        if (vfd.flags & V4L2_FMT_FLAG_EMULATED) {
+            av_log(ctx, AV_LOG_WARNING, "%s", "Emulated");
+            continue;
+        }
+#endif
+#if HAVE_STRUCT_V4L2_FRMIVALENUM_DISCRETE
+        list_framesizes(ctx, fd, vfd.pixelformat);
+#endif
+        av_log(ctx, AV_LOG_INFO, "\n");
+    }
+}
+
 static int mmap_init(AVFormatContext *ctx)
 {
-    struct video_data *s = ctx->priv_data;
-    struct v4l2_requestbuffers req;
     int i, res;
+    struct video_data *s = ctx->priv_data;
+    struct v4l2_requestbuffers req = {
+        .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+        .count  = desired_video_buffers,
+        .memory = V4L2_MEMORY_MMAP
+    };
 
-    memset(&req, 0, sizeof(struct v4l2_requestbuffers));
-    req.count = desired_video_buffers;
-    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    req.memory = V4L2_MEMORY_MMAP;
     res = ioctl(s->fd, VIDIOC_REQBUFS, &req);
     if (res < 0) {
         if (errno == EINVAL) {
@@ -300,12 +371,12 @@ static int mmap_init(AVFormatContext *ctx)
     }
 
     for (i = 0; i < req.count; i++) {
-        struct v4l2_buffer buf;
+        struct v4l2_buffer buf = {
+            .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+            .index  = i,
+            .memory = V4L2_MEMORY_MMAP
+        };
 
-        memset(&buf, 0, sizeof(struct v4l2_buffer));
-        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-        buf.memory = V4L2_MEMORY_MMAP;
-        buf.index = i;
         res = ioctl(s->fd, VIDIOC_QUERYBUF, &buf);
         if (res < 0) {
             av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QUERYBUF)\n");
@@ -337,14 +408,13 @@ static int mmap_init(AVFormatContext *ctx)
 
 static void mmap_release_buffer(AVPacket *pkt)
 {
-    struct v4l2_buffer buf;
+    struct v4l2_buffer buf = { 0 };
     int res, fd;
     struct buff_data *buf_descriptor = pkt->priv;
 
     if (pkt->data == NULL)
         return;
 
-    memset(&buf, 0, sizeof(struct v4l2_buffer));
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
     buf.index = buf_descriptor->index;
@@ -363,13 +433,20 @@ static void mmap_release_buffer(AVPacket *pkt)
 static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt)
 {
     struct video_data *s = ctx->priv_data;
-    struct v4l2_buffer buf;
+    struct v4l2_buffer buf = {
+        .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+        .memory = V4L2_MEMORY_MMAP
+    };
     struct buff_data *buf_descriptor;
+    struct pollfd p = { .fd = s->fd, .events = POLLIN };
     int res;
 
-    memset(&buf, 0, sizeof(struct v4l2_buffer));
-    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    buf.memory = V4L2_MEMORY_MMAP;
+    res = poll(&p, 1, s->timeout);
+    if (res < 0)
+        return AVERROR(errno);
+
+    if (!(p.revents & (POLLIN | POLLERR | POLLHUP)))
+        return AVERROR(EAGAIN);
 
     /* FIXME: Some special treatment might be needed in case of loss of signal... */
     while ((res = ioctl(s->fd, VIDIOC_DQBUF, &buf)) < 0 && (errno == EINTR));
@@ -422,12 +499,11 @@ static int mmap_start(AVFormatContext *ctx)
     int i, res;
 
     for (i = 0; i < s->buffers; i++) {
-        struct v4l2_buffer buf;
-
-        memset(&buf, 0, sizeof(struct v4l2_buffer));
-        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-        buf.memory = V4L2_MEMORY_MMAP;
-        buf.index  = i;
+        struct v4l2_buffer buf = {
+            .type   = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+            .index  = i,
+            .memory = V4L2_MEMORY_MMAP
+        };
 
         res = ioctl(s->fd, VIDIOC_QBUF, &buf);
         if (res < 0) {
@@ -470,12 +546,12 @@ static void mmap_close(struct video_data *s)
 static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
 {
     struct video_data *s = s1->priv_data;
-    struct v4l2_input input;
-    struct v4l2_standard standard;
+    struct v4l2_input input = { 0 };
+    struct v4l2_standard standard = { 0 };
     struct v4l2_streamparm streamparm = { 0 };
     struct v4l2_fract *tpf = &streamparm.parm.capture.timeperframe;
+    AVRational framerate_q = { 0 };
     int i, ret;
-    AVRational framerate_q;
 
     streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
@@ -487,7 +563,6 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
     }
 
     /* set tv video input */
-    memset (&input, 0, sizeof (input));
     input.index = s->channel;
     if (ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) {
         av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl enum input failed:\n");
@@ -507,7 +582,6 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
         av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set standard: %s\n",
                s->standard);
         /* set tv standard */
-        memset (&standard, 0, sizeof (standard));
         for(i=0;;i++) {
             standard.index = i;
             if (ioctl(s->fd, VIDIOC_ENUMSTD, &standard) < 0) {
@@ -564,6 +638,10 @@ static int v4l2_set_parameters(AVFormatContext *s1, AVFormatParameters *ap)
     s1->streams[0]->codec->time_base.den = tpf->denominator;
     s1->streams[0]->codec->time_base.num = tpf->numerator;
 
+    s->timeout = 100 +
+        av_rescale_q(1, s1->streams[0]->codec->time_base,
+                        (AVRational){1, 1000});
+
     return 0;
 }
 
@@ -621,6 +699,12 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
         goto out;
     }
 
+    if (s->list_format) {
+        list_formats(s1, s->fd, s->list_format);
+        res = AVERROR_EXIT;
+        goto out;
+    }
+
     avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
 
     if (s->video_size &&
@@ -629,12 +713,22 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
                s->video_size);
         goto out;
     }
-    if (s->pixel_format &&
-        (pix_fmt = av_get_pix_fmt(s->pixel_format)) == PIX_FMT_NONE) {
-        av_log(s1, AV_LOG_ERROR, "No such pixel format: %s.\n",
-               s->pixel_format);
-        res = AVERROR(EINVAL);
-        goto out;
+
+    if (s->pixel_format) {
+        AVCodec *codec = avcodec_find_decoder_by_name(s->pixel_format);
+
+        if (codec)
+            s1->video_codec_id = codec->id;
+
+        pix_fmt = av_get_pix_fmt(s->pixel_format);
+
+        if (pix_fmt == PIX_FMT_NONE && !codec) {
+            av_log(s1, AV_LOG_ERROR, "No such input format: %s.\n",
+                   s->pixel_format);
+
+            res = AVERROR(EINVAL);
+            goto out;
+        }
     }
 
     if (!s->width && !s->height) {
@@ -689,6 +783,9 @@ static int v4l2_read_header(AVFormatContext *s1, AVFormatParameters *ap)
 
     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
     st->codec->codec_id = codec_id;
+    if (codec_id == CODEC_ID_RAWVIDEO)
+        st->codec->codec_tag =
+            avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
     st->codec->width = s->width;
     st->codec->height = s->height;
     st->codec->bit_rate = s->frame_size * 1/av_q2d(st->codec->time_base) * 8;
@@ -732,8 +829,13 @@ static const AVOption options[] = {
     { "standard",     "TV standard, used only by analog frame grabber",            OFFSET(standard),     AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,       DEC },
     { "channel",      "TV channel, used only by frame grabber",                    OFFSET(channel),      AV_OPT_TYPE_INT,    {.dbl = 0 },    0, INT_MAX, DEC },
     { "video_size",   "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size),   AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       DEC },
-    { "pixel_format", "",                                                          OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       DEC },
+    { "pixel_format", "Preferred pixel format",                                    OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       DEC },
+    { "input_format", "Preferred pixel format (for raw video) or codec name",      OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       DEC },
     { "framerate",    "",                                                          OFFSET(framerate),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       DEC },
+    { "list_formats", "List available formats and exit",                           OFFSET(list_format),  AV_OPT_TYPE_INT,    {.dbl = 0 },  0, INT_MAX, DEC, "list_formats" },
+    { "all",          "Show all available formats",                                OFFSET(list_format),  AV_OPT_TYPE_CONST,  {.dbl = V4L_ALLFORMATS  },    0, INT_MAX, DEC, "list_formats" },
+    { "raw",          "Show only non-compressed formats",                          OFFSET(list_format),  AV_OPT_TYPE_CONST,  {.dbl = V4L_RAWFORMATS  },    0, INT_MAX, DEC, "list_formats" },
+    { "compressed",   "Show only compressed formats",                              OFFSET(list_format),  AV_OPT_TYPE_CONST,  {.dbl = V4L_COMPFORMATS },    0, INT_MAX, DEC, "list_formats" },
     { NULL },
 };