You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/barcode/barcode_v4l.cpp

539 lines
13 KiB

/***************************************************************************
copyright : (C) 2007 by Sebastian Held
email : sebastian.held@gmx.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of version 2 of the GNU General Public License as *
* published by the Free Software Foundation; *
* *
***************************************************************************/
// uses code from xawtv: webcam.c (c) 1998-2002 Gerd Knorr
//#include <stdio.h>
//#include <stdlib.h>
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
//#include <malloc.h>
//#include <sys/stat.h>
//#include <sys/types.h>
//#include <sys/time.h>
//#include <sys/mman.h>
#include <sys/ioctl.h>
//#include <asm/types.h> /* for videodev2.h */
#include "barcode_v4l.h"
#include "../tellico_debug.h"
using barcodeRecognition::ng_vid_driver;
using barcodeRecognition::ng_vid_driver_v4l;
using barcodeRecognition::barcode_v4l;
using barcodeRecognition::video_converter;
TQPtrList<barcodeRecognition::video_converter> barcodeRecognition::ng_vid_driver::m_converter;
const char *barcodeRecognition::device_cap[] = { "capture", "tuner", "teletext", "overlay", "chromakey", "clipping", "frameram", "scales", "monochrome", 0 };
const unsigned int barcodeRecognition::ng_vfmt_to_depth[] = {
0, /* unused */
8, /* RGB8 */
8, /* GRAY8 */
16, /* RGB15 LE */
16, /* RGB16 LE */
16, /* RGB15 BE */
16, /* RGB16 BE */
24, /* BGR24 */
32, /* BGR32 */
24, /* RGB24 */
32, /* RGB32 */
16, /* LUT2 */
32, /* LUT4 */
16, /* YUYV */
16, /* YUV422P */
12, /* YUV420P */
0, /* MJPEG */
0, /* JPEG */
16, /* UYVY */
};
const char* barcodeRecognition::ng_vfmt_to_desc[] = {
"none",
"8 bit PseudoColor (dithering)",
"8 bit StaticGray",
"15 bit TrueColor (LE)",
"16 bit TrueColor (LE)",
"15 bit TrueColor (BE)",
"16 bit TrueColor (BE)",
"24 bit TrueColor (LE: bgr)",
"32 bit TrueColor (LE: bgr-)",
"24 bit TrueColor (BE: rgb)",
"32 bit TrueColor (BE: -rgb)",
"16 bit TrueColor (lut)",
"32 bit TrueColor (lut)",
"16 bit YUV 4:2:2 (packed, YUYV)",
"16 bit YUV 4:2:2 (planar)",
"12 bit YUV 4:2:0 (planar)",
"MJPEG (AVI)",
"JPEG (JFIF)",
"16 bit YUV 4:2:2 (packed, UYVY)",
};
unsigned int barcodeRecognition::ng_yuv_gray[256];
unsigned int barcodeRecognition::ng_yuv_red[256];
unsigned int barcodeRecognition::ng_yuv_blue[256];
unsigned int barcodeRecognition::ng_yuv_g1[256];
unsigned int barcodeRecognition::ng_yuv_g2[256];
unsigned int barcodeRecognition::ng_clip[256 + 2 * CLIP];
void barcodeRecognition::ng_color_yuv2rgb_init()
{
int i;
# define RED_NULL 128
# define BLUE_NULL 128
# define LUN_MUL 256
# define RED_MUL 512
# define BLUE_MUL 512
#define GREEN1_MUL (-RED_MUL/2)
#define GREEN2_MUL (-BLUE_MUL/6)
#define RED_ADD (-RED_NULL * RED_MUL)
#define BLUE_ADD (-BLUE_NULL * BLUE_MUL)
#define GREEN1_ADD (-RED_ADD/2)
#define GREEN2_ADD (-BLUE_ADD/6)
/* init Lookup tables */
for (i = 0; i < 256; i++) {
barcodeRecognition::ng_yuv_gray[i] = i * LUN_MUL >> 8;
barcodeRecognition::ng_yuv_red[i] = (RED_ADD + i * RED_MUL) >> 8;
barcodeRecognition::ng_yuv_blue[i] = (BLUE_ADD + i * BLUE_MUL) >> 8;
barcodeRecognition::ng_yuv_g1[i] = (GREEN1_ADD + i * GREEN1_MUL) >> 8;
barcodeRecognition::ng_yuv_g2[i] = (GREEN2_ADD + i * GREEN2_MUL) >> 8;
}
for (i = 0; i < CLIP; i++)
barcodeRecognition::ng_clip[i] = 0;
for (; i < CLIP + 256; i++)
barcodeRecognition::ng_clip[i] = i - CLIP;
for (; i < 2 * CLIP + 256; i++)
barcodeRecognition::ng_clip[i] = 255;
}
barcode_v4l::barcode_v4l()
{
m_devname = "/dev/video0";
m_grab_width = 640;
m_grab_height = 480;
m_drv = 0;
m_conv = 0;
barcodeRecognition::ng_color_yuv2rgb_init();
//ng_vid_driver::register_video_converter( new barcodeRecognition::yuv422p_to_rgb24() );
//ng_vid_driver::register_video_converter( new barcodeRecognition::yuv422_to_rgb24() );
ng_vid_driver::register_video_converter( new barcodeRecognition::yuv420p_to_rgb24() );
grab_init();
}
barcode_v4l::~barcode_v4l()
{
if (m_drv) {
m_drv->close();
delete m_drv;
}
}
bool barcode_v4l::isOpen()
{
return m_drv;
}
TQImage barcode_v4l::grab_one2()
{
if (!m_drv) {
myDebug() << "no driver/device available" << endl;
return TQImage();
}
TQByteArray *cap;
if (!(cap = m_drv->getimage2())) {
myDebug() << "capturing image failed" << endl;
return TQImage();
}
TQByteArray *buf;
if (m_conv) {
buf = new TQByteArray( 3*m_fmt.width*m_fmt.height );
m_conv->frame( buf, cap, m_fmt_drv );
} else {
buf = new TQByteArray(*cap);
}
delete cap;
int depth = 32;
TQByteArray *buf2 = new TQByteArray( depth/8 *m_fmt.width*m_fmt.height );
for (uint i=0; i<m_fmt.width*m_fmt.height; i++) {
(*buf2)[i*4+0] = buf->at(i*3+2);
(*buf2)[i*4+1] = buf->at(i*3+1);
(*buf2)[i*4+2] = buf->at(i*3+0);
(*buf2)[i*4+3] = 0;
}
delete buf;
TQImage *temp = new TQImage( (uchar*)buf2->data(), m_fmt.width, m_fmt.height, depth, 0, 0, TQImage::LittleEndian );
TQImage temp2 = temp->copy();
delete temp;
delete buf2;
return temp2;
}
bool barcode_v4l::grab_init()
{
m_drv = ng_vid_driver::createDriver( m_devname );
if (!m_drv) {
myDebug() << "no grabber device available" << endl;
return false;
}
if (!(m_drv->capabilities() & CAN_CAPTURE)) {
myDebug() << "device doesn't support capture" << endl;
m_drv->close();
delete m_drv;
m_drv = 0;
return false;
}
/* try native */
m_fmt.fmtid = VIDEO_RGB24;
m_fmt.width = m_grab_width;
m_fmt.height = m_grab_height;
if (m_drv->setformat( &m_fmt )) {
m_fmt_drv = m_fmt;
return true;
}
/* check all available conversion functions */
m_fmt.bytesperline = m_fmt.width * ng_vfmt_to_depth[m_fmt.fmtid] / 8;
for (int i = 0; i<VIDEO_FMT_COUNT; i++) {
video_converter *conv = ng_vid_driver::find_video_converter( VIDEO_RGB24, i );
if (!conv)
continue;
m_fmt_drv = m_fmt;
m_fmt_drv.fmtid = conv->fmtid_in();
m_fmt_drv.bytesperline = 0;
if (m_drv->setformat( &m_fmt_drv )) {
m_fmt.width = m_fmt_drv.width;
m_fmt.height = m_fmt_drv.height;
m_conv = conv;
//m_conv->init( &m_fmt );
return true;
}
}
myDebug() << "can't get rgb24 data" << endl;
m_drv->close();
delete m_drv;
m_drv = 0;
return false;
}
ng_vid_driver* ng_vid_driver::createDriver( TQString device )
{
/* check all grabber drivers */
ng_vid_driver *drv = new ng_vid_driver_v4l();
if (!drv->open2( device )) {
myDebug() << "no v4l device found" << endl;
delete drv;
drv = 0;
}
return drv;
}
int ng_vid_driver::xioctl( int fd, int cmd, void *arg )
{
int rc;
rc = ioctl(fd,cmd,arg);
if (rc == 0)
return 0;
//print_ioctl(stderr,ioctls_v4l1,PREFIX,cmd,arg);
tqDebug( ": %s\n",(rc == 0) ? "ok" : strerror(errno) );
return rc;
}
void ng_vid_driver::register_video_converter( video_converter *conv )
{
if (!conv)
return;
m_converter.append( conv );
}
video_converter *ng_vid_driver::find_video_converter( int out, int in )
{
video_converter *conv;
for (conv = m_converter.first(); conv; conv = m_converter.next() )
if ((conv->fmtid_in() == in) && (conv->fmtid_out() == out))
return conv;
return 0;
}
ng_vid_driver_v4l::ng_vid_driver_v4l()
{
m_name = "v4l";
m_drv = 0;
m_fd = -1;
m_use_read = false;
for (int i=0; i<VIDEO_FMT_COUNT; i++)
format2palette[i] = 0;
format2palette[VIDEO_RGB08] = VIDEO_PALETTE_HI240;
format2palette[VIDEO_GRAY] = VIDEO_PALETTE_GREY;
format2palette[VIDEO_RGB15_LE] = VIDEO_PALETTE_RGB555;
format2palette[VIDEO_RGB16_LE] = VIDEO_PALETTE_RGB565;
format2palette[VIDEO_BGR24] = VIDEO_PALETTE_RGB24;
format2palette[VIDEO_BGR32] = VIDEO_PALETTE_RGB32;
format2palette[VIDEO_YUYV] = VIDEO_PALETTE_YUV422;
format2palette[VIDEO_UYVY] = VIDEO_PALETTE_UYVY;
format2palette[VIDEO_YUV422P] = VIDEO_PALETTE_YUV422P;
format2palette[VIDEO_YUV420P] = VIDEO_PALETTE_YUV420P;
}
bool ng_vid_driver_v4l::open2( TQString device )
{
/* open device */
if ((m_fd = ::open( device.latin1(), O_RDWR )) == -1) {
tqDebug( "v4l: open %s: %s\n", device.latin1(), strerror(errno) );
return false;
}
if (ioctl( m_fd, VIDIOCGCAP, &m_capability ) == -1) {
::close( m_fd );
return false;
}
#ifdef Barcode_DEBUG
tqDebug( "v4l: open: %s (%s)\n", device.latin1(), m_capability.name );
#endif
fcntl( m_fd, F_SETFD, FD_CLOEXEC ); // close on exit
#ifdef Barcode_DEBUG
myDebug() << " capabilities: " << endl;
for (int i = 0; device_cap[i] != NULL; i++)
if (m_capability.type & (1 << i))
tqDebug( " %s", device_cap[i] );
tqDebug( " size : %dx%d => %dx%d", m_capability.minwidth, m_capability.minheight, m_capability.maxwidth, m_capability.maxheight );
#endif
#ifdef Barcode_DEBUG
fprintf(stderr," v4l: using read() for capture\n");
#endif
m_use_read = true;
return true;
}
void ng_vid_driver_v4l::close()
{
#ifdef Barcode_DEBUG
fprintf(stderr, "v4l: close\n");
#endif
if (m_fd != -1)
::close(m_fd);
m_fd = -1;
return;
}
int ng_vid_driver_v4l::capabilities()
{
int ret = 0;
if (m_capability.type & VID_TYPE_OVERLAY)
ret |= CAN_OVERLAY;
if (m_capability.type & VID_TYPE_CAPTURE)
ret |= CAN_CAPTURE;
if (m_capability.type & VID_TYPE_TUNER)
ret |= CAN_TUNE;
if (m_capability.type & VID_TYPE_CHROMAKEY)
ret |= NEEDS_CHROMAKEY;
return ret;
}
bool ng_vid_driver_v4l::setformat( ng_video_fmt *fmt )
{
bool rc = false;
#ifdef Barcode_DEBUG
fprintf(stderr,"v4l: setformat\n");
#endif
if (m_use_read) {
rc = read_setformat( fmt );
} else {
Q_ASSERT( false );
}
m_fmt = *fmt;
return rc;
}
bool ng_vid_driver_v4l::read_setformat( ng_video_fmt *fmt )
{
xioctl( m_fd, VIDIOCGCAP, &m_capability );
if (fmt->width > static_cast<uint>(m_capability.maxwidth))
fmt->width = m_capability.maxwidth;
if (fmt->height > static_cast<uint>(m_capability.maxheight))
fmt->height = m_capability.maxheight;
fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8;
xioctl( m_fd, VIDIOCGPICT, &m_pict );
m_pict.depth = ng_vfmt_to_depth[fmt->fmtid];
m_pict.palette = GETELEM(format2palette,fmt->fmtid,0);
m_pict.brightness = 20000;
if (-1 == xioctl( m_fd, VIDIOCSPICT, &m_pict ))
return false;
xioctl( m_fd, VIDIOCGWIN, &m_win);
m_win.width = m_fmt.width;
m_win.height = m_fmt.height;
m_win.clipcount = 0;
if (-1 == xioctl( m_fd, VIDIOCSWIN, &m_win ))
return false;
return true;
}
TQByteArray* ng_vid_driver_v4l::getimage2()
{
#ifdef Barcode_DEBUG
myDebug() << "v4l: getimage2" << endl;
#endif
return read_getframe2();
}
TQByteArray* ng_vid_driver_v4l::read_getframe2()
{
TQByteArray* buf;
int size;
size = m_fmt.bytesperline * m_fmt.height;
buf = new TQByteArray( size );
if (size != read( m_fd, buf->data(), size )) {
delete( buf );
return 0;
}
return buf;
}
#define GRAY(val) barcodeRecognition::ng_yuv_gray[val]
#define RED(gray,red) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_red[red] ]
#define GREEN(gray,red,blue) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_g1[red] + \
barcodeRecognition::ng_yuv_g2[blue] ]
#define BLUE(gray,blue) ng_clip[ CLIP + gray + barcodeRecognition::ng_yuv_blue[blue] ]
void barcodeRecognition::yuv420p_to_rgb24::frame( TQByteArray *out, const TQByteArray *in, const ng_video_fmt fmt )
{
unsigned char *y, *u, *v, *d;
unsigned char *us,*vs;
unsigned char *dp;
unsigned int i,j;
int gray;
dp = (unsigned char*)out->data();
y = (unsigned char*)in->data();
u = y + fmt.width * fmt.height;
v = u + fmt.width * fmt.height / 4;
for (i = 0; i < fmt.height; i++) {
d = dp;
us = u; vs = v;
for (j = 0; j < fmt.width; j+= 2) {
gray = GRAY(*y);
*(d++) = RED(gray,*v);
*(d++) = GREEN(gray,*v,*u);
*(d++) = BLUE(gray,*u);
y++;
gray = GRAY(*y);
*(d++) = RED(gray,*v);
*(d++) = GREEN(gray,*v,*u);
*(d++) = BLUE(gray,*u);
y++; u++; v++;
}
if (0 == (i % 2)) {
u = us; v = vs;
}
dp += 3*fmt.width;
}
}