• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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