1 /*
2  *  libzvbi -- Raw VBI sampling parameters
3  *
4  *  Copyright (C) 2000-2004 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: sampling_par.c,v 1.12 2013-08-28 14:45:00 mschimek Exp $ */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include <errno.h>
29 
30 #include "misc.h"
31 #include "raw_decoder.h"
32 #include "sampling_par.h"
33 #include "sliced.h"
34 
35 #  define vbi_pixfmt_bytes_per_pixel VBI_PIXFMT_BPP
36 #  define sp_sample_format sampling_format
37 
38 /**
39  * @addtogroup Sampling Raw VBI sampling
40  * @ingroup Raw
41  * @brief Raw VBI data sampling interface.
42  */
43 
44 /**
45  * @internal
46  * Compatibility.
47  */
48 vbi_videostd_set
_vbi_videostd_set_from_scanning(int scanning)49 _vbi_videostd_set_from_scanning (int scanning)
50 {
51   switch (scanning) {
52     case 525:
53       return VBI_VIDEOSTD_SET_525_60;
54 
55     case 625:
56       return VBI_VIDEOSTD_SET_625_50;
57 
58     default:
59       break;
60   }
61 
62   return 0;
63 }
64 
65 _vbi_inline vbi_bool
range_check(unsigned int start,unsigned int count,unsigned int min,unsigned int max)66 range_check (unsigned int start,
67     unsigned int count, unsigned int min, unsigned int max)
68 {
69   /* Check bounds and overflow. */
70   return (start >= min && (start + count) <= max && (start + count) >= start);
71 }
72 
73 /**
74  * @internal
75  * @param sp Sampling parameters to verify.
76  *
77  * @return
78  * TRUE if the sampling parameters are valid (as far as we can tell).
79  */
80 vbi_bool
_vbi_sampling_par_valid_log(const vbi_sampling_par * sp,_vbi_log_hook * log)81 _vbi_sampling_par_valid_log (const vbi_sampling_par * sp, _vbi_log_hook * log)
82 {
83   vbi_videostd_set videostd_set;
84   unsigned int bpp;
85 
86   assert (NULL != sp);
87 
88   switch (sp->sp_sample_format) {
89     case VBI_PIXFMT_YUV420:
90       /* This conflicts with the ivtv driver, which returns an
91          odd number of bytes per line.  The driver format is
92          _GREY but libzvbi 0.2 has no VBI_PIXFMT_Y8. */
93       break;
94 
95     default:
96       bpp = vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format);
97       if (0 != (sp->bytes_per_line % bpp))
98         goto bad_samples;
99       break;
100   }
101 
102   if (0 == sp->bytes_per_line)
103     goto no_samples;
104 
105   if (0 == sp->count[0]
106       && 0 == sp->count[1])
107     goto bad_range;
108 
109   videostd_set = _vbi_videostd_set_from_scanning (sp->scanning);
110 
111   if (VBI_VIDEOSTD_SET_525_60 & videostd_set) {
112     if (VBI_VIDEOSTD_SET_625_50 & videostd_set)
113       goto ambiguous;
114 
115     if (0 != sp->start[0]
116         && !range_check (sp->start[0], sp->count[0], 1, 262))
117       goto bad_range;
118 
119     if (0 != sp->start[1]
120         && !range_check (sp->start[1], sp->count[1], 263, 525))
121       goto bad_range;
122   } else if (VBI_VIDEOSTD_SET_625_50 & videostd_set) {
123     if (0 != sp->start[0]
124         && !range_check (sp->start[0], sp->count[0], 1, 311))
125       goto bad_range;
126 
127     if (0 != sp->start[1]
128         && !range_check (sp->start[1], sp->count[1], 312, 625))
129       goto bad_range;
130   } else {
131   ambiguous:
132     info (log, "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set);
133     return FALSE;
134   }
135 
136   if (sp->interlaced && (sp->count[0] != sp->count[1]
137           || 0 == sp->count[0])) {
138     info (log,
139         "Line counts %u, %u must be equal and "
140         "non-zero when raw VBI data is interlaced.",
141         sp->count[0], sp->count[1]);
142     return FALSE;
143   }
144 
145   return TRUE;
146 
147 no_samples:
148   info (log, "samples_per_line is zero.");
149   return FALSE;
150 
151 
152 bad_samples:
153   info (log,
154       "bytes_per_line value %u is no multiple of "
155       "the sample size %u.",
156       sp->bytes_per_line, vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format));
157   return FALSE;
158 
159 bad_range:
160   info (log,
161       "Invalid VBI scan range %u-%u (%u lines), "
162       "%u-%u (%u lines).",
163       sp->start[0], sp->start[0] + sp->count[0] - 1,
164       sp->count[0],
165       sp->start[1], sp->start[1] + sp->count[1] - 1, sp->count[1]);
166   return FALSE;
167 }
168 
169 static vbi_bool
_vbi_sampling_par_permit_service(const vbi_sampling_par * sp,const _vbi_service_par * par,unsigned int strict,_vbi_log_hook * log)170     _vbi_sampling_par_permit_service
171     (const vbi_sampling_par * sp,
172     const _vbi_service_par * par, unsigned int strict, _vbi_log_hook * log)
173 {
174   const unsigned int unknown = 0;
175   double signal;
176   unsigned int field;
177   unsigned int samples_per_line;
178   vbi_videostd_set videostd_set;
179 
180   assert (NULL != sp);
181   assert (NULL != par);
182 
183   videostd_set = _vbi_videostd_set_from_scanning (sp->scanning);
184   if (0 == (par->videostd_set & videostd_set)) {
185     info (log,
186         "Service 0x%08x (%s) requires "
187         "videostd_set 0x%lx, "
188         "have 0x%lx.",
189         par->id, par->label,
190         (unsigned long) par->videostd_set, (unsigned long) videostd_set);
191     return FALSE;
192   }
193 
194   if (par->flags & _VBI_SP_LINE_NUM) {
195     if ((par->first[0] > 0 && unknown == (unsigned int) sp->start[0])
196         || (par->first[1] > 0 && unknown == (unsigned int) sp->start[1])) {
197       info (log,
198           "Service 0x%08x (%s) requires known "
199           "line numbers.", par->id, par->label);
200       return FALSE;
201     }
202   }
203 
204   {
205     unsigned int rate;
206 
207     rate = MAX (par->cri_rate, par->bit_rate);
208 
209     switch (par->id) {
210       case VBI_SLICED_WSS_625:
211         /* Effective bit rate is just 1/3 max_rate,
212            so 1 * max_rate should suffice. */
213         break;
214 
215       default:
216         rate = (rate * 3) >> 1;
217         break;
218     }
219 
220     if (rate > (unsigned int) sp->sampling_rate) {
221       info (log,
222           "Sampling rate %f MHz too low "
223           "for service 0x%08x (%s).",
224           sp->sampling_rate / 1e6, par->id, par->label);
225       return FALSE;
226     }
227   }
228 
229   signal = par->cri_bits / (double) par->cri_rate
230       + (par->frc_bits + par->payload) / (double) par->bit_rate;
231 
232   samples_per_line = sp->bytes_per_line / VBI_PIXFMT_BPP (sp->sampling_format);
233 
234   if (0 && sp->offset > 0 && strict > 0) {
235     double sampling_rate;
236     double offset;
237     double end;
238 
239     sampling_rate = (double) sp->sampling_rate;
240 
241     offset = sp->offset / sampling_rate;
242     end = (sp->offset + samples_per_line) / sampling_rate;
243 
244     if (offset > (par->offset / 1e3 - 0.5e-6)) {
245       info (log,
246           "Sampling starts at 0H + %f us, too "
247           "late for service 0x%08x (%s) at "
248           "%f us.", offset * 1e6, par->id, par->label, par->offset / 1e3);
249       return FALSE;
250     }
251 
252     if (end < (par->offset / 1e9 + signal + 0.5e-6)) {
253       info (log,
254           "Sampling ends too early at 0H + "
255           "%f us for service 0x%08x (%s) "
256           "which ends at %f us",
257           end * 1e6,
258           par->id, par->label, par->offset / 1e3 + signal * 1e6 + 0.5);
259       return FALSE;
260     }
261   } else {
262     double samples;
263 
264     samples = samples_per_line / (double) sp->sampling_rate;
265 
266     if (strict > 0)
267       samples -= 1e-6;          /* headroom */
268 
269     if (samples < signal) {
270       info (log,
271           "Service 0x%08x (%s) signal length "
272           "%f us exceeds %f us sampling length.",
273           par->id, par->label, signal * 1e6, samples * 1e6);
274       return FALSE;
275     }
276   }
277 
278   if ((par->flags & _VBI_SP_FIELD_NUM)
279       && !sp->synchronous) {
280     info (log,
281         "Service 0x%08x (%s) requires "
282         "synchronous field order.", par->id, par->label);
283     return FALSE;
284   }
285 
286   for (field = 0; field < 2; ++field) {
287     unsigned int start;
288     unsigned int end;
289 
290     start = sp->start[field];
291     end = start + sp->count[field] - 1;
292 
293     if (0 == par->first[field]
294         || 0 == par->last[field]) {
295       /* No data on this field. */
296       continue;
297     }
298 
299     if (0 == sp->count[field]) {
300       info (log,
301           "Service 0x%08x (%s) requires "
302           "data from field %u", par->id, par->label, field + 1);
303       return FALSE;
304     }
305 
306     /* (int) <= 0 for compatibility with libzvbi 0.2.x */
307     if ((int) strict <= 0 || 0 == sp->start[field])
308       continue;
309 
310     if (1 == strict && par->first[field] > par->last[field]) {
311       /* May succeed if not all scanning lines
312          available for the service are actually used. */
313       continue;
314     }
315 
316     if (start > par->first[field]
317         || end < par->last[field]) {
318       info (log,
319           "Service 0x%08x (%s) requires "
320           "lines %u-%u, have %u-%u.",
321           par->id, par->label, par->first[field], par->last[field], start, end);
322       return FALSE;
323     }
324   }
325 
326   return TRUE;
327 }
328 
329 /**
330  * @internal
331  */
332 vbi_service_set
_vbi_sampling_par_check_services_log(const vbi_sampling_par * sp,vbi_service_set services,unsigned int strict,_vbi_log_hook * log)333     _vbi_sampling_par_check_services_log
334     (const vbi_sampling_par * sp,
335     vbi_service_set services, unsigned int strict, _vbi_log_hook * log) {
336   const _vbi_service_par *par;
337   vbi_service_set rservices;
338 
339   assert (NULL != sp);
340 
341   rservices = 0;
342 
343   for (par = _vbi_service_table; par->id; ++par) {
344     if (0 == (par->id & services))
345       continue;
346 
347     if (_vbi_sampling_par_permit_service (sp, par, strict, log))
348       rservices |= par->id;
349   }
350 
351   return rservices;
352 }
353 
354 /**
355  * @internal
356  */
357 vbi_service_set
_vbi_sampling_par_from_services_log(vbi_sampling_par * sp,unsigned int * max_rate,vbi_videostd_set videostd_set_req,vbi_service_set services,_vbi_log_hook * log)358     _vbi_sampling_par_from_services_log
359     (vbi_sampling_par * sp,
360     unsigned int *max_rate,
361     vbi_videostd_set videostd_set_req,
362     vbi_service_set services, _vbi_log_hook * log) {
363   const _vbi_service_par *par;
364   vbi_service_set rservices;
365   vbi_videostd_set videostd_set;
366   unsigned int rate;
367   unsigned int samples_per_line;
368 
369   assert (NULL != sp);
370 
371   videostd_set = 0;
372 
373   if (0 != videostd_set_req) {
374     if (0 == (VBI_VIDEOSTD_SET_ALL & videostd_set_req)
375         || ((VBI_VIDEOSTD_SET_525_60 & videostd_set_req)
376             && (VBI_VIDEOSTD_SET_625_50 & videostd_set_req))) {
377       warning (log,
378           "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set_req);
379       CLEAR (*sp);
380       return 0;
381     }
382 
383     videostd_set = videostd_set_req;
384   }
385 
386   samples_per_line = 0;
387   sp->sampling_rate = 27000000; /* ITU-R BT.601 */
388   sp->offset = (int) (64e-6 * sp->sampling_rate);
389   sp->start[0] = 30000;
390   sp->count[0] = 0;
391   sp->start[1] = 30000;
392   sp->count[1] = 0;
393   sp->interlaced = FALSE;
394   sp->synchronous = TRUE;
395 
396   rservices = 0;
397   rate = 0;
398 
399   for (par = _vbi_service_table; par->id; ++par) {
400 #if 0                           /* Set but unused */
401     double margin;
402 #endif
403     double signal;
404     int offset;
405     unsigned int samples;
406     unsigned int i;
407 
408     if (0 == (par->id & services))
409       continue;
410 
411     if (0 == videostd_set_req) {
412       vbi_videostd_set set;
413 
414       set = par->videostd_set | videostd_set;
415 
416       if (0 == (set & ~VBI_VIDEOSTD_SET_525_60)
417           || 0 == (set & ~VBI_VIDEOSTD_SET_625_50))
418         videostd_set |= par->videostd_set;
419     }
420 #if 0                           /* Set but unused */
421     if (VBI_VIDEOSTD_SET_525_60 & videostd_set)
422       margin = 1.0e-6;
423     else
424       margin = 2.0e-6;
425 #endif
426 
427     if (0 == (par->videostd_set & videostd_set)) {
428       info (log,
429           "Service 0x%08x (%s) requires "
430           "videostd_set 0x%lx, "
431           "have 0x%lx.",
432           par->id, par->label,
433           (unsigned long) par->videostd_set, (unsigned long) videostd_set);
434       continue;
435     }
436 
437     rate = MAX (rate, par->cri_rate);
438     rate = MAX (rate, par->bit_rate);
439 
440     signal = par->cri_bits / (double) par->cri_rate
441         + ((par->frc_bits + par->payload) / (double) par->bit_rate);
442 
443     offset = (int) ((par->offset / 1e9) * sp->sampling_rate);
444     samples = (int) ((signal + 1.0e-6) * sp->sampling_rate);
445 
446     sp->offset = MIN (sp->offset, offset);
447 
448     samples_per_line = MAX (samples_per_line + sp->offset,
449         samples + offset) - sp->offset;
450 
451     for (i = 0; i < 2; ++i)
452       if (par->first[i] > 0 && par->last[i] > 0) {
453         sp->start[i] = MIN
454             ((unsigned int) sp->start[i], (unsigned int) par->first[i]);
455         sp->count[i] = MAX ((unsigned int) sp->start[i]
456             + sp->count[i], (unsigned int) par->last[i] + 1)
457             - sp->start[i];
458       }
459 
460     rservices |= par->id;
461   }
462 
463   if (0 == rservices) {
464     CLEAR (*sp);
465     return 0;
466   }
467 
468   if (0 == sp->count[1]) {
469     sp->start[1] = 0;
470 
471     if (0 == sp->count[0]) {
472       sp->start[0] = 0;
473       sp->offset = 0;
474     }
475   } else if (0 == sp->count[0]) {
476     sp->start[0] = 0;
477   }
478 
479   sp->scanning = (videostd_set & VBI_VIDEOSTD_SET_525_60)
480       ? 525 : 625;
481   sp->sp_sample_format = VBI_PIXFMT_YUV420;
482 
483   /* Note bpp is 1. */
484   sp->bytes_per_line = MAX (1440U, samples_per_line);
485 
486   if (max_rate)
487     *max_rate = rate;
488 
489   return rservices;
490 }
491 
492 /**
493  * @param sp Sampling parameters to check against.
494  * @param services Set of data services.
495  * @param strict See description of vbi_raw_decoder_add_services().
496  *
497  * Check which of the given services can be decoded with the given
498  * sampling parameters at the given strictness level.
499  *
500  * @return
501  * Subset of @a services decodable with the given sampling parameters.
502  */
503 vbi_service_set
vbi_sampling_par_check_services(const vbi_sampling_par * sp,vbi_service_set services,unsigned int strict)504     vbi_sampling_par_check_services
505     (const vbi_sampling_par * sp,
506     vbi_service_set services, unsigned int strict) {
507   return _vbi_sampling_par_check_services_log (sp, services, strict,
508       /* log_hook */ NULL);
509 }
510 
511 /**
512  * @param sp Sampling parameters calculated by this function
513  *   will be stored here.
514  * @param max_rate If not NULL, the highest data bit rate in Hz of
515  *   all services requested will be stored here. The sampling rate
516  *   should be at least twice as high; @sp sampling_rate will
517  *   be set to a more reasonable value of 27 MHz, which is twice
518  *   the video sampling rate defined by ITU-R Rec. BT.601.
519  * @param videostd_set Create sampling parameters matching these
520  *   video standards. When 0 determine video standard from requested
521  *   services.
522  * @param services Set of VBI_SLICED_ symbols. Here (and only here) you
523  *   can add @c VBI_SLICED_VBI_625 or @c VBI_SLICED_VBI_525 to include all
524  *   vbi scan lines in the calculated sampling parameters.
525  *
526  * Calculate the sampling parameters required to receive and decode the
527  * requested data @a services. The @a sp sampling_format will be
528  * @c VBI_PIXFMT_Y8, offset and bytes_per_line will be set to
529  * reasonable minimums. This function can be used to initialize hardware
530  * prior to creating a vbi_raw_decoder object.
531  *
532  * @return
533  * Subset of @a services covered by the calculated sampling parameters.
534  */
535 vbi_service_set
vbi_sampling_par_from_services(vbi_sampling_par * sp,unsigned int * max_rate,vbi_videostd_set videostd_set,vbi_service_set services)536 vbi_sampling_par_from_services (vbi_sampling_par * sp,
537     unsigned int *max_rate,
538     vbi_videostd_set videostd_set, vbi_service_set services)
539 {
540   return _vbi_sampling_par_from_services_log (sp, max_rate,
541       videostd_set, services,
542       /* log_hook */ NULL);
543 }
544 
545 
546 /*
547 Local variables:
548 c-set-style: K&R
549 c-basic-offset: 8
550 End:
551 */
552