1 /* qv4l2: a control panel controlling v4l2 devices.
2 *
3 * Copyright (C) 2012 Hans Verkuil <hverkuil@xs4all.nl>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20
21 #include <stdint.h>
22 #include "vbi-tab.h"
23 #include <QTableWidget>
24
25 #include <stdio.h>
26 #include <errno.h>
27
VbiTab(QWidget * parent)28 VbiTab::VbiTab(QWidget *parent) :
29 QGridLayout(parent)
30 {
31 QStringList q;
32
33 m_tableF1 = new QTableWidget(1, 1, parent);
34 m_tableF2 = new QTableWidget(1, 1, parent);
35 q.append("Field 1");
36 m_tableF1->setHorizontalHeaderLabels(q);
37 q.clear();
38 q.append("Field 2");
39 m_tableF2->setHorizontalHeaderLabels(q);
40 q.clear();
41 q.append("Line");
42 m_tableF1->setVerticalHeaderLabels(q);
43 m_tableF2->setVerticalHeaderLabels(q);
44 addWidget(m_tableF1, 0, 0);
45 addWidget(m_tableF2, 0, 1);
46 }
47
tableFormat()48 void VbiTab::tableFormat()
49 {
50 QTableWidgetItem *item;
51 QStringList q;
52 unsigned i;
53
54 m_tableF1->setRowCount(m_countF1);
55 m_tableF2->setRowCount(m_countF2);
56 for (i = 0; i < m_countF1; i++) {
57 item = new QTableWidgetItem();
58 item->setFlags(Qt::ItemIsEnabled);
59 m_tableF1->setItem(i, 0, item);
60 q.append("Line " + QString::number(i + m_startF1));
61 }
62 m_tableF1->setVerticalHeaderLabels(q);
63 q.clear();
64 for (i = 0; i < m_countF2; i++) {
65 item = new QTableWidgetItem();
66 item->setFlags(Qt::ItemIsEnabled);
67 m_tableF2->setItem(i, 0, item);
68 q.append("Line " + QString::number(i + m_startF2));
69 }
70 m_tableF2->setVerticalHeaderLabels(q);
71 }
72
rawFormat(const v4l2_vbi_format & fmt)73 void VbiTab::rawFormat(const v4l2_vbi_format &fmt)
74 {
75
76 m_countF1 = fmt.count[0];
77 m_countF2 = fmt.count[1];
78 m_startF1 = fmt.start[0];
79 m_startF2 = fmt.start[1];
80 m_offsetF2 = m_startF2 >= 313 ? 313 : 263;
81 tableFormat();
82 }
83
slicedFormat(const v4l2_sliced_vbi_format & fmt)84 void VbiTab::slicedFormat(const v4l2_sliced_vbi_format &fmt)
85 {
86 bool is_625 = fmt.service_set & V4L2_SLICED_VBI_625;
87
88 m_startF1 = is_625 ? 6 : 10;
89 m_startF2 = is_625 ? 319 : 273;
90 m_offsetF2 = is_625 ? 313 : 263;
91 m_countF1 = m_countF2 = is_625 ? 18 : 12;
92 tableFormat();
93 }
94
95 static const char *formats[] = {
96 "Full format 4:3, 576 lines",
97 "Letterbox 14:9 centre, 504 lines",
98 "Letterbox 14:9 top, 504 lines",
99 "Letterbox 16:9 centre, 430 lines",
100 "Letterbox 16:9 top, 430 lines",
101 "Letterbox > 16:9 centre",
102 "Full format 14:9 centre, 576 lines",
103 "Anamorphic 16:9, 576 lines"
104 };
105
106 static const char *subtitles[] = {
107 "none",
108 "in active image area",
109 "out of active image area",
110 "?"
111 };
112
decode_wss(QTableWidgetItem * item,const struct v4l2_sliced_vbi_data * s)113 static void decode_wss(QTableWidgetItem *item, const struct v4l2_sliced_vbi_data *s)
114 {
115 unsigned char parity;
116 int wss;
117
118 wss = s->data[0] | (s->data[1] << 8);
119
120 parity = wss & 15;
121 parity ^= parity >> 2;
122 parity ^= parity >> 1;
123
124 if (!(parity & 1)) {
125 item->setStatusTip("");
126 return;
127 }
128
129 item->setToolTip(QString(formats[wss & 7]) + "\n" +
130 ((wss & 0x10) ? "Film" : "Camera") + " mode\n" +
131 ((wss & 0x20) ? "Motion Adaptive ColorPlus" : "Standard") + " color coding\n" +
132 "Helper signals " + ((wss & 0x40) ? "" : "not ") + "present\n" +
133 ((wss & 0x0100) ? "Teletext subtitles\n" : "") +
134 "Open subtitles: " + subtitles[(wss >> 9) & 3] + "\n" +
135 ((wss & 0x0800) ? "Surround sound\n" : "") +
136 "Copyright " + ((wss & 0x1000) ? "asserted\n" : "unknown\n") +
137 "Copying " + ((wss & 0x2000) ? "restricted" : "not restricted"));
138 }
139
140 const uint8_t vbi_bit_reverse[256] = {
141 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
142 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
143 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
144 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
145 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
146 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
147 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
148 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
149 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
150 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
151 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
152 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
153 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
154 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
155 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
156 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
157 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
158 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
159 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
160 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
161 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
162 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
163 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
164 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
165 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
166 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
167 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
168 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
169 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
170 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
171 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
172 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
173 };
174
175 #define printable(c) ((((c) & 0x7F) < 0x20 || ((c) & 0x7F) > 0x7E) ? '.' : ((c) & 0x7F))
176
177 #define PIL(day, mon, hour, min) \
178 (((day) << 15) + ((mon) << 11) + ((hour) << 6) + ((min) << 0))
179
dump_pil(int pil)180 static QString dump_pil(int pil)
181 {
182 int day, mon, hour, min;
183 char buf[50];
184
185 day = pil >> 15;
186 mon = (pil >> 11) & 0xF;
187 hour = (pil >> 6) & 0x1F;
188 min = pil & 0x3F;
189
190 if (pil == PIL(0, 15, 31, 63))
191 return QString("PDC: Timer-control (no PDC)");
192 if (pil == PIL(0, 15, 30, 63))
193 return QString("PDC: Recording inhibit/terminate");
194 if (pil == PIL(0, 15, 29, 63))
195 return QString("PDC: Interruption");
196 if (pil == PIL(0, 15, 28, 63))
197 return QString("PDC: Continue");
198 if (pil == PIL(31, 15, 31, 63))
199 return QString("PDC: No time");
200 sprintf(buf, "PDC: 20XX-%02d-%02d %02d:%02d",
201 mon, day, hour, min);
202 return buf;
203 }
204
205 static const char *pcs_text[] = {
206 "unknown",
207 "mono",
208 "stereo",
209 "dual sound",
210 };
211
212 static const char *pty_text[] = {
213 /* 0x00 - 0x0f */
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216
217 /* 0x10 - 0x1f */
218 "movie (general)",
219 "detective/thriller",
220 "adventure/western/war",
221 "science fiction/fantasy/horror",
222 "comedy",
223 "soap/melodrama/folklore",
224 "romance",
225 "serious/classical/religious/historical drama",
226 "adult movie",
227 NULL, NULL, NULL, NULL, NULL, NULL,
228 "user defined",
229
230 /* 0x20 - 0x2f */
231 "news/current affairs (general)",
232 "news/weather report",
233 "news magazine",
234 "documentary",
235 "discussion/interview/debate",
236 "social/political issues/economics (general)",
237 "magazines/reports/documentary",
238 "economics/social advisory",
239 "remarkable people",
240 NULL, NULL, NULL, NULL, NULL, NULL,
241 "user defined",
242
243 /* 0x30 - 0x3f */
244 "show/game show (general)",
245 "game show/quiz/contest",
246 "variety show",
247 "talk show",
248 "leisure hobbies (general)",
249 "tourism/travel",
250 "handicraft",
251 "motoring",
252 "fitness and health",
253 "cooking",
254 "advertisement/shopping",
255 NULL, NULL, NULL, NULL,
256 "alarm/emergency identification",
257
258 /* 0x40 - 0x4f */
259 "sports (general)",
260 "special events (Olympic Games, World Cup etc.)",
261 "sports magazines",
262 "football/soccer",
263 "tennis/squash",
264 "team sports (excluding football)",
265 "athletics",
266 "motor sport",
267 "water sport",
268 "winter sports",
269 "equestrian",
270 "martial sports",
271 "local sports",
272 NULL, NULL,
273 "user defined",
274
275 /* 0x50 - 0x5f */
276 "children's/youth programmes (general)",
277 "pre-school children's programmes",
278 "entertainment programmes for 6 to 14",
279 "entertainment programmes for 10 to 16",
280 "informational/educational/school programmes",
281 "cartoons/puppets",
282 "education/science/factual topics (general)",
283 "nature/animals/environment",
284 "technology/natural sciences",
285 "medicine/physiology/psychology",
286 "foreign countries/expeditions",
287 "social/spiritual sciences",
288 "further education",
289 "languages",
290 NULL,
291 "user defined",
292
293 /* 0x60 - 0x6f */
294 "music/ballet/dance (general)",
295 "rock/pop",
296 "serious music/classical music",
297 "folk/traditional music",
298 "jazz",
299 "musical/opera",
300 "ballet",
301 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
302 "user defined",
303
304 /* 0x70 - 0x7f */
305 "arts/culture (without music, general)",
306 "performing arts",
307 "fine arts",
308 "religion",
309 "popular culture/traditional arts",
310 "literature",
311 "film/cinema",
312 "experimental film/video",
313 "broadcasting/press",
314 "new media",
315 "arts/culture magazines",
316 "fashion",
317 NULL, NULL, NULL,
318 "user defined",
319 };
320
decode_vps(QTableWidgetItem * item,const struct v4l2_sliced_vbi_data * s)321 static void decode_vps(QTableWidgetItem *item, const struct v4l2_sliced_vbi_data *s)
322 {
323 QString q;
324 int cni, pcs, pty, pil;
325 const char *pty_txt;
326 const unsigned char *buf = s->data;
327
328 pcs = buf[2] >> 6;
329
330 cni = +((buf[10] & 3) << 10)
331 + ((buf[11] & 0xC0) << 2)
332 + ((buf[8] & 0xC0) << 0)
333 + (buf[11] & 0x3F);
334
335 pil = ((buf[8] & 0x3F) << 14) + (buf[9] << 6) + (buf[10] >> 2);
336
337 pty = buf[12];
338 if (pty > 0x7f)
339 pty_txt = "service specific";
340 else if (pty < 0x10)
341 pty_txt = "undefined content";
342 else
343 pty_txt = pty_text[pty];
344 if (pty_txt == NULL)
345 pty_txt = "reserved for future use";
346
347 q = "CNI: 0x";
348 q += QString::number(cni, 16) + " PCS: " + pcs_text[pcs] + " PTY: " + pty_txt + "\n";
349 item->setToolTip(q + dump_pil(pil));
350 }
351
setItem(QTableWidgetItem * item,const v4l2_sliced_vbi_data * data)352 static void setItem(QTableWidgetItem *item, const v4l2_sliced_vbi_data *data)
353 {
354 switch (data->id) {
355 case V4L2_SLICED_TELETEXT_B:
356 item->setText("TXT");
357 break;
358 case V4L2_SLICED_VPS:
359 item->setText("VPS");
360 decode_vps(item, data);
361 break;
362 case V4L2_SLICED_CAPTION_525:
363 item->setText("CC");
364 break;
365 case V4L2_SLICED_WSS_625:
366 item->setText("WSS");
367 decode_wss(item, data);
368 break;
369 default:
370 item->setText("");
371 break;
372 }
373 }
374
slicedData(const v4l2_sliced_vbi_data * data,unsigned elems)375 void VbiTab::slicedData(const v4l2_sliced_vbi_data *data, unsigned elems)
376 {
377 char found[m_countF1 + m_countF2];
378
379 memset(found, 0, m_countF1 + m_countF2);
380 for (unsigned i = 0; i < elems; i++) {
381 QTableWidgetItem *item;
382
383 if (data[i].id == 0)
384 continue;
385 if (data[i].field == 0) {
386 if (data[i].line < m_startF1 ||
387 data[i].line >= m_startF1 + m_countF1)
388 continue;
389 item = m_tableF1->item(data[i].line - m_startF1, 0);
390 found[data[i].line - m_startF1] = 1;
391 } else {
392 if (data[i].line + m_offsetF2 < m_startF2 ||
393 data[i].line + m_offsetF2 >= m_startF2 + m_countF2)
394 continue;
395 item = m_tableF2->item(data[i].line + m_offsetF2 - m_startF2, 0);
396 found[data[i].line + m_offsetF2 - m_startF2 + m_countF1] = 1;
397 }
398 setItem(item, data + i);
399 }
400 for (unsigned i = 0; i < m_countF1 + m_countF2; i++) {
401 if (found[i])
402 continue;
403 QTableWidgetItem *item = (i < m_countF1) ?
404 m_tableF1->item(i, 0) : m_tableF2->item(i - m_countF1, 0);
405 item->setText("");
406 }
407 }
408