/*
* interface to the v4l driver
*
* (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/videodev.h>
#include "grab-ng.h"
#include "struct-dump.h"
#include "struct-v4l.h"
#define SYNC_TIMEOUT 5
/* ---------------------------------------------------------------------- */
/* open+close */
static void* v4l_open(char *device);
static int v4l_close(void *handle);
/* attributes */
static char* v4l_devname(void *handle);
static int v4l_flags(void *handle);
static struct ng_attribute* v4l_attrs(void *handle);
static int v4l_read_attr(struct ng_attribute*);
static void v4l_write_attr(struct ng_attribute*, int val);
/* overlay */
static int v4l_setupfb(void *handle, struct ng_video_fmt *fmt, void *base);
static int v4l_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y,
struct OVERLAY_CLIP *oc, int count, int aspect);
/* capture video */
static int v4l_setformat(void *handle, struct ng_video_fmt *fmt);
static int v4l_startvideo(void *handle, int fps, unsigned int buffers);
static void v4l_stopvideo(void *handle);
static struct ng_video_buf* v4l_nextframe(void *handle);
static struct ng_video_buf* v4l_getimage(void *handle);
/* tuner */
static unsigned long v4l_getfreq(void *handle);
static void v4l_setfreq(void *handle, unsigned long freq);
static int v4l_tuned(void *handle);
/* ---------------------------------------------------------------------- */
static const char *device_cap[] = {
"capture", "tuner", "teletext", "overlay", "chromakey", "clipping",
"frameram", "scales", "monochrome", NULL
};
static const char *device_pal[] = {
"-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15",
"yuv422", "yuyv", "uyvy", "yuv420", "yuv411", "raw",
"yuv422p", "yuv411p", "yuv420p", "yuv410p"
};
#define PALETTE(x) ((x < sizeof(device_pal)/sizeof(char*)) ? device_pal[x] : "UNKNOWN")
static struct STRTAB stereo[] = {
{ 0, "auto" },
{ VIDEO_SOUND_MONO, "mono" },
{ VIDEO_SOUND_STEREO, "stereo" },
{ VIDEO_SOUND_LANG1, "lang1" },
{ VIDEO_SOUND_LANG2, "lang2" },
{ -1, NULL },
};
static struct STRTAB norms_v4l[] = {
{ VIDEO_MODE_PAL, "PAL" },
{ VIDEO_MODE_NTSC, "NTSC" },
{ VIDEO_MODE_SECAM, "SECAM" },
{ VIDEO_MODE_AUTO, "AUTO" },
{ -1, NULL }
};
static struct STRTAB norms_bttv[] = {
{ VIDEO_MODE_PAL, "PAL" },
{ VIDEO_MODE_NTSC, "NTSC" },
{ VIDEO_MODE_SECAM, "SECAM" },
{ 3, "PAL-NC" },
{ 4, "PAL-M" },
{ 5, "PAL-N" },
{ 6, "NTSC-JP" },
{ -1, NULL }
};
static unsigned short format2palette[VIDEO_FMT_COUNT] = {
[ VIDEO_RGB08 ] = VIDEO_PALETTE_HI240,
[ VIDEO_GRAY ] = VIDEO_PALETTE_GREY,
[ VIDEO_RGB15_LE ] = VIDEO_PALETTE_RGB555,
[ VIDEO_RGB16_LE ] = VIDEO_PALETTE_RGB565,
[ VIDEO_BGR24 ] = VIDEO_PALETTE_RGB24,
[ VIDEO_BGR32 ] = VIDEO_PALETTE_RGB32,
[ VIDEO_YUYV ] = VIDEO_PALETTE_YUV422,
[ VIDEO_UYVY ] = VIDEO_PALETTE_UYVY,
[ VIDEO_YUV422P ] = VIDEO_PALETTE_YUV422P,
[ VIDEO_YUV420P ] = VIDEO_PALETTE_YUV420P,
};
/* pass 0/1 by reference */
static int one = 1, zero = 0;
/* ---------------------------------------------------------------------- */
struct v4l_handle {
int fd;
/* general informations */
struct video_capability capability;
struct video_channel *channels;
struct video_tuner tuner;
struct video_audio audio;
struct video_picture pict;
/* attributes */
int nattr;
struct ng_attribute *attr;
int input;
int audio_mode;
/* overlay */
struct video_buffer fbuf;
struct video_window win;
int ov_error;
unsigned int ov_fmtid;
int ov_enabled;
int ov_on;
/* capture */
int use_read;
struct ng_video_fmt fmt;
long long start;
int fps;
/* capture via read() */
struct ng_video_fmt rd_fmt;
struct video_window rd_win;
unsigned int rd_fmtid;
/* capture to mmap()'ed buffers */
struct video_mbuf mbuf;
unsigned char *mmap;
unsigned int nbuf;
unsigned int queue;
unsigned int waiton;
int probe[VIDEO_FMT_COUNT];
struct video_mmap *buf_v4l;
struct ng_video_buf *buf_me;
};
struct ng_vid_driver v4l_driver = {
name: "v4l",
open: v4l_open,
close: v4l_close,
get_devname: v4l_devname,
capabilities: v4l_flags,
list_attrs: v4l_attrs,
setupfb: v4l_setupfb,
overlay: v4l_overlay,
setformat: v4l_setformat,
startvideo: v4l_startvideo,
stopvideo: v4l_stopvideo,
nextframe: v4l_nextframe,
getimage: v4l_getimage,
getfreq: v4l_getfreq,
setfreq: v4l_setfreq,
is_tuned: v4l_tuned,
};
/* ---------------------------------------------------------------------- */
static int alarms;
static void
sigalarm(int signal)
{
alarms++;
fprintf(stderr,"v4l: timeout (got SIGALRM), hardware/driver problems?\n");
}
static void
siginit(void)
{
struct sigaction act,old;
memset(&act,0,sizeof(act));
act.sa_handler = sigalarm;
sigemptyset(&act.sa_mask);
sigaction(SIGALRM,&act,&old);
}
/* ---------------------------------------------------------------------- */
#define PREFIX "ioctl: "
static int
xioctl(int fd, int cmd, void *arg)
{
int rc;
rc = ioctl(fd,cmd,arg);
if (0 == rc && ng_debug < 2)
return 0;
print_ioctl(stderr,ioctls_v4l1,PREFIX,cmd,arg);
fprintf(stderr,": %s\n",(rc == 0) ? "ok" : strerror(errno));
return rc;
}
/* ---------------------------------------------------------------------- */
static void
v4l_add_attr(struct v4l_handle *h, int id, int type,
int defval, struct STRTAB *choices)
{
h->attr = realloc(h->attr,(h->nattr+2) * sizeof(struct ng_attribute));
memset(h->attr+h->nattr,0,sizeof(struct ng_attribute)*2);
h->attr[h->nattr].id = id;
h->attr[h->nattr].type = type;
h->attr[h->nattr].defval = defval;
h->attr[h->nattr].choices = choices;
if (ATTR_TYPE_INTEGER == type) {
h->attr[h->nattr].min = 0;
h->attr[h->nattr].max = 65535;
}
if (id < ATTR_ID_COUNT)
h->attr[h->nattr].name = ng_attr_to_desc[id];
h->attr[h->nattr].read = v4l_read_attr;
h->attr[h->nattr].write = v4l_write_attr;
h->attr[h->nattr].handle = h;
h->nattr++;
}
static void*
v4l_open(char *device)
{
struct v4l_handle *h;
struct STRTAB *inputs;
struct STRTAB *norms;
unsigned int i;
int rc;
h = malloc(sizeof(*h));
if (NULL == h)
return NULL;
memset(h,0,sizeof(*h));
/* open device */
if (-1 == (h->fd = open(device,O_RDWR))) {
fprintf(stderr,"v4l: open %s: %s\n",device,strerror(errno));
goto err;
}
if (-1 == ioctl(h->fd,VIDIOCGCAP,&h->capability))
goto err;
if (ng_debug)
fprintf(stderr, "v4l: open: %s (%s)\n",device,h->capability.name);
fcntl(h->fd,F_SETFD,FD_CLOEXEC);
siginit();
if (ng_debug) {
fprintf(stderr," capabilities: ");
for (i = 0; device_cap[i] != NULL; i++)
if (h->capability.type & (1 << i))
fprintf(stderr," %s",device_cap[i]);
fprintf(stderr,"\n");
fprintf(stderr," size : %dx%d => %dx%d\n",