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