/* * libzvbi -- Raw VBI sampling parameters * * Copyright (C) 2000-2004 Michael H. Schimek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. */ /* $Id: sampling_par.c,v 1.12 2013-08-28 14:45:00 mschimek Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <errno.h> #include "misc.h" #include "raw_decoder.h" #include "sampling_par.h" #include "sliced.h" # define vbi_pixfmt_bytes_per_pixel VBI_PIXFMT_BPP # define sp_sample_format sampling_format /** * @addtogroup Sampling Raw VBI sampling * @ingroup Raw * @brief Raw VBI data sampling interface. */ /** * @internal * Compatibility. */ vbi_videostd_set _vbi_videostd_set_from_scanning (int scanning) { switch (scanning) { case 525: return VBI_VIDEOSTD_SET_525_60; case 625: return VBI_VIDEOSTD_SET_625_50; default: break; } return 0; } _vbi_inline vbi_bool range_check (unsigned int start, unsigned int count, unsigned int min, unsigned int max) { /* Check bounds and overflow. */ return (start >= min && (start + count) <= max && (start + count) >= start); } /** * @internal * @param sp Sampling parameters to verify. * * @return * TRUE if the sampling parameters are valid (as far as we can tell). */ vbi_bool _vbi_sampling_par_valid_log (const vbi_sampling_par * sp, _vbi_log_hook * log) { vbi_videostd_set videostd_set; unsigned int bpp; assert (NULL != sp); switch (sp->sp_sample_format) { case VBI_PIXFMT_YUV420: /* This conflicts with the ivtv driver, which returns an odd number of bytes per line. The driver format is _GREY but libzvbi 0.2 has no VBI_PIXFMT_Y8. */ break; default: bpp = vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format); if (0 != (sp->bytes_per_line % bpp)) goto bad_samples; break; } if (0 == sp->bytes_per_line) goto no_samples; if (0 == sp->count[0] && 0 == sp->count[1]) goto bad_range; videostd_set = _vbi_videostd_set_from_scanning (sp->scanning); if (VBI_VIDEOSTD_SET_525_60 & videostd_set) { if (VBI_VIDEOSTD_SET_625_50 & videostd_set) goto ambiguous; if (0 != sp->start[0] && !range_check (sp->start[0], sp->count[0], 1, 262)) goto bad_range; if (0 != sp->start[1] && !range_check (sp->start[1], sp->count[1], 263, 525)) goto bad_range; } else if (VBI_VIDEOSTD_SET_625_50 & videostd_set) { if (0 != sp->start[0] && !range_check (sp->start[0], sp->count[0], 1, 311)) goto bad_range; if (0 != sp->start[1] && !range_check (sp->start[1], sp->count[1], 312, 625)) goto bad_range; } else { ambiguous: info (log, "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set); return FALSE; } if (sp->interlaced && (sp->count[0] != sp->count[1] || 0 == sp->count[0])) { info (log, "Line counts %u, %u must be equal and " "non-zero when raw VBI data is interlaced.", sp->count[0], sp->count[1]); return FALSE; } return TRUE; no_samples: info (log, "samples_per_line is zero."); return FALSE; bad_samples: info (log, "bytes_per_line value %u is no multiple of " "the sample size %u.", sp->bytes_per_line, vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format)); return FALSE; bad_range: info (log, "Invalid VBI scan range %u-%u (%u lines), " "%u-%u (%u lines).", sp->start[0], sp->start[0] + sp->count[0] - 1, sp->count[0], sp->start[1], sp->start[1] + sp->count[1] - 1, sp->count[1]); return FALSE; } 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) { const unsigned int unknown = 0; double signal; unsigned int field; unsigned int samples_per_line; vbi_videostd_set videostd_set; assert (NULL != sp); assert (NULL != par); videostd_set = _vbi_videostd_set_from_scanning (sp->scanning); if (0 == (par->videostd_set & videostd_set)) { info (log, "Service 0x%08x (%s) requires " "videostd_set 0x%lx, " "have 0x%lx.", par->id, par->label, (unsigned long) par->videostd_set, (unsigned long) videostd_set); return FALSE; } if (par->flags & _VBI_SP_LINE_NUM) { if ((par->first[0] > 0 && unknown == (unsigned int) sp->start[0]) || (par->first[1] > 0 && unknown == (unsigned int) sp->start[1])) { info (log, "Service 0x%08x (%s) requires known " "line numbers.", par->id, par->label); return FALSE; } } { unsigned int rate; rate = MAX (par->cri_rate, par->bit_rate); switch (par->id) { case VBI_SLICED_WSS_625: /* Effective bit rate is just 1/3 max_rate, so 1 * max_rate should suffice. */ break; default: rate = (rate * 3) >> 1; break; } if (rate > (unsigned int) sp->sampling_rate) { info (log, "Sampling rate %f MHz too low " "for service 0x%08x (%s).", sp->sampling_rate / 1e6, par->id, par->label); return FALSE; } } signal = par->cri_bits / (double) par->cri_rate + (par->frc_bits + par->payload) / (double) par->bit_rate; samples_per_line = sp->bytes_per_line / VBI_PIXFMT_BPP (sp->sampling_format); if (0 && sp->offset > 0 && strict > 0) { double sampling_rate; double offset; double end; sampling_rate = (double) sp->sampling_rate; offset = sp->offset / sampling_rate; end = (sp->offset + samples_per_line) / sampling_rate; if (offset > (par->offset / 1e3 - 0.5e-6)) { info (log, "Sampling starts at 0H + %f us, too " "late for service 0x%08x (%s) at " "%f us.", offset * 1e6, par->id, par->label, par->offset / 1e3); return FALSE; } if (end < (par->offset / 1e9 + signal + 0.5e-6)) { info (log, "Sampling ends too early at 0H + " "%f us for service 0x%08x (%s) " "which ends at %f us", end * 1e6, par->id, par->label, par->offset / 1e3 + signal * 1e6 + 0.5); return FALSE; } } else { double samples; samples = samples_per_line / (double) sp->sampling_rate; if (strict > 0) samples -= 1e-6; /* headroom */ if (samples < signal) { info (log, "Service 0x%08x (%s) signal length " "%f us exceeds %f us sampling length.", par->id, par->label, signal * 1e6, samples * 1e6); return FALSE; } } if ((par->flags & _VBI_SP_FIELD_NUM) && !sp->synchronous) { info (log, "Service 0x%08x (%s) requires " "synchronous field order.", par->id, par->label); return FALSE; } for (field = 0; field < 2; ++field) { unsigned int start; unsigned int end; start = sp->start[field]; end = start + sp->count[field] - 1; if (0 == par->first[field] || 0 == par->last[field]) { /* No data on this field. */ continue; } if (0 == sp->count[field]) { info (log, "Service 0x%08x (%s) requires " "data from field %u", par->id, par->label, field + 1); return FALSE; } /* (int) <= 0 for compatibility with libzvbi 0.2.x */ if ((int) strict <= 0 || 0 == sp->start[field]) continue; if (1 == strict && par->first[field] > par->last[field]) { /* May succeed if not all scanning lines available for the service are actually used. */ continue; } if (start > par->first[field] || end < par->last[field]) { info (log, "Service 0x%08x (%s) requires " "lines %u-%u, have %u-%u.", par->id, par->label, par->first[field], par->last[field], start, end); return FALSE; } } return TRUE; } /** * @internal */ 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) { const _vbi_service_par *par; vbi_service_set rservices; assert (NULL != sp); rservices = 0; for (par = _vbi_service_table; par->id; ++par) { if (0 == (par->id & services)) continue; if (_vbi_sampling_par_permit_service (sp, par, strict, log)) rservices |= par->id; } return rservices; } /** * @internal */ 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) { const _vbi_service_par *par; vbi_service_set rservices; vbi_videostd_set videostd_set; unsigned int rate; unsigned int samples_per_line; assert (NULL != sp); videostd_set = 0; if (0 != videostd_set_req) { if (0 == (VBI_VIDEOSTD_SET_ALL & videostd_set_req) || ((VBI_VIDEOSTD_SET_525_60 & videostd_set_req) && (VBI_VIDEOSTD_SET_625_50 & videostd_set_req))) { warning (log, "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set_req); CLEAR (*sp); return 0; } videostd_set = videostd_set_req; } samples_per_line = 0; sp->sampling_rate = 27000000; /* ITU-R BT.601 */ sp->offset = (int) (64e-6 * sp->sampling_rate); sp->start[0] = 30000; sp->count[0] = 0; sp->start[1] = 30000; sp->count[1] = 0; sp->interlaced = FALSE; sp->synchronous = TRUE; rservices = 0; rate = 0; for (par = _vbi_service_table; par->id; ++par) { #if 0 /* Set but unused */ double margin; #endif double signal; int offset; unsigned int samples; unsigned int i; if (0 == (par->id & services)) continue; if (0 == videostd_set_req) { vbi_videostd_set set; set = par->videostd_set | videostd_set; if (0 == (set & ~VBI_VIDEOSTD_SET_525_60) || 0 == (set & ~VBI_VIDEOSTD_SET_625_50)) videostd_set |= par->videostd_set; } #if 0 /* Set but unused */ if (VBI_VIDEOSTD_SET_525_60 & videostd_set) margin = 1.0e-6; else margin = 2.0e-6; #endif if (0 == (par->videostd_set & videostd_set)) { info (log, "Service 0x%08x (%s) requires " "videostd_set 0x%lx, " "have 0x%lx.", par->id, par->label, (unsigned long) par->videostd_set, (unsigned long) videostd_set); continue; } rate = MAX (rate, par->cri_rate); rate = MAX (rate, par->bit_rate); signal = par->cri_bits / (double) par->cri_rate + ((par->frc_bits + par->payload) / (double) par->bit_rate); offset = (int) ((par->offset / 1e9) * sp->sampling_rate); samples = (int) ((signal + 1.0e-6) * sp->sampling_rate); sp->offset = MIN (sp->offset, offset); samples_per_line = MAX (samples_per_line + sp->offset, samples + offset) - sp->offset; for (i = 0; i < 2; ++i) if (par->first[i] > 0 && par->last[i] > 0) { sp->start[i] = MIN ((unsigned int) sp->start[i], (unsigned int) par->first[i]); sp->count[i] = MAX ((unsigned int) sp->start[i] + sp->count[i], (unsigned int) par->last[i] + 1) - sp->start[i]; } rservices |= par->id; } if (0 == rservices) { CLEAR (*sp); return 0; } if (0 == sp->count[1]) { sp->start[1] = 0; if (0 == sp->count[0]) { sp->start[0] = 0; sp->offset = 0; } } else if (0 == sp->count[0]) { sp->start[0] = 0; } sp->scanning = (videostd_set & VBI_VIDEOSTD_SET_525_60) ? 525 : 625; sp->sp_sample_format = VBI_PIXFMT_YUV420; /* Note bpp is 1. */ sp->bytes_per_line = MAX (1440U, samples_per_line); if (max_rate) *max_rate = rate; return rservices; } /** * @param sp Sampling parameters to check against. * @param services Set of data services. * @param strict See description of vbi_raw_decoder_add_services(). * * Check which of the given services can be decoded with the given * sampling parameters at the given strictness level. * * @return * Subset of @a services decodable with the given sampling parameters. */ vbi_service_set vbi_sampling_par_check_services (const vbi_sampling_par * sp, vbi_service_set services, unsigned int strict) { return _vbi_sampling_par_check_services_log (sp, services, strict, /* log_hook */ NULL); } /** * @param sp Sampling parameters calculated by this function * will be stored here. * @param max_rate If not NULL, the highest data bit rate in Hz of * all services requested will be stored here. The sampling rate * should be at least twice as high; @sp sampling_rate will * be set to a more reasonable value of 27 MHz, which is twice * the video sampling rate defined by ITU-R Rec. BT.601. * @param videostd_set Create sampling parameters matching these * video standards. When 0 determine video standard from requested * services. * @param services Set of VBI_SLICED_ symbols. Here (and only here) you * can add @c VBI_SLICED_VBI_625 or @c VBI_SLICED_VBI_525 to include all * vbi scan lines in the calculated sampling parameters. * * Calculate the sampling parameters required to receive and decode the * requested data @a services. The @a sp sampling_format will be * @c VBI_PIXFMT_Y8, offset and bytes_per_line will be set to * reasonable minimums. This function can be used to initialize hardware * prior to creating a vbi_raw_decoder object. * * @return * Subset of @a services covered by the calculated sampling parameters. */ 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) { return _vbi_sampling_par_from_services_log (sp, max_rate, videostd_set, services, /* log_hook */ NULL); } /* Local variables: c-set-style: K&R c-basic-offset: 8 End: */