1 /*
2 * libzvbi -- Teletext Page Format Clear packet demultiplexer
3 *
4 * Copyright (C) 2003, 2004, 2007 Michael H. Schimek
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
20 */
21
22 /* $Id: pfc_demux.c,v 1.11 2013/07/10 11:37:33 mschimek Exp $ */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include "misc.h"
29 #include "hamm.h" /* vbi_iham8(), vbi_iham16p() */
30 #include "pfc_demux.h"
31
32 #define BLOCK_SEPARATOR 0x0C
33 #define FILLER_BYTE 0x03
34
35 /** @internal */
36 void
_vbi_pfc_block_dump(const vbi_pfc_block * pb,FILE * fp,vbi_bool binary)37 _vbi_pfc_block_dump (const vbi_pfc_block * pb,
38 FILE * fp,
39 vbi_bool binary)
40 {
41 assert (NULL != pb);
42 assert (NULL != fp);
43
44 fprintf (fp, "PFC pgno=%x stream=%u id=%u size=%u\n",
45 pb->pgno, pb->stream,
46 pb->application_id,
47 pb->block_size);
48
49 if (binary) {
50 fwrite (pb->block, sizeof (pb->block[0]), pb->block_size, fp);
51 } else {
52 unsigned int i;
53
54 for (i = 0; i < pb->block_size; ++i) {
55 fputc (_vbi_to_ascii (pb->block[i]), fp);
56
57 if ((i % 75) == 75)
58 fputc ('\n', fp);
59 }
60
61 if ((i % 75) != 75)
62 fputc ('\n', fp);
63 }
64 }
65
66 /**
67 * @param dx PFC demultiplexer context allocated with vbi_pfc_demux_new().
68 *
69 * Resets the PFC demux context, useful for example after a channel
70 * change.
71 */
72 void
vbi_pfc_demux_reset(vbi_pfc_demux * dx)73 vbi_pfc_demux_reset (vbi_pfc_demux * dx)
74 {
75 assert (NULL != dx);
76
77 dx->ci = 256; /* normally 0 ... 15 */
78 dx->packet = 256; /* normally 1 ... 25 */
79 dx->n_packets = 0; /* discard all */
80
81 dx->bi = 0; /* empty buffer */
82 dx->left = 0;
83
84 dx->block.application_id = (unsigned int) -1; /* expect SH next */
85 }
86
87 /** @internal */
88 vbi_bool
_vbi_pfc_demux_decode(vbi_pfc_demux * dx,const uint8_t buffer[42])89 _vbi_pfc_demux_decode (vbi_pfc_demux * dx,
90 const uint8_t buffer[42])
91 {
92 unsigned int col;
93 int bp;
94
95 bp = vbi_unham8 (buffer[2]) * 3;
96 if (bp < 0 || bp > 39) {
97 /* Invalid pointer or hamming error (-1). */
98 goto desynced;
99 }
100
101 col = 3;
102
103 while (col < 42) {
104 int bs;
105
106 if (dx->left > 0) {
107 unsigned int size;
108
109 size = MIN (dx->left, 42 - col);
110
111 memcpy (dx->block.block + dx->bi, buffer + col, size);
112
113 dx->bi += size;
114 dx->left -= size;
115
116 if (dx->left > 0) {
117 /* Packet done, block unfinished. */
118 return TRUE;
119 }
120
121 col += size;
122
123 if ((int) dx->block.application_id < 0) {
124 int sh; /* structure header */
125
126 sh = vbi_unham16p (dx->block.block)
127 + vbi_unham16p (dx->block.block + 2)
128 * 256;
129
130 if (sh < 0) {
131 /* Hamming error. */
132 goto desynced;
133 }
134
135 dx->block.application_id = sh & 0x1F;
136 dx->block.block_size = sh >> 5;
137
138 dx->bi = 0;
139 dx->left = dx->block.block_size;
140
141 continue;
142 } else {
143 if (!dx->callback (dx, dx->user_data,
144 &dx->block)) {
145 goto desynced;
146 }
147 }
148 }
149
150 if (col <= 3) {
151 if (bp >= 39) {
152 /* No new block starts in this packet. */
153 return TRUE;
154 }
155
156 col = bp + 4; /* 2 pmag, 1 bp, 1 bs */
157 bs = vbi_unham8 (buffer[col - 1]);
158 } else {
159 while (FILLER_BYTE ==
160 (bs = vbi_unham8 (buffer[col++]))) {
161 if (col >= 42) {
162 /* No more data in this packet. */
163 return TRUE;
164 }
165 }
166 }
167
168 if (BLOCK_SEPARATOR != bs) {
169 /* BP must point to a block separator. */
170 goto desynced;
171 }
172
173 /* First with application_id == -1 we read 4 bytes structure
174 header into block[], then with application_id >= 0
175 block_size data bytes. */
176
177 dx->bi = 0;
178 dx->left = 4;
179
180 dx->block.application_id = (unsigned int) -1;
181 }
182
183 return TRUE;
184
185 desynced:
186 /* Incorrectable error, discard current block. */
187 vbi_pfc_demux_reset (dx);
188
189 return FALSE;
190 }
191
192 /**
193 * @param dx PFC demultiplexer context allocated with vbi_pfc_demux_new().
194 * @param buffer Teletext packet (last 42 bytes, i. e. without clock
195 * run-in and framing code), as in struct vbi_sliced.
196 *
197 * This function takes a raw stream of Teletext packets, filters out the page
198 * and stream requested with vbi_pfc_demux_new() and assembles the
199 * data transmitted in this page in a buffer. When a data block is complete
200 * it calls the output function given to vbi_pfc_demux_new().
201 *
202 * @returns
203 * FALSE if the packet contained uncorrectable errors.
204 */
205 vbi_bool
vbi_pfc_demux_feed(vbi_pfc_demux * dx,const uint8_t buffer[42])206 vbi_pfc_demux_feed (vbi_pfc_demux * dx,
207 const uint8_t buffer[42])
208 {
209 int pmag;
210 vbi_pgno pgno;
211 vbi_subno subno;
212 unsigned int packet;
213
214 assert (NULL != dx);
215 assert (NULL != buffer);
216
217 /* Packet filter. */
218
219 if ((pmag = vbi_unham16p (buffer)) < 0)
220 goto desynced;
221
222 pgno = pmag & 7;
223 if (0 == pgno)
224 pgno = 0x800;
225 else
226 pgno <<= 8;
227
228 packet = pmag >> 3;
229
230 if (0 == packet) {
231 unsigned int stream;
232 unsigned int ci;
233
234 pgno |= vbi_unham16p (buffer + 2);
235 if (pgno < 0)
236 goto desynced;
237
238 if (pgno != dx->block.pgno) {
239 dx->n_packets = 0;
240 return TRUE;
241 }
242
243 subno = vbi_unham16p (buffer + 4)
244 + vbi_unham16p (buffer + 6) * 256;
245 if (subno < 0)
246 goto desynced;
247
248 stream = (subno >> 8) & 15;
249 if (stream != dx->block.stream) {
250 dx->n_packets = 0;
251 return TRUE;
252 }
253
254 ci = subno & 15;
255 if (ci != dx->ci) {
256 /* Page continuity lost, wait for new block. */
257 vbi_pfc_demux_reset (dx);
258 }
259
260 dx->ci = (ci + 1) & 15; /* next ci expected */
261
262 dx->packet = 1;
263 dx->n_packets = ((subno >> 4) & 7) + ((subno >> 9) & 0x18);
264
265 return TRUE;
266 } else {
267 /* In case 0 == C11 parallel page transmission. */
268 if ((pgno ^ dx->block.pgno) & 0xF00) {
269 /* Not dx->block.pgno. */
270 return TRUE;
271 }
272 }
273
274 if (0 == dx->n_packets) {
275 /* Not dx->block.pgno. */
276 return TRUE;
277 }
278
279 if (packet > 25) {
280 /* Stuffing packets, whatever. */
281 return TRUE;
282 }
283
284 if (packet != dx->packet
285 || packet > dx->n_packets) {
286 /* Packet continuity lost, wait for new
287 block and page header. */
288 vbi_pfc_demux_reset (dx);
289 return TRUE;
290 }
291
292 dx->packet = packet + 1; /* next packet expected */
293
294 /* Now the actual decoding. */
295
296 return _vbi_pfc_demux_decode (dx, buffer);
297
298 desynced:
299 /* Incorrectable error, discard current block. */
300 vbi_pfc_demux_reset (dx);
301
302 return FALSE;
303 }
304
305 /**
306 * @param dx PFC demultiplexer context allocated with vbi_pfc_demux_new().
307 * @param sliced Sliced VBI data.
308 * @param n_lines Number of lines in the @a sliced array.
309 *
310 * This function works like vbi_pfc_demux_feed() but operates
311 * on sliced VBI data and filters out @c VBI_SLICED_TELETEXT_B_625.
312 *
313 * @returns
314 * FALSE if any Teletext lines contained uncorrectable errors.
315 *
316 * @since 0.2.26
317 */
318 vbi_bool
vbi_pfc_demux_feed_frame(vbi_pfc_demux * dx,const vbi_sliced * sliced,unsigned int n_lines)319 vbi_pfc_demux_feed_frame (vbi_pfc_demux * dx,
320 const vbi_sliced * sliced,
321 unsigned int n_lines)
322 {
323 const vbi_sliced *end;
324
325 assert (NULL != dx);
326 assert (NULL != sliced);
327
328 for (end = sliced + n_lines; sliced < end; ++sliced) {
329 if (sliced->id & VBI_SLICED_TELETEXT_B_625) {
330 if (!vbi_pfc_demux_feed (dx, sliced->data))
331 return FALSE;
332 }
333 }
334
335 return TRUE;
336 }
337
338 /**
339 * @internal
340 */
341 void
_vbi_pfc_demux_destroy(vbi_pfc_demux * dx)342 _vbi_pfc_demux_destroy (vbi_pfc_demux * dx)
343 {
344 assert (NULL != dx);
345
346 CLEAR (*dx);
347 }
348
349 /**
350 * @internal
351 */
352 vbi_bool
_vbi_pfc_demux_init(vbi_pfc_demux * dx,vbi_pgno pgno,unsigned int stream,vbi_pfc_demux_cb * callback,void * user_data)353 _vbi_pfc_demux_init (vbi_pfc_demux * dx,
354 vbi_pgno pgno,
355 unsigned int stream,
356 vbi_pfc_demux_cb * callback,
357 void * user_data)
358 {
359 assert (NULL != dx);
360 assert (NULL != callback);
361
362 vbi_pfc_demux_reset (dx);
363
364 dx->callback = callback;
365 dx->user_data = user_data;
366
367 dx->block.pgno = pgno;
368 dx->block.stream = stream;
369
370 return TRUE;
371 }
372
373 /**
374 * @param dx PFC demultiplexer context allocated with
375 * vbi_pfc_demux_new(), can be @c NULL.
376 *
377 * Frees all resources associated with @a dx.
378 */
379 void
vbi_pfc_demux_delete(vbi_pfc_demux * dx)380 vbi_pfc_demux_delete (vbi_pfc_demux * dx)
381 {
382 if (NULL == dx)
383 return;
384
385 _vbi_pfc_demux_destroy (dx);
386
387 vbi_free (dx);
388 }
389
390 /**
391 * @param pgno Page to take PFC data from.
392 * @param stream PFC stream to be demultiplexed.
393 * @param callback Function to be called by vbi_pfc_demux_feed() when
394 * a new data block is available.
395 * @param user_data User pointer passed through to @a cb function.
396 *
397 * Allocates a new Page Function Clear (ETS 300 708 section 4)
398 * demultiplexer.
399 *
400 * @returns
401 * Pointer to newly allocated PFC demux context which must be
402 * freed with vbi_pfc_demux_delete() when done. @c NULL on failure
403 * (out of memory).
404 */
405 vbi_pfc_demux *
vbi_pfc_demux_new(vbi_pgno pgno,unsigned int stream,vbi_pfc_demux_cb * callback,void * user_data)406 vbi_pfc_demux_new (vbi_pgno pgno,
407 unsigned int stream,
408 vbi_pfc_demux_cb * callback,
409 void * user_data)
410 {
411 vbi_pfc_demux *dx;
412
413 if (!(dx = vbi_malloc (sizeof (*dx)))) {
414 return NULL;
415 }
416
417 if (!_vbi_pfc_demux_init (dx, pgno, stream,
418 callback, user_data)) {
419 vbi_free (dx);
420 dx = NULL;
421 }
422
423 return dx;
424 }
425
426 /*
427 Local variables:
428 c-set-style: K&R
429 c-basic-offset: 8
430 End:
431 */
432