📄 saa7146_video.c
字号:
#include <media/saa7146_vv.h>static int memory = 32;MODULE_PARM(memory,"i");MODULE_PARM_DESC(memory, "maximum memory usage for capture buffers (default: 32Mb)");/* format descriptions for capture and preview */static struct saa7146_format formats[] = { { .name = "RGB-8 (3-3-2)", .pixelformat = V4L2_PIX_FMT_RGB332, .trans = RGB08_COMPOSED, .depth = 8, .flags = 0, }, { .name = "RGB-16 (5/B-6/G-5/R)", .pixelformat = V4L2_PIX_FMT_RGB565, .trans = RGB16_COMPOSED, .depth = 16, .flags = 0, }, { .name = "RGB-24 (B-G-R)", .pixelformat = V4L2_PIX_FMT_BGR24, .trans = RGB24_COMPOSED, .depth = 24, .flags = 0, }, { .name = "RGB-32 (B-G-R)", .pixelformat = V4L2_PIX_FMT_BGR32, .trans = RGB32_COMPOSED, .depth = 32, .flags = 0, }, { .name = "Greyscale-8", .pixelformat = V4L2_PIX_FMT_GREY, .trans = Y8, .depth = 8, .flags = 0, }, { .name = "YUV 4:2:2 planar (Y-Cb-Cr)", .pixelformat = V4L2_PIX_FMT_YUV422P, .trans = YUV422_DECOMPOSED, .depth = 16, .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, }, { .name = "YVU 4:2:0 planar (Y-Cb-Cr)", .pixelformat = V4L2_PIX_FMT_YVU420, .trans = YUV420_DECOMPOSED, .depth = 12, .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, }, { .name = "YUV 4:2:0 planar (Y-Cb-Cr)", .pixelformat = V4L2_PIX_FMT_YUV420, .trans = YUV420_DECOMPOSED, .depth = 12, .flags = FORMAT_IS_PLANAR, }, { .name = "YUV 4:2:2 (U-Y-V-Y)", .pixelformat = V4L2_PIX_FMT_UYVY, .trans = YUV422_COMPOSED, .depth = 16, .flags = 0, }};/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped (like V4L2_PIX_FMT_YUYV) ... 8-( */ static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc){ int i, j = NUM_FORMATS; for (i = 0; i < j; i++) { if (formats[i].pixelformat == fourcc) { return formats+i; } } DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc)); return NULL;}static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f){ struct saa7146_dev *dev = fh->dev; DEB_EE(("dev:%p, fh:%p\n",dev,fh)); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: f->fmt.pix = fh->video_fmt; return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: f->fmt.win = fh->ov.win; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: { f->fmt.vbi = fh->vbi_fmt; return 0; } default: DEB_D(("invalid format type '%d'.\n",f->type)); return -EINVAL; }}static int try_win(struct saa7146_dev *dev, struct v4l2_window *win){ struct saa7146_vv *vv = dev->vv_data; enum v4l2_field field; int maxw, maxh; DEB_EE(("dev:%p\n",dev)); if (NULL == vv->ov_fb.base) { DEB_D(("no fb base set.\n")); return -EINVAL; } if (NULL == vv->ov_fmt) { DEB_D(("no fb fmt set.\n")); return -EINVAL; } if (win->w.width < 48 || win->w.height < 32) { DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height)); return -EINVAL; } if (win->clipcount > 16) { DEB_D(("clipcount too big.\n")); return -EINVAL; } field = win->field; maxw = vv->standard->h_max_out; maxh = vv->standard->v_max_out; if (V4L2_FIELD_ANY == field) { field = (win->w.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; } switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_ALTERNATE: maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: break; default: { DEB_D(("no known field mode '%d'.\n",field)); return -EINVAL; } } win->field = field; if (win->w.width > maxw) win->w.width = maxw; if (win->w.height > maxh) win->w.height = maxh; return 0;}static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { struct saa7146_format *fmt; enum v4l2_field field; int maxw, maxh; int calc_bpl; DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh)); fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat); if (NULL == fmt) { return -EINVAL; } field = f->fmt.pix.field; maxw = vv->standard->h_max_out; maxh = vv->standard->v_max_out; if (V4L2_FIELD_ANY == field) { field = (f->fmt.pix.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; } switch (field) { case V4L2_FIELD_ALTERNATE: { vv->last_field = V4L2_FIELD_TOP; maxh = maxh / 2; break; } case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: vv->last_field = V4L2_FIELD_INTERLACED; maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: vv->last_field = V4L2_FIELD_INTERLACED; break; default: { DEB_D(("no known field mode '%d'.\n",field)); return -EINVAL; } } f->fmt.pix.field = field; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; calc_bpl = (f->fmt.pix.width * fmt->depth)/8; if (f->fmt.pix.bytesperline < calc_bpl) f->fmt.pix.bytesperline = calc_bpl; if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */ f->fmt.pix.bytesperline = calc_bpl; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage)); return 0; } case V4L2_BUF_TYPE_VIDEO_OVERLAY: DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh)); err = try_win(dev,&f->fmt.win); if (0 != err) { return err; } return 0; default: DEB_EE(("unknown format type '%d'\n",f->type)); return -EINVAL; }}int saa7146_start_preview(struct saa7146_fh *fh){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; int ret = 0, err = 0; DEB_EE(("dev:%p, fh:%p\n",dev,fh)); /* check if we have overlay informations */ if( NULL == fh->ov.fh ) { DEB_D(("no overlay data available. try S_FMT first.\n")); return -EAGAIN; } /* check if overlay is running */ if( 0 != vv->ov_data ) { if( fh != vv->ov_data->fh ) { DEB_D(("overlay is running in another open.\n")); return -EAGAIN; } DEB_D(("overlay is already active.\n")); return 0; } if( 0 != vv->streaming ) { DEB_D(("streaming capture is active.\n")); return -EBUSY; } err = try_win(dev,&fh->ov.win); if (0 != err) { return err; } vv->ov_data = &fh->ov; DEB_D(("%dx%d+%d+%d %s field=%s\n", fh->ov.win.w.width,fh->ov.win.w.height, fh->ov.win.w.left,fh->ov.win.w.top, vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field])); if (0 != (ret = saa7146_enable_overlay(fh))) { vv->ov_data = NULL; DEB_D(("enabling overlay failed: %d\n",ret)); return ret; } return 0;}int saa7146_stop_preview(struct saa7146_fh *fh){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; DEB_EE(("dev:%p, fh:%p\n",dev,fh)); /* check if overlay is running */ if( 0 == vv->ov_data ) { DEB_D(("overlay is not active.\n")); return 0; } if( fh != vv->ov_data->fh ) { DEB_D(("overlay is active, but for another open.\n")); return 0; } vv->ov_data = NULL; saa7146_disable_overlay(fh); return 0;}static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; unsigned long flags; int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh)); if( fh == vv->streaming ) { DEB_EE(("streaming capture is active")); return -EAGAIN; } err = try_fmt(fh,f); if (0 != err) return err; fh->video_fmt = f->fmt.pix; DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat)); return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh)); err = try_win(dev,&f->fmt.win); if (0 != err) return err; down(&dev->lock); fh->ov.win = f->fmt.win; fh->ov.nclips = f->fmt.win.clipcount; if (fh->ov.nclips > 16) fh->ov.nclips = 16; if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) { up(&dev->lock); return -EFAULT; } /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ fh->ov.fh = fh; /* check if we have an active overlay */ if( vv->ov_data != NULL ) { if( fh == vv->ov_data->fh) { spin_lock_irqsave(&dev->slock,flags); saa7146_stop_preview(fh); saa7146_start_preview(fh); spin_unlock_irqrestore(&dev->slock,flags); } } up(&dev->lock); return 0; default: DEB_D(("unknown format type '%d'\n",f->type)); return -EINVAL; }}/********************************************************************************//* device controls */static struct v4l2_queryctrl controls[] = { { .id = V4L2_CID_BRIGHTNESS, .name = "Brightness", .minimum = 0, .maximum = 255, .step = 1, .default_value = 128, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_CONTRAST, .name = "Contrast", .minimum = 0, .maximum = 127, .step = 1, .default_value = 64, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_SATURATION, .name = "Saturation", .minimum = 0, .maximum = 127, .step = 1, .default_value = 64, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_VFLIP, .name = "Vertical flip", .minimum = 0, .maximum = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, },{ .id = V4L2_CID_HFLIP, .name = "Horizontal flip", .minimum = 0, .maximum = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, },};static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)static struct v4l2_queryctrl* ctrl_by_id(int id){ int i; for (i = 0; i < NUM_CONTROLS; i++) if (controls[i].id == id) return controls+i; return NULL;}static int get_control(struct saa7146_fh *fh, struct v4l2_control *c){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; const struct v4l2_queryctrl* ctrl; u32 value = 0; ctrl = ctrl_by_id(c->id); if (NULL == ctrl) return -EINVAL; switch (c->id) { case V4L2_CID_BRIGHTNESS: value = saa7146_read(dev, BCS_CTRL); c->value = 0xff & (value >> 24); DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value)); break; case V4L2_CID_CONTRAST: value = saa7146_read(dev, BCS_CTRL); c->value = 0x7f & (value >> 16); DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value)); break; case V4L2_CID_SATURATION: value = saa7146_read(dev, BCS_CTRL); c->value = 0x7f & (value >> 0); DEB_D(("V4L2_CID_SATURATION: %d\n",c->value)); break; case V4L2_CID_VFLIP: c->value = vv->vflip; DEB_D(("V4L2_CID_VFLIP: %d\n",c->value)); break; case V4L2_CID_HFLIP: c->value = vv->hflip; DEB_D(("V4L2_CID_HFLIP: %d\n",c->value)); break; default: return -EINVAL; } return 0;}static int set_control(struct saa7146_fh *fh, struct v4l2_control *c){ struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; const struct v4l2_queryctrl* ctrl; unsigned long flags; int restart_overlay = 0; ctrl = ctrl_by_id(c->id); if (NULL == ctrl) { DEB_D(("unknown control %d\n",c->id)); return -EINVAL; } switch (ctrl->type) { case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER: if (c->value < ctrl->minimum) c->value = ctrl->minimum; if (c->value > ctrl->maximum) c->value = ctrl->maximum; break; default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -