1 /*
2  *  zvbi-decode -- Decode sliced VBI data using low-level
3  *		   libzvbi functions
4  *
5  *  Copyright (C) 2004, 2006, 2007 Michael H. Schimek
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  *  MA 02110-1301, USA.
21  */
22 
23 /* $Id: decode.c,v 1.36 2009/12/14 23:43:52 mschimek Exp $ */
24 
25 /* For libzvbi version 0.2.x / 0.3.x. */
26 
27 #undef NDEBUG
28 
29 #ifdef HAVE_CONFIG_H
30 #  include "config.h"
31 #endif
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <locale.h>
38 #include <assert.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <time.h>
42 #ifdef HAVE_GETOPT_LONG
43 #  include <getopt.h>
44 #endif
45 
46 #include "src/version.h"
47 #if 2 == VBI_VERSION_MINOR
48 #  include "src/bcd.h"
49 #  include "src/cc608_decoder.h"
50 #  include "src/conv.h"
51 #  include "src/dvb_demux.h"
52 #  include "src/hamm.h"
53 #  include "src/idl_demux.h"
54 #  include "src/lang.h"
55 #  include "src/packet-830.h"
56 #  include "src/pfc_demux.h"
57 #  include "src/vps.h"
58 #  include "src/xds_demux.h"
59 #elif 3 == VBI_VERSION_MINOR
60 #  include "src/zvbi.h"
61 #  include "src/misc.h"		/* _vbi_to_ascii() */
62 #else
63 #  error VBI_VERSION_MINOR == ?
64 #endif
65 
66 #include "sliced.h"
67 
68 #undef _
69 #define _(x) x /* i18n TODO */
70 
71 /* Will be installed one day. */
72 #define PROGRAM_NAME "zvbi-decode"
73 
74 static const char *		option_in_file_name;
75 static enum file_format		option_in_file_format;
76 static unsigned int		option_in_ts_pid;
77 
78 static vbi_pgno		option_pfc_pgno;
79 static unsigned int		option_pfc_stream;
80 
81 static vbi_bool		option_decode_ttx;
82 static vbi_bool		option_decode_8301;
83 static vbi_bool		option_decode_8302;
84 static vbi_bool		option_decode_caption;
85 static vbi_bool		option_decode_xds;
86 static vbi_bool		option_decode_idl;
87 static vbi_bool		option_decode_vps;
88 static vbi_bool		option_decode_vps_other;
89 static vbi_bool		option_decode_wss;
90 
91 static vbi_bool		option_dump_network;
92 static vbi_bool		option_dump_hex;
93 static vbi_bool		option_dump_bin;
94 static vbi_bool		option_dump_time;
95 static double			option_metronome_tick;
96 
97 static vbi_pgno		option_pfc_pgno	= 0;
98 static unsigned int		option_pfc_stream = 0;
99 
100 static unsigned int		option_idl_channel = 0;
101 static unsigned int		option_idl_address = 0;
102 
103 static struct stream *		rst;
104 
105 static vbi_pfc_demux *		pfc;
106 static vbi_idl_demux *		idl;
107 static vbi_xds_demux *		xds;
108 
109 extern void
110 _vbi_pfc_block_dump		(const vbi_pfc_block *	pb,
111 				 FILE *			fp,
112 				 vbi_bool		binary);
113 
114 static vbi_bool
xds_cb(vbi_xds_demux * xd,const vbi_xds_packet * xp,void * user_data)115 xds_cb				(vbi_xds_demux *	xd,
116 				 const vbi_xds_packet *	xp,
117 				 void *			user_data)
118 {
119 	xd = xd; /* unused */
120 	user_data = user_data;
121 
122 	_vbi_xds_packet_dump (xp, stdout);
123 
124 	return TRUE; /* no errors */
125 }
126 
127 static void
caption(const uint8_t buffer[2],unsigned int line)128 caption				(const uint8_t		buffer[2],
129 				 unsigned int		line)
130 {
131 	if (option_decode_xds && 284 == line) {
132 		if (!vbi_xds_demux_feed (xds, buffer)) {
133 			printf ("Parity error in XDS data.\n");
134 		}
135 	}
136 
137 	if (option_decode_caption
138 	    && (21 == line || 284 == line /* NTSC */
139 		|| 22 == line /* PAL? */)) {
140 
141 		printf ("CC line=%3u ", line);
142 		_vbi_cc608_dump (stdout, buffer[0], buffer[1]);
143 	}
144 }
145 
146 #if 3 == VBI_VERSION_MINOR /* XXX port me back */
147 
148 static void
dump_cni(vbi_cni_type type,unsigned int cni)149 dump_cni			(vbi_cni_type		type,
150 				 unsigned int		cni)
151 {
152 	vbi_network nk;
153 	vbi_bool success;
154 
155 	if (!option_dump_network)
156 		return;
157 
158 	success = vbi_network_init (&nk);
159 	if (!success)
160 		no_mem_exit ();
161 
162 	success = vbi_network_set_cni (&nk, type, cni);
163 	if (!success)
164 		no_mem_exit ();
165 
166 	_vbi_network_dump (&nk, stdout);
167 	putchar ('\n');
168 
169 	vbi_network_destroy (&nk);
170 }
171 
172 #endif /* 3 == VBI_VERSION_MINOR */
173 
174 static void
dump_bytes(const uint8_t * buffer,unsigned int n_bytes)175 dump_bytes			(const uint8_t *	buffer,
176 				 unsigned int		n_bytes)
177 {
178 	unsigned int j;
179 
180 	if (option_dump_bin) {
181 		fwrite (buffer, 1, n_bytes, stdout);
182 		return;
183 	}
184 
185 	if (option_dump_hex) {
186 		for (j = 0; j < n_bytes; ++j)
187 			printf ("%02x ", buffer[j]);
188 	}
189 
190 	putchar ('>');
191 
192 	for (j = 0; j < n_bytes; ++j) {
193 		/* Not all Teletext characters are representable
194 		   in ASCII or even UTF-8, but at this stage we don't
195 		   know the Teletext code page for a proper conversion. */
196 		char c = _vbi_to_ascii (buffer[j]);
197 
198 		putchar (c);
199 	}
200 
201 	puts ("<");
202 }
203 
204 static void
packet_8301(const uint8_t buffer[42],unsigned int designation)205 packet_8301			(const uint8_t		buffer[42],
206 				 unsigned int		designation)
207 {
208 	unsigned int cni;
209 	time_t time;
210 	int gmtoff;
211 	struct tm tm;
212 
213 	if (!option_decode_8301)
214 		return;
215 
216 	if (!vbi_decode_teletext_8301_cni (&cni, buffer)) {
217 		printf ("Error in Teletext "
218 			"packet 8/30 format 1 CNI.\n");
219 		return;
220 	}
221 
222 	if (!vbi_decode_teletext_8301_local_time (&time, &gmtoff, buffer)) {
223 		printf ("Error in Teletext "
224 			"packet 8/30 format 1 local time.\n");
225 		return;
226 	}
227 
228 	printf ("Teletext packet 8/30/%u cni=%x time=%u gmtoff=%d ",
229 		designation, cni, (unsigned int) time, gmtoff);
230 
231 	gmtime_r (&time, &tm);
232 
233 	printf ("(%4u-%02u-%02u %02u:%02u:%02u UTC)\n",
234 		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
235 		tm.tm_hour, tm.tm_min, tm.tm_sec);
236 
237 #if 3 == VBI_VERSION_MINOR
238 	if (0 != cni)
239 		dump_cni (VBI_CNI_TYPE_8301, cni);
240 #endif
241 }
242 
243 static void
packet_8302(const uint8_t buffer[42],unsigned int designation)244 packet_8302			(const uint8_t		buffer[42],
245 				 unsigned int		designation)
246 {
247 	unsigned int cni;
248 	vbi_program_id pi;
249 
250 	if (!option_decode_8302)
251 		return;
252 
253 	if (!vbi_decode_teletext_8302_cni (&cni, buffer)) {
254 		printf ("Error in Teletext "
255 			"packet 8/30 format 2 CNI.\n");
256 		return;
257 	}
258 
259 	if (!vbi_decode_teletext_8302_pdc (&pi, buffer)) {
260 		printf ("Error in Teletext "
261 			"packet 8/30 format 2 PDC data.\n");
262 		return;
263 	}
264 
265 	printf ("Teletext packet 8/30/%u cni=%x ", designation, cni);
266 
267 	_vbi_program_id_dump (&pi, stdout);
268 
269 	putchar ('\n');
270 
271 #if 3 == VBI_VERSION_MINOR
272 	if (0 != pi.cni)
273 		dump_cni (pi.cni_type, pi.cni);
274 #endif
275 }
276 
277 static vbi_bool
page_function_clear_cb(vbi_pfc_demux * dx,void * user_data,const vbi_pfc_block * block)278 page_function_clear_cb		(vbi_pfc_demux *	dx,
279 				 void *			user_data,
280 		                 const vbi_pfc_block *	block)
281 {
282 	dx = dx; /* unused */
283 	user_data = user_data;
284 
285 	_vbi_pfc_block_dump (block, stdout, option_dump_bin);
286 
287 	return TRUE;
288 }
289 
290 static vbi_bool
idl_format_a_cb(vbi_idl_demux * idl,const uint8_t * buffer,unsigned int n_bytes,unsigned int flags,void * user_data)291 idl_format_a_cb			(vbi_idl_demux *	idl,
292 				 const uint8_t *	buffer,
293 				 unsigned int		n_bytes,
294 				 unsigned int		flags,
295 				 void *			user_data)
296 {
297 	idl = idl;
298 	user_data = user_data;
299 
300 	if (!option_dump_bin) {
301 		printf ("IDL-A%s%s ",
302 			(flags & VBI_IDL_DATA_LOST) ? " <data lost>" : "",
303 			(flags & VBI_IDL_DEPENDENT) ? " <dependent>" : "");
304 	}
305 
306 	dump_bytes (buffer, n_bytes);
307 
308 	return TRUE;
309 }
310 
311 static void
packet_idl(const uint8_t buffer[42],unsigned int channel)312 packet_idl			(const uint8_t		buffer[42],
313 				 unsigned int		channel)
314 {
315 	int pa; /* packet address */
316 	int ft; /* format type */
317 
318 	printf ("IDL ch=%u ", channel);
319 
320 	switch (channel) {
321 	case 0:
322 		assert (0);
323 
324 	case 4:
325 	case 12:
326 		printf ("(Low bit rate audio) ");
327 
328 		dump_bytes (buffer, 42);
329 
330 		break;
331 
332 	case 5:
333 	case 6:
334 	case 13:
335 	case 14:
336 		pa = vbi_unham8 (buffer[3]);
337 		pa |= vbi_unham8 (buffer[4]) << 4;
338 		pa |= vbi_unham8 (buffer[5]) << 8;
339 
340 		if (pa < 0) {
341 			printf ("Hamming error in Datavideo "
342 				"packet-address byte.\n");
343 			return;
344 		}
345 
346 		printf ("(Datavideo) pa=0x%x ", pa);
347 
348 		dump_bytes (buffer, 42);
349 
350 		break;
351 
352 	case 8:
353 	case 9:
354 	case 10:
355 	case 11:
356 	case 15:
357 		ft = vbi_unham8 (buffer[2]);
358 		if (ft < 0) {
359 			printf ("Hamming error in IDL format "
360 				"A or B format-type byte.\n");
361 			return;
362 		}
363 
364 		if (0 == (ft & 1)) {
365 			int ial; /* interpretation and address length */
366 			unsigned int spa_length;
367 			int spa; /* service packet address */
368 			unsigned int i;
369 
370 			ial = vbi_unham8 (buffer[3]);
371 			if (ial < 0) {
372 				printf ("Hamming error in IDL format "
373 					"A interpretation-and-address-"
374 					"length byte.\n");
375 				return;
376 			}
377 
378 			spa_length = (unsigned int) ial & 7;
379 			if (7 == spa_length) {
380 				printf ("(Format A?) ");
381 				dump_bytes (buffer, 42);
382 				return;
383 			}
384 
385 			spa = 0;
386 
387 			for (i = 0; i < spa_length; ++i)
388 				spa |= vbi_unham8 (buffer[4 + i]) << (4 * i);
389 
390 			if (spa < 0) {
391 				printf ("Hamming error in IDL format "
392 					"A service-packet-address byte.\n");
393 				return;
394 			}
395 
396 			printf ("(Format A) spa=0x%x ", spa);
397 		} else if (1 == (ft & 3)) {
398 			int an; /* application number */
399 			int ai; /* application identifier */
400 
401 			an = (ft >> 2);
402 
403 			ai = vbi_unham8 (buffer[3]);
404 			if (ai < 0) {
405 				printf ("Hamming error in IDL format "
406 					"B application-number byte.\n");
407 				return;
408 			}
409 
410 			printf ("(Format B) an=%d ai=%d ", an, ai);
411 		}
412 
413 		dump_bytes (buffer, 42);
414 
415 		break;
416 
417 	default:
418 		dump_bytes (buffer, 42);
419 
420 		break;
421 	}
422 }
423 
424 static void
teletext(const uint8_t buffer[42],unsigned int line)425 teletext			(const uint8_t		buffer[42],
426 				 unsigned int		line)
427 {
428 	int pmag;
429 	unsigned int magazine;
430 	unsigned int packet;
431 
432 	if (NULL != pfc) {
433 		if (!vbi_pfc_demux_feed (pfc, buffer)) {
434 			printf ("Error in Teletext "
435 				"PFC packet.\n");
436 			return;
437 		}
438 	}
439 
440 	if (NULL != idl) {
441 		if (!vbi_idl_demux_feed (idl, buffer)) {
442 			printf ("Error in Teletext "
443 				"IDL packet.\n");
444 			return;
445 		}
446 	}
447 
448 	if (!(option_decode_ttx |
449 	      option_decode_8301 |
450 	      option_decode_8302 |
451 	      option_decode_idl))
452 		return;
453 
454 	pmag = vbi_unham16p (buffer);
455 	if (pmag < 0) {
456 		printf ("Hamming error in Teletext "
457 			"packet number.\n");
458 		return;
459 	}
460 
461 	magazine = pmag & 7;
462 	if (0 == magazine)
463 		magazine = 8;
464 
465 	packet = pmag >> 3;
466 
467 	if (8 == magazine && 30 == packet) {
468 		int designation;
469 
470 		designation = vbi_unham8 (buffer[2]);
471 		if (designation < 0 ) {
472 			printf ("Hamming error in Teletext "
473 				"packet 8/30 designation byte.\n");
474 			return;
475 		}
476 
477 		if (designation >= 0 && designation <= 1) {
478 			packet_8301 (buffer, designation);
479 			return;
480 		}
481 
482 		if (designation >= 2 && designation <= 3) {
483 			packet_8302 (buffer, designation);
484 			return;
485 		}
486 	}
487 
488 	if (30 == packet || 31 == packet) {
489 		if (option_decode_idl) {
490 #if 1
491 			packet_idl (buffer, pmag & 15);
492 #else
493 			printf ("Teletext IDL packet %u/%2u ",
494 				magazine, packet);
495 			dump_bytes (buffer, /* n_bytes */ 42);
496 #endif
497 			return;
498 		}
499 	}
500 
501 	if (option_decode_ttx) {
502 		printf ("Teletext line=%3u %x/%2u ",
503 			line, magazine, packet);
504 		dump_bytes (buffer, /* n_bytes */ 42);
505 		return;
506 	}
507 }
508 
509 static void
vps(const uint8_t buffer[13],unsigned int line)510 vps				(const uint8_t		buffer[13],
511 				 unsigned int		line)
512 {
513 	if (option_decode_vps) {
514 		unsigned int cni;
515 #if 1
516 		vbi_program_id pi;
517 #endif
518 		if (option_dump_bin) {
519 			printf ("VPS line=%3u ", line);
520 			fwrite (buffer, 1, 13, stdout);
521 			fflush (stdout);
522 			return;
523 		}
524 
525 		if (!vbi_decode_vps_cni (&cni, buffer)) {
526 			printf ("Error in VPS packet CNI.\n");
527 			return;
528 		}
529 
530 		if (!vbi_decode_vps_pdc (&pi, buffer)) {
531 			printf ("Error in VPS packet PDC data.\n");
532 			return;
533 		}
534 
535 		printf ("VPS line=%3u ", line);
536 
537 		_vbi_program_id_dump (&pi, stdout);
538 
539 		putchar ('\n');
540 
541 #if 3 == VBI_VERSION_MINOR
542 		if (0 != pi.cni)
543 			dump_cni (pi.cni_type, pi.cni);
544 #endif
545 	}
546 
547 	if (option_decode_vps_other) {
548 		static char pr_label[2][20];
549 		static char label[2][20];
550 		static int l[2] = { 0, 0 };
551 		unsigned int i;
552 		int c;
553 
554 		i = (line != 16);
555 
556 		c = vbi_rev8 (buffer[1]);
557 
558 		if (c & 0x80) {
559 			label[i][l[i]] = 0;
560 			strcpy (pr_label[i], label[i]);
561 			l[i] = 0;
562 		}
563 
564 		label[i][l[i]] = _vbi_to_ascii (c);
565 
566 		l[i] = (l[i] + 1) % 16;
567 
568 		printf ("VPS line=%3u bytes 3-10: "
569 			"%02x %02x (%02x='%c') %02x %02x "
570 			"%02x %02x %02x %02x (\"%s\")\n",
571 			line,
572 			buffer[0], buffer[1],
573 			c, _vbi_to_ascii (c),
574 			buffer[2], buffer[3],
575 			buffer[4], buffer[5], buffer[6], buffer[7],
576 			pr_label[i]);
577 	}
578 }
579 
580 #if 3 == VBI_VERSION_MINOR /* XXX port me back */
581 
582 static void
wss_625(const uint8_t buffer[2])583 wss_625				(const uint8_t		buffer[2])
584 {
585 	if (option_decode_wss) {
586 		vbi_aspect_ratio ar;
587 
588 		if (!vbi_decode_wss_625 (&ar, buffer)) {
589 			printf ("Error in WSS packet.\n");
590 			return;
591 		}
592 
593 		fputs ("WSS ", stdout);
594 
595 		_vbi_aspect_ratio_dump (&ar, stdout);
596 
597 		putchar ('\n');
598 	}
599 }
600 
601 #endif /* 3 == VBI_VERSION_MINOR */
602 
603 static vbi_bool
decode_frame(const vbi_sliced * s,unsigned int n_lines,const uint8_t * raw,const vbi_sampling_par * sp,double sample_time,int64_t stream_time)604 decode_frame			(const vbi_sliced *	s,
605 				 unsigned int		n_lines,
606 				 const uint8_t *	raw,
607 				 const vbi_sampling_par *sp,
608 				 double			sample_time,
609 				 int64_t		stream_time)
610 {
611 	static double metronome = 0.0;
612 	static double last_sample_time = 0.0;
613 	static int64_t last_stream_time = 0;
614 
615 	raw = raw; /* unused */
616 	sp = sp;
617 
618 	if (option_dump_time || option_metronome_tick > 0.0) {
619 		/* Sample time: When we captured the data, in
620 		   		seconds since 1970-01-01 (gettimeofday()).
621 		   Stream time: For ATSC/DVB the Presentation Time Stamp.
622 				For analog the frame number multiplied by
623 				the nominal frame period (1/25 or
624 				1001/30000 s). Both given in 90 kHz units.
625 		   Note this isn't fully implemented yet. */
626 
627 		if (option_metronome_tick > 0.0) {
628 			printf ("ST %f (adv %+f, err %+f) PTS %"
629 				PRId64 " (adv %+" PRId64 ", err %+f)\n",
630 				sample_time, sample_time - last_sample_time,
631 				sample_time - metronome,
632 				stream_time, stream_time - last_stream_time,
633 				(double) stream_time - metronome);
634 
635 			metronome += option_metronome_tick;
636 		} else {
637 			printf ("ST %f (%+f) PTS %" PRId64 " (%+" PRId64 ")\n",
638 				sample_time, sample_time - last_sample_time,
639 				stream_time, stream_time - last_stream_time);
640 		}
641 
642 		last_sample_time = sample_time;
643 		last_stream_time = stream_time;
644 	}
645 
646 	while (n_lines > 0) {
647 		switch (s->id) {
648 		case VBI_SLICED_TELETEXT_B_L10_625:
649 		case VBI_SLICED_TELETEXT_B_L25_625:
650 		case VBI_SLICED_TELETEXT_B_625:
651 			teletext (s->data, s->line);
652 			break;
653 
654 		case VBI_SLICED_VPS:
655 		case VBI_SLICED_VPS_F2:
656 			vps (s->data, s->line);
657 			break;
658 
659 		case VBI_SLICED_CAPTION_625_F1:
660 		case VBI_SLICED_CAPTION_625_F2:
661 		case VBI_SLICED_CAPTION_625:
662 		case VBI_SLICED_CAPTION_525_F1:
663 		case VBI_SLICED_CAPTION_525_F2:
664 		case VBI_SLICED_CAPTION_525:
665 			caption (s->data, s->line);
666 			break;
667 
668 		case VBI_SLICED_WSS_625:
669 #if 3 == VBI_VERSION_MINOR /* XXX port me back */
670 			wss_625 (s->data);
671 #endif
672 			break;
673 
674 		case VBI_SLICED_WSS_CPR1204:
675 			break;
676 		}
677 
678 		++s;
679 		--n_lines;
680 	}
681 
682 	return TRUE;
683 }
684 
685 static void
usage(FILE * fp)686 usage				(FILE *			fp)
687 {
688 	/* FIXME Supposed to be localized but we can't use #ifs
689 	   within the _() macro. */
690 	fprintf (fp, "\
691 %s %s -- Low-level VBI decoder\n\n\
692 Copyright (C) 2004, 2006, 2007 Michael H. Schimek\n\
693 This program is licensed under GPLv2 or later. NO WARRANTIES.\n\n\
694 Usage: %s [options] < sliced VBI data\n\
695 -h | --help | --usage  Print this message and exit\n\
696 -q | --quiet           Suppress progress and error messages\n\
697 -V | --version         Print the program version and exit\n\
698 Input options:\n\
699 -i | --input name      Read the VBI data from this file instead of\n\
700                        standard input\n\
701 -P | --pes             Source is a DVB PES stream\n\
702 -T | --ts pid          Source is a DVB TS stream\n\
703 Decoding options:\n\
704 -1 | --8301            Teletext packet 8/30 format 1 (local time)\n\
705 -2 | --8302            Teletext packet 8/30 format 2 (PDC)\n\
706 -c | --cc              Closed Caption\n\
707 -j | --idl             Any Teletext IDL packets (M/30, M/31)\n\
708 -t | --ttx             Decode any Teletext packet\n\
709 -v | --vps             Video Programming System (PDC)\n"
710 #if 3 == VBI_VERSION_MINOR /* XXX port me back */
711 "-w | --wss             Wide Screen Signalling\n"
712 #endif
713 "-x | --xds             Decode eXtended Data Service (NTSC line 284)\n\
714 -a | --all             Everything above, e.g.\n\
715                        -i     decode IDL packets\n\
716                        -a     decode everything\n\
717                        -a -i  everything except IDL\n\
718 -l | --idl-ch N\n\
719 -d | --idl-addr NNN    Decode Teletext IDL format A data from channel N,\n\
720                        service packet address NNN (default 0)\n\
721 -r | --vps-other       Decode VPS data unrelated to PDC\n\
722 -p | --pfc-pgno NNN\n\
723 -s | --pfc-stream NN   Decode Teletext Page Function Clear data\n\
724                        from page NNN (for example 1DF), stream NN\n\
725                        (default 0)\n\
726 Modifying options:\n\
727 -e | --hex             With -t dump packets in hex and ASCII,\n\
728                          otherwise only ASCII\n\
729 -n | --network         With -1, -2, -v decode CNI and print\n\
730                          available information about the network\n\
731 -b | --bin             With -t, -p, -v dump data in binary format\n\
732                          instead of ASCII\n\
733 -m | --time            Dump capture timestamps\n\
734 -M | --metronome tick  Compare timestamps against a metronome advancing\n\
735                        by tick seconds per frame\n\
736 ",
737 		 PROGRAM_NAME, VERSION, program_invocation_name);
738 }
739 
740 static const char
741 short_options [] = "12abcd:ehi:jl:mnp:qrs:tvwxM:PT:V";
742 
743 #ifdef HAVE_GETOPT_LONG
744 static const struct option
745 long_options [] = {
746 	{ "8301",	no_argument,		NULL,		'1' },
747 	{ "8302",	no_argument,		NULL,		'2' },
748 	{ "all",	no_argument,		NULL,		'a' },
749 	{ "bin",	no_argument,		NULL,		'b' },
750 	{ "cc",		no_argument,		NULL,		'c' },
751 	{ "idl-addr",	required_argument,	NULL,		'd' },
752 	{ "hex",	no_argument,		NULL,		'e' },
753 	{ "help",	no_argument,		NULL,		'h' },
754 	{ "usage",	no_argument,		NULL,		'h' },
755 	{ "input",	required_argument,	NULL,		'i' },
756 	{ "idl",	no_argument,		NULL,		'j' },
757 	{ "idl-ch",	required_argument,	NULL,		'l' },
758 	{ "time",	no_argument,		NULL,		'm' },
759 	{ "network",	no_argument,		NULL,		'n' },
760 	{ "pfc-pgno",	required_argument,	NULL,		'p' },
761 	{ "quiet",	no_argument,		NULL,		'q' },
762 	{ "vps-other",	no_argument,		NULL,		'r' },
763 	{ "pfc-stream",	required_argument,	NULL,		's' },
764 	{ "ttx",	no_argument,		NULL,		't' },
765 	{ "vps",	no_argument,		NULL,		'v' },
766 	{ "wss",	no_argument,		NULL,		'w' },
767 	{ "xds",	no_argument,		NULL,		'x' },
768 	{ "metronome",	required_argument,	NULL,		'M' },
769 	{ "pes",	no_argument,		NULL,		'P' },
770 	{ "ts",		required_argument,	NULL,		'T' },
771 	{ "version",	no_argument,		NULL,		'V' },
772 	{ NULL, 0, 0, 0 }
773 };
774 #else
775 #  define getopt_long(ac, av, s, l, i) getopt(ac, av, s)
776 #endif
777 
778 static int			option_index;
779 
780 int
main(int argc,char ** argv)781 main				(int			argc,
782 				 char **		argv)
783 {
784 	init_helpers (argc, argv);
785 
786 	option_in_file_format = FILE_FORMAT_SLICED;
787 
788 	for (;;) {
789 		int c;
790 
791 		c = getopt_long (argc, argv, short_options,
792 				 long_options, &option_index);
793 		if (-1 == c)
794 			break;
795 
796 		switch (c) {
797 		case 0: /* getopt_long() flag */
798 			break;
799 
800 		case '1':
801 			option_decode_8301 ^= TRUE;
802 			break;
803 
804 		case '2':
805 			option_decode_8302 ^= TRUE;
806 			break;
807 
808 		case 'a':
809 			option_decode_ttx = TRUE;
810 			option_decode_8301 = TRUE;
811 			option_decode_8302 = TRUE;
812 			option_decode_caption = TRUE;
813 			option_decode_idl = TRUE;
814 			option_decode_vps = TRUE;
815 			option_decode_wss = TRUE;
816 			option_decode_xds = TRUE;
817 			option_pfc_pgno = 0x1DF;
818 			break;
819 
820 		case 'b':
821 			option_dump_bin ^= TRUE;
822 			break;
823 
824 		case 'c':
825 			option_decode_caption ^= TRUE;
826 			break;
827 
828 		case 'd':
829 			assert (NULL != optarg);
830 			option_idl_address = strtol (optarg, NULL, 0);
831 			break;
832 
833 		case 'e':
834 			option_dump_hex ^= TRUE;
835 			break;
836 
837 		case 'h':
838 			usage (stdout);
839 			exit (EXIT_SUCCESS);
840 
841 		case 'i':
842 			assert (NULL != optarg);
843 			option_in_file_name = optarg;
844 			break;
845 
846 		case 'j':
847 			option_decode_idl ^= TRUE;
848 			break;
849 
850 		case 'l':
851 			assert (NULL != optarg);
852 			option_idl_channel = strtol (optarg, NULL, 0);
853 			break;
854 
855 		case 'm':
856 			option_dump_time ^= TRUE;
857 			break;
858 
859 		case 'n':
860 			option_dump_network ^= TRUE;
861 			break;
862 
863 		case 'p':
864 			assert (NULL != optarg);
865 			option_pfc_pgno = strtol (optarg, NULL, 16);
866 			break;
867 
868 		case 'q':
869 			parse_option_quiet ();
870 			break;
871 
872 		case 'r':
873 			option_decode_vps_other ^= TRUE;
874 			break;
875 
876 		case 's':
877 			assert (NULL != optarg);
878 			option_pfc_stream = strtol (optarg, NULL, 0);
879 			break;
880 
881 		case 't':
882 			option_decode_ttx ^= TRUE;
883 			break;
884 
885 		case 'v':
886 			option_decode_vps ^= TRUE;
887 			break;
888 
889 		case 'w':
890 			option_decode_wss ^= TRUE;
891 			break;
892 
893 		case 'x':
894 			option_decode_xds ^= TRUE;
895 			break;
896 
897 		case 'M':
898 			assert (NULL != optarg);
899 			option_metronome_tick = strtod (optarg, NULL);
900 			break;
901 
902 		case 'P':
903 			option_in_file_format = FILE_FORMAT_DVB_PES;
904 			break;
905 
906 		case 'T':
907 			option_in_ts_pid = parse_option_ts ();
908 			option_in_file_format = FILE_FORMAT_DVB_TS;
909 			break;
910 
911 		case 'V':
912 			printf (PROGRAM_NAME " " VERSION "\n");
913 			exit (EXIT_SUCCESS);
914 
915 		default:
916 			usage (stderr);
917 			exit (EXIT_FAILURE);
918 		}
919 	}
920 
921 	if (0 != option_pfc_pgno) {
922 		pfc = vbi_pfc_demux_new (option_pfc_pgno,
923 					 option_pfc_stream,
924 					 page_function_clear_cb,
925 					 /* user_data */ NULL);
926 		if (NULL == pfc)
927 			no_mem_exit ();
928 	}
929 
930 	if (0 != option_idl_channel) {
931 		idl = vbi_idl_a_demux_new (option_idl_channel,
932 					   option_idl_address,
933 					   idl_format_a_cb,
934 					   /* user_data */ NULL);
935 		if (NULL == idl)
936 			no_mem_exit ();
937 	}
938 
939 	if (option_decode_xds) {
940 		xds = vbi_xds_demux_new (xds_cb,
941 					 /* used_data */ NULL);
942 		if (NULL == xds)
943 			no_mem_exit ();
944 	}
945 
946 	rst = read_stream_new (option_in_file_name,
947 			       option_in_file_format,
948 			       option_in_ts_pid,
949 			       decode_frame);
950 
951 	stream_loop (rst);
952 
953 	stream_delete (rst);
954 	rst = NULL;
955 
956 	error_msg (_("End of stream."));
957 
958 	vbi_xds_demux_delete (xds);
959 	xds = NULL;
960 
961 	vbi_idl_demux_delete (idl);
962 	idl = NULL;
963 
964 	vbi_pfc_demux_delete (pfc);
965 	pfc = NULL;
966 
967 	exit (EXIT_SUCCESS);
968 }
969