1 /***************************************************************************
2     Copyright (C) 2007-2009 Sebastian Held <sebastian.held@gmx.de>
3  ***************************************************************************/
4 
5 /***************************************************************************
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or         *
8  *   modify it under the terms of the GNU General Public License as        *
9  *   published by the Free Software Foundation; either version 2 of        *
10  *   the License or (at your option) version 3 or any later version        *
11  *   accepted by the membership of KDE e.V. (or its successor approved     *
12  *   by the membership of KDE e.V.), which shall act as a proxy            *
13  *   defined in Section 14 of version 3 of the license.                    *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
22  *                                                                         *
23  ***************************************************************************/
24 
25 // uses code from v4lgrab.c         (c) Linux Kernel 2.6.30
26 
27 #include "barcode_v4l.h"
28 #include "../tellico_debug.h"
29 
30 #include <fcntl.h>              /* low-level i/o */
31 #include <errno.h>
32 #include <sys/ioctl.h>
33 
34 extern "C" {
35 #include <libv4l1.h>
36 }
37 
38 using barcodeRecognition::barcode_v4l;
39 
40 #define READ_VIDEO_PIXEL(buf, format, depth, r, g, b)                   \
41 {                                                                       \
42   switch (format)                                                 \
43   {                                                               \
44     case VIDEO_PALETTE_GREY:                                \
45       switch (depth)                                  \
46       {                                               \
47         case 4:                                 \
48         case 6:                                 \
49         case 8:                                 \
50           (r) = (g) = (b) = (*buf++ << 8);\
51           break;                          \
52                   \
53         case 16:                                \
54           (r) = (g) = (b) =               \
55             *((unsigned short *) buf);      \
56           buf += 2;                       \
57           break;                          \
58       }                                               \
59       break;                                          \
60                   \
61                   \
62     case VIDEO_PALETTE_RGB565:                              \
63     {                                                       \
64       unsigned short tmp = *(unsigned short *)buf;    \
65       (r) = tmp&0xF800;                               \
66       (g) = (tmp<<5)&0xFC00;                          \
67       (b) = (tmp<<11)&0xF800;                         \
68       buf += 2;                                       \
69     }                                                       \
70     break;                                                  \
71                   \
72     case VIDEO_PALETTE_RGB555:                              \
73       (r) = (buf[0]&0xF8)<<8;                         \
74       (g) = ((buf[0] << 5 | buf[1] >> 3)&0xF8)<<8;    \
75       (b) = ((buf[1] << 2 ) & 0xF8)<<8;               \
76       buf += 2;                                       \
77       break;                                          \
78                   \
79     case VIDEO_PALETTE_RGB24:                               \
80       (r) = buf[0] << 8; (g) = buf[1] << 8;           \
81       (b) = buf[2] << 8;                              \
82       buf += 3;                                       \
83       break;                                          \
84                   \
85     default:                                                \
86       fprintf(stderr,                                 \
87         "Format %d not yet supported\n",        \
88         format);                                \
89   }                                                               \
90 }
91 
barcode_v4l()92 barcode_v4l::barcode_v4l()
93 {
94   m_devname = QStringLiteral("/dev/video0");
95   m_grab_width = 640;
96   m_grab_height = 480;
97 
98   m_fd = -1;
99   m_buffer = nullptr;
100   m_image = nullptr;
101 
102   grab_init();
103 }
104 
~barcode_v4l()105 barcode_v4l::~barcode_v4l()
106 {
107   if (m_fd >= 0)
108     v4l1_close(m_fd);
109   if (m_buffer)
110     delete m_buffer;
111   if (m_image)
112     delete m_image;
113 }
114 
isOpen()115 bool barcode_v4l::isOpen()
116 {
117   return (m_fd >= 0);
118 }
119 
grab_one2()120 QImage barcode_v4l::grab_one2()
121 {
122   unsigned int bpp = 24, x, y;
123   unsigned int r = 0, g = 0, b = 0;
124   unsigned int src_depth = 16;
125   char *src = m_buffer->data();
126 
127   static int counter = 0; // adjustment disabled; set to e.g. 20 or 50 to enable the brightness adjustment
128 
129   if (!isOpen())
130     return QImage();
131 
132   v4l1_read(m_fd, m_buffer->data(), m_win.width * m_win.height * bpp);
133 
134   if (counter) {
135     long newbright;
136     int f;
137     counter--;
138     f = get_brightness_adj((unsigned char *)m_buffer->data(), m_win.width * m_win.height, &newbright);
139     if (f) {
140       m_pict.brightness += (newbright << 8);
141       myDebug() << "v4l: Adjusting brightness: new brightness " << m_pict.brightness;
142       if (v4l1_ioctl(m_fd, VIDIOCSPICT, &m_pict) == -1) {
143         myDebug() << "v4l: Cannot set brightness.";
144         counter = 0; // do not try again
145       }
146     } else
147       counter = 0; // do not try again
148   }
149 
150   if (m_pict.palette == VIDEO_PALETTE_RGB24) {
151     // optimized case
152     QRgb *scanline;
153     for (y = 0; y < m_win.height; ++y) {
154       scanline = reinterpret_cast<QRgb*>(m_image->scanLine(y));
155       for (x = 0; x < m_win.width; ++x) {
156         const char src1 = *(src++);
157         const char src2 = *(src++);
158         const char src3 = *(src++);
159         scanline[x] = qRgb(src1,src2,src3);
160       }
161     }
162   } else {
163     // generic case
164     for (y = 0; y < m_win.height; ++y) {
165       for (x = 0; x < m_win.width; ++x) {
166         READ_VIDEO_PIXEL(src, m_pict.palette, src_depth, r, g, b);
167         m_image->setPixel( x, y, qRgb(r>>8,g>>8,b>>8) );
168       }
169     }
170   }
171 
172   return *m_image;
173 }
174 
grab_init()175 bool barcode_v4l::grab_init()
176 {
177   m_fd = v4l1_open(m_devname.toLatin1().constData(), O_RDONLY);
178   if (m_fd < 0) {
179     myDebug() << "v4l: open " << m_devname << ": " << strerror(errno);
180     return false;
181   }
182 
183   if (v4l1_ioctl(m_fd, VIDIOCGCAP, &m_capability) < 0) {
184     myDebug() << "v4l: ioctl VIDIOCGCAP failed; " << m_devname << " not a video4linux device?";
185     v4l1_close(m_fd);
186     m_fd = -1;
187     return false;
188   }
189 
190   if (v4l1_ioctl(m_fd, VIDIOCGWIN, &m_win) < 0) {
191     myDebug() << "v4l: ioctl VIDIOCGWIN failed";
192     v4l1_close(m_fd);
193     m_fd = -1;
194     return false;
195   }
196 
197   if (v4l1_ioctl(m_fd, VIDIOCGPICT, &m_pict) < 0) {
198     myDebug() << "v4l: ioctl VIDIOCGPICT failed";
199     v4l1_close(m_fd);
200     m_fd = -1;
201     return false;
202   }
203 
204   if (m_capability.type & VID_TYPE_MONOCHROME) {
205     m_pict.depth=8;
206     m_pict.palette=VIDEO_PALETTE_GREY;    /* 8bit grey */
207     if (v4l1_ioctl(m_fd, VIDIOCSPICT, &m_pict) < 0) {
208       myDebug() << "v4l: Unable to find a supported capture format.";
209       v4l1_close(m_fd);
210       m_fd = -1;
211       return false;
212     }
213   } else {
214     m_pict.depth=24;
215     m_pict.palette=VIDEO_PALETTE_RGB24;
216     if (v4l1_ioctl(m_fd, VIDIOCSPICT, &m_pict) < 0) {
217       myDebug() << "v4l: Unable to find a supported capture format.";
218       v4l1_close(m_fd);
219       m_fd = -1;
220       return false;
221     }
222   }
223 
224   // check the values
225   video_picture temp;
226   v4l1_ioctl(m_fd, VIDIOCGPICT, &temp);
227   if ((temp.depth != m_pict.depth) || (temp.palette != m_pict.palette)) {
228     myDebug() << "v4l: Unable to find a supported capture format.";
229     v4l1_close(m_fd);
230     m_fd = -1;
231     return false;
232   }
233 
234   int bpp = 24;
235   m_buffer = new QByteArray;
236   m_buffer->reserve( m_win.width * m_win.height * bpp ); // FIXME! I think the example from the Linux kernel wastes memory here
237   m_image = new QImage( m_win.width, m_win.height, QImage::Format_RGB32 );
238 
239   return true;
240 }
241 
get_brightness_adj(unsigned char * image,long size,long * brightness)242 int barcode_v4l::get_brightness_adj(unsigned char *image, long size, long *brightness) {
243   long i, tot = 0;
244   for (i=0;i<size*3;i++) {
245     tot += image[i];
246   }
247   *brightness = (128 - tot/(size*3))/3;
248   return !((tot/(size*3)) >= 126 && (tot/(size*3)) <= 130);
249 }
250