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