/* * V4L2 video output * * Copyright (C) 2010-2011 Ideas on board SPRL * * Contact: Laurent Pinchart * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "videoout.h" #include "isp/v4l2.h" #include "isp/v4l2-pool.h" #define MAX_BUFFERS 16 struct videoout { struct v4l2_device *dev; struct v4l2_buffers_pool *pool; const struct video_out_operations *ops; int streaming; int queued[MAX_BUFFERS]; int num_queued; }; struct videoout *vo_init(const char *devname, const struct video_out_operations *ops, unsigned int buffers, struct v4l2_pix_format *format, unsigned int rotation) { struct v4l2_pix_format pixfmt; struct v4l2_format fmt; struct videoout *vo; int32_t ctrl; int ret; /* Allocate the video output object. */ vo = malloc(sizeof *vo); if (vo == NULL) return NULL; memset(vo, 0, sizeof *vo); vo->ops = ops; /* Open and configure the V4L2 device. */ vo->dev = v4l2_open(devname); if (vo->dev == NULL) { perror("Failed to open display device\n"); goto error; } ctrl = rotation; ret = v4l2_set_control(vo->dev, V4L2_CID_ROTATE, &ctrl); if (ret < 0) { perror("failed to configure rotation\n"); goto error; } pixfmt.pixelformat = format->pixelformat; pixfmt.width = format->width; pixfmt.height = format->height; pixfmt.field = V4L2_FIELD_ANY; ret = v4l2_set_format(vo->dev, &pixfmt); if (ret < 0) { perror("VIDIOC_S_FMT(output)\n"); goto error; } fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; fmt.fmt.win.w.left = 0; fmt.fmt.win.w.top = 0; fmt.fmt.win.w.width = format->width; fmt.fmt.win.w.height = format->height; ret = ioctl(vo->dev->fd, VIDIOC_S_FMT, &fmt); if (ret < 0) { perror("VIDIOC_S_FMT(overlay)\n"); goto error; } /* Allocate buffers. */ if (buffers > MAX_BUFFERS) buffers = MAX_BUFFERS; vo->pool = v4l2_buffers_pool_new(buffers); if (vo->pool == NULL) { printf("error: unable to allocate buffers pool for display.\n"); goto error; } ret = v4l2_alloc_buffers(vo->dev, vo->pool, V4L2_MEMORY_MMAP); if (ret < 0) { printf("error: unable to allocate buffers pool for display.\n"); goto error; } return vo; error: vo_cleanup(vo); return NULL; } void vo_cleanup(struct videoout *vo) { if (vo->dev) v4l2_close(vo->dev); if (vo->pool) v4l2_buffers_pool_delete(vo->pool); free(vo); } int vo_enable_colorkey(struct videoout *vo, unsigned int val) { struct v4l2_framebuffer framebuffer; struct v4l2_format fmt; int ret; ret = ioctl(vo->dev->fd, VIDIOC_G_FBUF, &framebuffer); if (ret < 0) { perror("VIDIOC_G_FBUF"); return -errno; } if (!(framebuffer.capability & V4L2_FBUF_CAP_CHROMAKEY)) return -ENOTSUP; fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; ret = ioctl(vo->dev->fd, VIDIOC_G_FMT, &fmt); if (ret < 0) { perror("VIDIOC_G_FMT\n"); return -errno; } fmt.fmt.win.chromakey = val; ret = ioctl(vo->dev->fd, VIDIOC_S_FMT, &fmt); if (ret < 0) { perror("VIDIOC_G_FMT\n"); return -errno; } framebuffer.flags |= V4L2_FBUF_FLAG_CHROMAKEY; framebuffer.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA; ret = ioctl(vo->dev->fd, VIDIOC_S_FBUF, &framebuffer); if (ret < 0) { perror("VIDIOC_S_FBUF"); return -errno; } return 0; } int vo_disable_colorkey(struct videoout *vo) { struct v4l2_framebuffer framebuffer; int ret; ret = ioctl(vo->dev->fd, VIDIOC_G_FBUF, &framebuffer); if (ret < 0) { perror("VIDIOC_G_FBUF"); return -errno; } if (!(framebuffer.capability & V4L2_FBUF_CAP_CHROMAKEY)) return 0; framebuffer.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY; ret = ioctl(vo->dev->fd, VIDIOC_S_FBUF, &framebuffer); if (ret < 0) { perror("VIDIOC_S_FBUF"); return -errno; } return 0; } struct v4l2_buffers_pool *vo_get_pool(struct videoout *vo) { return vo->pool; } int vo_dequeue_buffer(struct videoout *vo, struct v4l2_video_buffer *buffer) { int ret; if (vo->num_queued < 2) return -ENOBUFS; ret = v4l2_dequeue_buffer(vo->dev, buffer); if (ret < 0) { if (errno != EIO) perror("VIDIOC_DQBUF\n"); return -errno; } vo->queued[buffer->index] = 0; vo->num_queued--; if (vo->num_queued == 0) vo->ops->unwatch_fd(vo->dev->fd); return 0; } int vo_queue_buffer(struct videoout *vo, struct v4l2_video_buffer *buffer) { int ret; if (vo->queued[buffer->index]) { fprintf(stderr, "buffer already queued\n"); return -EBUSY; } ret = v4l2_queue_buffer(vo->dev, buffer); if (ret < 0) { perror("VIDIOC_QBUF\n"); return ret; } vo->queued[buffer->index] = 1; if (!vo->streaming) { if (v4l2_stream_on(vo->dev)) { printf("error: failed to start display streamon\n"); } vo->streaming = 1; } if (vo->num_queued == 0) vo->ops->watch_fd(vo->dev->fd); vo->num_queued++; return 0; }