<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=9"/> <meta name="generator" content="Doxygen 1.8.11"/> <title>ZVBI Library: examples/pdc2.c</title> <link href="tabs.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="dynsections.js"></script> <link href="doxygen.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="top"><!-- do not remove this div, it is closed by doxygen! --> <div id="titlearea"> <table cellspacing="0" cellpadding="0"> <tbody> <tr style="height: 56px;"> <td id="projectalign" style="padding-left: 0.5em;"> <div id="projectname">ZVBI Library  <span id="projectnumber">0.2.35</span> </div> </td> </tr> </tbody> </table> </div> <!-- end header part --> <!-- Generated by Doxygen 1.8.11 --> <div id="navrow1" class="tabs"> <ul class="tablist"> <li><a href="index.html"><span>Main Page</span></a></li> <li><a href="pages.html"><span>Related Pages</span></a></li> <li><a href="modules.html"><span>Modules</span></a></li> <li><a href="annotated.html"><span>Data Structures</span></a></li> <li><a href="examples.html"><span>Examples</span></a></li> </ul> </div> </div><!-- top --> <div class="header"> <div class="headertitle"> <div class="title">examples/pdc2.c</div> </div> </div><!--header--> <div class="contents"> <div class="fragment"><div class="line"><span class="comment">/*</span></div><div class="line"><span class="comment"> * libzvbi VPS/PDC example 2</span></div><div class="line"><span class="comment"> *</span></div><div class="line"><span class="comment"> * Copyright (C) 2009 Michael H. Schimek</span></div><div class="line"><span class="comment"> *</span></div><div class="line"><span class="comment"> * Redistribution and use in source and binary forms, with or without</span></div><div class="line"><span class="comment"> * modification, are permitted provided that the following conditions</span></div><div class="line"><span class="comment"> * are met:</span></div><div class="line"><span class="comment"> * 1. Redistributions of source code must retain the above copyright</span></div><div class="line"><span class="comment"> * notice, this list of conditions and the following disclaimer.</span></div><div class="line"><span class="comment"> * 2. Redistributions in binary form must reproduce the above copyright</span></div><div class="line"><span class="comment"> * notice, this list of conditions and the following disclaimer in</span></div><div class="line"><span class="comment"> * the documentation and/or other materials provided with the</span></div><div class="line"><span class="comment"> * distribution.</span></div><div class="line"><span class="comment"> * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS</span></div><div class="line"><span class="comment"> * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT</span></div><div class="line"><span class="comment"> * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR</span></div><div class="line"><span class="comment"> * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT</span></div><div class="line"><span class="comment"> * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,</span></div><div class="line"><span class="comment"> * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT</span></div><div class="line"><span class="comment"> * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,</span></div><div class="line"><span class="comment"> * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY</span></div><div class="line"><span class="comment"> * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT</span></div><div class="line"><span class="comment"> * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE</span></div><div class="line"><span class="comment"> * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span></div><div class="line"><span class="comment"> */</span></div><div class="line"></div><div class="line"><span class="comment">/* $Id: pdc2.c,v 1.1 2009/03/23 01:30:39 mschimek Exp $ */</span></div><div class="line"></div><div class="line"><span class="comment">/* This example shows how to receive and decode VPS/PDC Program IDs.</span></div><div class="line"><span class="comment"> For simplicity channel change functions have been omitted and not</span></div><div class="line"><span class="comment"> all PDC features are supported. (A more complete example will be</span></div><div class="line"><span class="comment"> added later.)</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> To compile this program type:</span></div><div class="line"><span class="comment"> gcc -o pdc2 pdc2.c `pkg-config zvbi-0.2 --cflags --libs`</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> This program expects the starting date and time, ending time</span></div><div class="line"><span class="comment"> and VPS/PDC time of a TV program to record as arguments:</span></div><div class="line"><span class="comment"> ./pdc2 YYYY-MM-DD HH:MM HH:MM HH:MM</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> It opens a V4L2 device at /dev/vbi and scans the currently tuned in</span></div><div class="line"><span class="comment"> channel for a matching VPS/PDC label, logging the progress on</span></div><div class="line"><span class="comment"> standard output, without actually recording anything.</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> The -t option enables a test mode where the program reads VPS/PDC</span></div><div class="line"><span class="comment"> signal changes from standard input instead of opening a VBI</span></div><div class="line"><span class="comment"> device. See parse_test_file_line() for a description of the file</span></div><div class="line"><span class="comment"> format.</span></div><div class="line"><span class="comment">*/</span></div><div class="line"></div><div class="line"><span class="preprocessor">#define _GNU_SOURCE 1</span></div><div class="line"><span class="preprocessor">#undef NDEBUG</span></div><div class="line"></div><div class="line"><span class="preprocessor">#include <stdio.h></span></div><div class="line"><span class="preprocessor">#include <stdlib.h></span></div><div class="line"><span class="preprocessor">#include <stdarg.h></span></div><div class="line"><span class="preprocessor">#include <string.h></span></div><div class="line"><span class="preprocessor">#include <float.h></span></div><div class="line"><span class="preprocessor">#include <math.h></span></div><div class="line"><span class="preprocessor">#include <time.h></span></div><div class="line"><span class="preprocessor">#include <locale.h></span></div><div class="line"><span class="preprocessor">#include <ctype.h></span></div><div class="line"><span class="preprocessor">#include <unistd.h></span></div><div class="line"><span class="preprocessor">#include <getopt.h></span></div><div class="line"><span class="preprocessor">#include <limits.h></span></div><div class="line"><span class="preprocessor">#include <errno.h></span></div><div class="line"><span class="preprocessor">#include <assert.h></span></div><div class="line"></div><div class="line"><span class="preprocessor">#include <libzvbi.h></span></div><div class="line"></div><div class="line"><span class="preprocessor">#ifndef N_ELEMENTS</span></div><div class="line"><span class="preprocessor"># define N_ELEMENTS(array) (sizeof (array) / sizeof (*(array)))</span></div><div class="line"><span class="preprocessor">#endif</span></div><div class="line"><span class="preprocessor">#ifndef MIN</span></div><div class="line"><span class="preprocessor"># define MIN(x, y) ((x) < (y) ? (x) : (y))</span></div><div class="line"><span class="preprocessor">#endif</span></div><div class="line"></div><div class="line"><span class="keyword">static</span> <a name="_a0"></a><a class="code" href="structvbi__capture.html">vbi_capture</a> * cap;</div><div class="line"><span class="keyword">static</span> <a name="_a1"></a><a class="code" href="structvbi__decoder.html">vbi_decoder</a> * dec;</div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> * dev_name;</div><div class="line"><span class="keyword">static</span> vbi_bool quit;</div><div class="line"><span class="keyword">static</span> <span class="keywordtype">int</span> exit_code;</div><div class="line"></div><div class="line"><span class="comment">/* The current time of the intended audience of the tuned in network</span></div><div class="line"><span class="comment"> according to the network (see VBI_EVENT_LOCAL_TIME). It may differ</span></div><div class="line"><span class="comment"> from system time if the system is not in sync with UTC or if we</span></div><div class="line"><span class="comment"> receive the TV signal with a delay. */</span></div><div class="line"><span class="keyword">static</span> time_t audience_time;</div><div class="line"></div><div class="line"><span class="comment">/* The system time in seconds when the most recent PDC signal was</span></div><div class="line"><span class="comment"> received. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">double</span> timestamp;</div><div class="line"></div><div class="line"><span class="comment">/* PDC Label Channel state. */</span></div><div class="line"><span class="keyword">struct </span>lc_state {</div><div class="line"> <span class="comment">/* The PIL most recently received on this LC, zero if none. */</span></div><div class="line"> <a class="code" href="group__ProgramID.html#ga870eeecedc6aed609b9f53d4dbe0f6c7">vbi_pil</a> pil;</div><div class="line"></div><div class="line"> <span class="comment">/* The system time in seconds when the PIL was most recently</span></div><div class="line"><span class="comment"> received. */</span></div><div class="line"> <span class="keywordtype">double</span> last_at;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/* The most recently received PILs. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">struct </span>lc_state lc_state[VBI_MAX_PID_CHANNELS];</div><div class="line"></div><div class="line"><span class="comment">/* Video recorder states. */</span></div><div class="line"><span class="keyword">enum</span> vcr_state {</div><div class="line"> <span class="comment">/* All capturing stopped. */</span></div><div class="line"> VCR_STATE_STBY,</div><div class="line"></div><div class="line"> <span class="comment">/* Searching for a PDC signal. */</span></div><div class="line"> VCR_STATE_SCAN,</div><div class="line"></div><div class="line"> <span class="comment">/* Preparing to record. */</span></div><div class="line"> VCR_STATE_PTR,</div><div class="line"></div><div class="line"> <span class="comment">/* Recording a program. */</span></div><div class="line"> VCR_STATE_REC</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/* The current video recorder state. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">enum</span> vcr_state vcr_state;</div><div class="line"></div><div class="line"><span class="comment">/* The system time in seconds at the last change of vcr_state. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">double</span> vcr_state_since;</div><div class="line"></div><div class="line"><span class="comment">/* In timer control mode we start and stop recording at the scheduled</span></div><div class="line"><span class="comment"> times. Timer control mode is enabled when the network does not</span></div><div class="line"><span class="comment"> transmit program IDs or when we lost all PDC signals. */</span></div><div class="line"><span class="keyword">static</span> vbi_bool timer_control_mode;</div><div class="line"></div><div class="line"><span class="comment">/* In VCR_STATE_REC this variable stops recording with a 30 second</span></div><div class="line"><span class="comment"> delay as required by EN 300 231. This is a system time in</span></div><div class="line"><span class="comment"> seconds, or DBL_MAX if no stop is planned. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">double</span> delayed_stop_at;</div><div class="line"></div><div class="line"><span class="comment">/* In VCR_REC_STATE if delayed_stop_at < DBL_MAX, delayed_stop_pid</span></div><div class="line"><span class="comment"> contains a copy of the program ID which caused the delayed stop.</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> If delayed_stop_pid.luf == 1 the program will continue on the</span></div><div class="line"><span class="comment"> channel with delayed_stop_pid.cni, accompanied by</span></div><div class="line"><span class="comment"> delayed_stop_pid.pil (which may also provide a new start date and</span></div><div class="line"><span class="comment"> time for the schedule).</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> Otherwise delayed_stop_pid.pil can be a valid PIL, a RI/T or INT</span></div><div class="line"><span class="comment"> service code, or zero if a loss of the PDC signal or service caused</span></div><div class="line"><span class="comment"> the delayed stop. */</span></div><div class="line"><span class="keyword">static</span> <a name="_a2"></a><a class="code" href="structvbi__program__id.html">vbi_program_id</a> delayed_stop_pid;</div><div class="line"></div><div class="line"><span class="comment">/* A program to be recorded. */</span></div><div class="line"><span class="keyword">struct </span>program {</div><div class="line"> <span class="keyword">struct </span>program * next;</div><div class="line"></div><div class="line"> <span class="comment">/* A number in lieu of a title. */</span></div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> index;</div><div class="line"></div><div class="line"> <span class="comment">/* The most recently announced start and end time of the</span></div><div class="line"><span class="comment"> program ("AT-1" in EN 300 231 in parlance), in case we do</span></div><div class="line"><span class="comment"> not receive a PDC signal. When the duration of the program</span></div><div class="line"><span class="comment"> is unknown start_time == end_time. end_time is</span></div><div class="line"><span class="comment"> exclusive. */</span></div><div class="line"> time_t start_time;</div><div class="line"> time_t end_time;</div><div class="line"></div><div class="line"> <span class="comment">/* The expected Program Identification Label. Usually this is</span></div><div class="line"><span class="comment"> the originally announced start date and time of the program</span></div><div class="line"><span class="comment"> ("AT-2" in EN 300 231), relative to the time zone of the</span></div><div class="line"><span class="comment"> intended audience. */</span></div><div class="line"> <a class="code" href="group__ProgramID.html#ga870eeecedc6aed609b9f53d4dbe0f6c7">vbi_pil</a> pil;</div><div class="line"></div><div class="line"> <span class="comment">/* The validity window of pil, that is the time when the</span></div><div class="line"><span class="comment"> network can be expected to transmit the PIL. Usually from</span></div><div class="line"><span class="comment"> 00:00 on the same day to 04:00 on the next</span></div><div class="line"><span class="comment"> day. pil_valid_end is exclusive. */</span></div><div class="line"> time_t pil_valid_start;</div><div class="line"> time_t pil_valid_end;</div><div class="line"></div><div class="line"> <span class="comment">/* Recording is in progress or was interrupted. */</span></div><div class="line"> vbi_bool continues;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/* The recording schedule, a singly-linked list of program</span></div><div class="line"><span class="comment"> structures. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">struct </span>program * schedule;</div><div class="line"></div><div class="line"><span class="comment">/* In VCR_STATE_PTR and VCR_STATE_REC the program we (are about to)</span></div><div class="line"><span class="comment"> record, a pointer into the schedule list. Otherwise NULL. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">struct </span>program * curr_program;</div><div class="line"></div><div class="line"><span class="comment">/* If curr_program != NULL this variable contains a copy of the</span></div><div class="line"><span class="comment"> program ID which put us into PTR or REC state. If recording was</span></div><div class="line"><span class="comment"> started by the timer curr_pid.pil is zero. */</span></div><div class="line"><span class="keyword">static</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> curr_pid;</div><div class="line"></div><div class="line"><span class="keyword">static</span> vbi_bool test_mode;</div><div class="line"></div><div class="line"><span class="comment">/* In test mode this is the expected VCR state after the most recent</span></div><div class="line"><span class="comment"> PDC signal change. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">enum</span> vcr_state test_exp_vcr_state;</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">double</span></div><div class="line">signal_timeout [<a name="a3"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>] = {</div><div class="line"> [<a name="a4"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>] = 2,</div><div class="line"> [<a name="a5"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262afdd62439879c03c78fce236bf7dcc081">VBI_PID_CHANNEL_LCI_1</a>] = 2,</div><div class="line"> [<a name="a6"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262ac9a4d6e6932f0f4fd2a19f71f07f4447">VBI_PID_CHANNEL_LCI_2</a>] = 2,</div><div class="line"> [<a name="a7"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a969d34250cbd4e60539740cc4f3398f1">VBI_PID_CHANNEL_LCI_3</a>] = 2,</div><div class="line"></div><div class="line"> <span class="comment">/* VPS signals have no error protection. When the payload</span></div><div class="line"><span class="comment"> changes, libzvbi will wait for one repetition to confirm</span></div><div class="line"><span class="comment"> correct reception. */</span></div><div class="line"> [<a name="a8"></a><a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>] = 3 / 25.0,</div><div class="line"></div><div class="line"> <span class="comment">/* Other channels not implemented yet. */</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">double</span></div><div class="line">signal_period [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>] = {</div><div class="line"> <span class="comment">/* EN 300 231 Section 8.3: "In the case of the packet 8/30</span></div><div class="line"><span class="comment"> version (Method B) the repetition rate of labels in any</span></div><div class="line"><span class="comment"> label data channel is once per second." Section E.2: "Where</span></div><div class="line"><span class="comment"> more than one label channel is in use the signalling rate</span></div><div class="line"><span class="comment"> is normally one line per label channel per second." */</span></div><div class="line"> [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>] = 1,</div><div class="line"> [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262afdd62439879c03c78fce236bf7dcc081">VBI_PID_CHANNEL_LCI_1</a>] = 1,</div><div class="line"> [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262ac9a4d6e6932f0f4fd2a19f71f07f4447">VBI_PID_CHANNEL_LCI_2</a>] = 1,</div><div class="line"> [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a969d34250cbd4e60539740cc4f3398f1">VBI_PID_CHANNEL_LCI_3</a>] = 1,</div><div class="line"></div><div class="line"> [<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>] = 1 / 25.0,</div><div class="line"></div><div class="line"> <span class="comment">/* Other channels not implemented yet. */</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/* For debugging. */</span></div><div class="line"><span class="preprocessor">#define D printf ("%s:%u\n", __FILE__, __LINE__)</span></div><div class="line"></div><div class="line"><span class="comment">/* For debugging. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">print_time (time_t time)</div><div class="line">{</div><div class="line"> <span class="keywordtype">char</span> buffer[80];</div><div class="line"> <span class="keyword">struct </span>tm tm;</div><div class="line"></div><div class="line"> memset (&tm, 0, <span class="keyword">sizeof</span> (tm));</div><div class="line"> localtime_r (&time, &tm);</div><div class="line"> strftime (buffer, <span class="keyword">sizeof</span> (buffer),</div><div class="line"> <span class="stringliteral">"%Y-%m-%d %H:%M:%S %Z = "</span>, &tm);</div><div class="line"> fputs (buffer, stdout);</div><div class="line"></div><div class="line"> memset (&tm, 0, <span class="keyword">sizeof</span> (tm));</div><div class="line"> gmtime_r (&time, &tm);</div><div class="line"> strftime (buffer, <span class="keyword">sizeof</span> (buffer),</div><div class="line"> <span class="stringliteral">"%Y-%m-%d %H:%M:%S UTC"</span>, &tm);</div><div class="line"> puts (buffer);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/* Attention! This function returns a static string. */</span></div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> *</div><div class="line">pil_str (<a class="code" href="group__ProgramID.html#ga870eeecedc6aed609b9f53d4dbe0f6c7">vbi_pil</a> pil)</div><div class="line">{</div><div class="line"> <span class="keyword">static</span> <span class="keywordtype">char</span> buffer[32];</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (pil) {</div><div class="line"> <span class="keywordflow">case</span> <a name="a9"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eae7cbef095ebb79470785785bebdb2f8d">VBI_PIL_TIMER_CONTROL</a>: <span class="keywordflow">return</span> <span class="stringliteral">"TC"</span>;</div><div class="line"> <span class="keywordflow">case</span> <a name="a10"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eaad30c6505f26ef2b6e26626afdff02dd">VBI_PIL_INHIBIT_TERMINATE</a>: <span class="keywordflow">return</span> <span class="stringliteral">"RI/T"</span>;</div><div class="line"> <span class="keywordflow">case</span> <a name="a11"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea835908e1f719f7761589ac1f3b968cb4">VBI_PIL_INTERRUPTION</a>: <span class="keywordflow">return</span> <span class="stringliteral">"INT"</span>;</div><div class="line"> <span class="keywordflow">case</span> <a name="a12"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea4084b4cb78ad4667f7645101adebb3e1">VBI_PIL_CONTINUE</a>: <span class="keywordflow">return</span> <span class="stringliteral">"CONT"</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> <a name="a13"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eac9b7c435e58a2f76a87f1acb221630f9">VBI_PIL_NSPV</a>:</div><div class="line"> <span class="comment">/* NSVP service code if source is VPS/PDC,</span></div><div class="line"><span class="comment"> END code if source is XDS. */</span></div><div class="line"> <span class="keywordflow">return</span> <span class="stringliteral">"NSPV/END"</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">default</span>:</div><div class="line"> snprintf (buffer, <span class="keyword">sizeof</span> (buffer),</div><div class="line"> <span class="stringliteral">"%02u%02uT%02u%02u"</span>,</div><div class="line"> <a name="a14"></a><a class="code" href="group__ProgramID.html#gaae3974c3e680d42322edaad37b35ebcc">VBI_PIL_MONTH</a> (pil),</div><div class="line"> <a name="a15"></a><a class="code" href="group__ProgramID.html#ga138a4f95542c36527f6df6ee08094f2f">VBI_PIL_DAY</a> (pil),</div><div class="line"> <a name="a16"></a><a class="code" href="group__ProgramID.html#ga78d71b2b88dd3729e5364f6061000ebf">VBI_PIL_HOUR</a> (pil),</div><div class="line"> <a name="a17"></a><a class="code" href="group__ProgramID.html#gaacada356d88add876dc7484892231e73">VBI_PIL_MINUTE</a> (pil));</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span> buffer;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">msg (<span class="keyword">const</span> <span class="keywordtype">char</span> * templ,</div><div class="line"> ...)</div><div class="line">{</div><div class="line"> va_list ap;</div><div class="line"></div><div class="line"> va_start (ap, templ);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (test_mode) {</div><div class="line"> <span class="keywordtype">char</span> buffer[80];</div><div class="line"> <span class="keyword">struct </span>tm tm;</div><div class="line"></div><div class="line"> memset (&tm, 0, <span class="keyword">sizeof</span> (tm));</div><div class="line"> localtime_r (&audience_time, &tm);</div><div class="line"> strftime (buffer, <span class="keyword">sizeof</span> (buffer), <span class="stringliteral">"%Y%m%dT%H%M%S "</span>, &tm);</div><div class="line"> fputs (buffer, stdout);</div><div class="line"> }</div><div class="line"></div><div class="line"> vprintf (templ, ap);</div><div class="line"></div><div class="line"> va_end (ap);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">remove_program_from_schedule (<span class="keyword">struct</span> program * p)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program **pp;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (p == curr_program) {</div><div class="line"> assert (quit</div><div class="line"> || VCR_STATE_STBY == vcr_state</div><div class="line"> || VCR_STATE_SCAN == vcr_state);</div><div class="line"></div><div class="line"> curr_program = NULL;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (pp = &schedule; NULL != *pp; pp = &(*pp)->next) {</div><div class="line"> <span class="keywordflow">if</span> (*pp == p) {</div><div class="line"> *pp = p->next;</div><div class="line"> free (p);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">remove_stale_programs_from_schedule (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"> <span class="keyword">struct </span>program *p_next;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (p = schedule; NULL != p; p = p_next) {</div><div class="line"> p_next = p->next;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (audience_time >= p->end_time</div><div class="line"> && audience_time >= p->pil_valid_end) {</div><div class="line"> msg (<span class="stringliteral">"PIL %s no longer valid, "</span></div><div class="line"> <span class="stringliteral">"removing program %u from schedule.\n"</span>,</div><div class="line"> pil_str (p->pil), p->index);</div><div class="line"> remove_program_from_schedule (p);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">struct </span>program *</div><div class="line">find_program_by_pil (<a class="code" href="group__ProgramID.html#ga870eeecedc6aed609b9f53d4dbe0f6c7">vbi_pil</a> pil)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (p = schedule; NULL != p; p = p->next) {</div><div class="line"> <span class="keywordflow">if</span> (pil == p->pil)</div><div class="line"> <span class="keywordflow">return</span> p;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span> NULL;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> *</div><div class="line">vcr_state_name (<span class="keyword">enum</span> vcr_state state)</div><div class="line">{</div><div class="line"> <span class="keywordflow">switch</span> (state) {</div><div class="line"><span class="preprocessor">#define CASE(x) case VCR_STATE_ ## x: return #x;</span></div><div class="line"> CASE (STBY)</div><div class="line"> CASE (SCAN)</div><div class="line"> CASE (PTR)</div><div class="line"> CASE (REC)</div><div class="line"><span class="preprocessor">#undef CASE</span></div><div class="line"> }</div><div class="line"></div><div class="line"> assert (0);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">change_vcr_state (<span class="keyword">enum</span> vcr_state new_state)</div><div class="line">{</div><div class="line"> <span class="keywordflow">if</span> (new_state == vcr_state)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"VCR state %s -> %s.\n"</span>,</div><div class="line"> vcr_state_name (vcr_state),</div><div class="line"> vcr_state_name (new_state));</div><div class="line"></div><div class="line"> vcr_state = new_state;</div><div class="line"> vcr_state_since = timestamp;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> vbi_bool</div><div class="line">teletext_8302_available (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordflow">return</span> (0 != (lc_state[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>].pil |</div><div class="line"> lc_state[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262afdd62439879c03c78fce236bf7dcc081">VBI_PID_CHANNEL_LCI_1</a>].pil |</div><div class="line"> lc_state[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262ac9a4d6e6932f0f4fd2a19f71f07f4447">VBI_PID_CHANNEL_LCI_2</a>].pil |</div><div class="line"> lc_state[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a969d34250cbd4e60539740cc4f3398f1">VBI_PID_CHANNEL_LCI_3</a>].pil));</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">disable_timer_control (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordflow">if</span> (!timer_control_mode)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> msg (<span class="stringliteral">"Leaving timer control mode.\n"</span>);</div><div class="line"> timer_control_mode = FALSE;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">enable_timer_control (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> msg (<span class="stringliteral">"Entering timer control mode.\n"</span>);</div><div class="line"> timer_control_mode = TRUE;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">stop_recording_now (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> assert (VCR_STATE_REC == vcr_state);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Program %u ended according to %s%s.\n"</span>,</div><div class="line"> curr_program->index,</div><div class="line"> timer_control_mode ? <span class="stringliteral">"schedule"</span> : <span class="stringliteral">"VPS/PDC signal"</span>,</div><div class="line"> (delayed_stop_at < DBL_MAX) ? <span class="stringliteral">" with delay"</span> : <span class="stringliteral">""</span>);</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_SCAN);</div><div class="line"></div><div class="line"> delayed_stop_at = DBL_MAX;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">stop_recording_in_30s (<span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> assert (VCR_STATE_REC == vcr_state);</div><div class="line"></div><div class="line"> <span class="comment">/* What triggered the stop. */</span></div><div class="line"> <span class="keywordflow">if</span> (NULL == pid) {</div><div class="line"> <span class="comment">/* Signal lost. */</span></div><div class="line"> memset (&delayed_stop_pid, 0,</div><div class="line"> <span class="keyword">sizeof</span> (delayed_stop_pid));</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> delayed_stop_pid = *pid;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* If we stop because the PIL is no longer transmitted we may</span></div><div class="line"><span class="comment"> need one second to realize (e.g. receiving LCI 0 at time t,</span></div><div class="line"><span class="comment"> LCI 1 at t + 0.2, then LCI 0 at t + 1, and again LCI 0 at</span></div><div class="line"><span class="comment"> t + 2 seconds) so we start counting 30 seconds not from</span></div><div class="line"><span class="comment"> the current time (t + 2) but the first time the label was</span></div><div class="line"><span class="comment"> missing (t + 1). */</span></div><div class="line"> <span class="keywordflow">if</span> (NULL == pid && 0 != curr_pid.<a name="a18"></a><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> delayed_stop_at = lc_state[curr_pid.<a name="a19"></a><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>].last_at + 31;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> delayed_stop_at = timestamp + 30;</div><div class="line"> }</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Will stop recording in %d seconds.\n"</span>,</div><div class="line"> (<span class="keywordtype">int</span>)(delayed_stop_at - timestamp));</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">start_recording_by_pil (<span class="keyword">struct</span> program * p,</div><div class="line"> <span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> assert (!timer_control_mode);</div><div class="line"> assert (VCR_STATE_SCAN == vcr_state</div><div class="line"> || VCR_STATE_PTR == vcr_state);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Recording program %u using VPS/PDC signal.\n"</span>,</div><div class="line"> p->index);</div><div class="line"></div><div class="line"> <span class="comment">/* EN 300 231 Section 9.4.1: "[When] labels are not received</span></div><div class="line"><span class="comment"> correctly during a recording, the recording will be</span></div><div class="line"><span class="comment"> continued for the computed duration following the actual</span></div><div class="line"><span class="comment"> start time" */</span></div><div class="line"> <span class="keywordflow">if</span> (!p->continues) {</div><div class="line"> p->end_time += audience_time - p->start_time;</div><div class="line"> p->start_time = audience_time;</div><div class="line"> p->continues = TRUE;</div><div class="line"> }</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_REC);</div><div class="line"></div><div class="line"> curr_program = p;</div><div class="line"> curr_pid = *pid;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">prepare_to_record_by_pil (<span class="keyword">struct</span> program * p,</div><div class="line"> <span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> assert (!timer_control_mode);</div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_PTR);</div><div class="line"></div><div class="line"> curr_program = p;</div><div class="line"> curr_pid = *pid;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">start_recording_by_timer (<span class="keyword">struct</span> program * p)</div><div class="line">{</div><div class="line"> assert (timer_control_mode);</div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Recording program %u using timer.\n"</span>,</div><div class="line"> p->index);</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_REC);</div><div class="line"></div><div class="line"> curr_program = p;</div><div class="line"> memset (&curr_pid, 0, <span class="keyword">sizeof</span> (curr_pid));</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">remove_program_if_ended (<span class="keyword">struct</span> program * p,</div><div class="line"> <span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode) {</div><div class="line"> <span class="comment">/* We don't know if the program really ends now, so we</span></div><div class="line"><span class="comment"> keep it scheduled until curr_program->pil_valid_end</span></div><div class="line"><span class="comment"> in case we receive its PIL after all. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (NULL != pid && <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea835908e1f719f7761589ac1f3b968cb4">VBI_PIL_INTERRUPTION</a> == pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> <span class="comment">/* The program pauses, will not be removed. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (NULL != pid && pid-><a name="a20"></a><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a>) {</div><div class="line"> <span class="comment">/* The program has been rescheduled to another date,</span></div><div class="line"><span class="comment"> we don't care in this example. */</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Objective accomplished. */</span></div><div class="line"> remove_program_from_schedule (p);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">signal_or_service_lost (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> enable_timer_control ();</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (vcr_state) {</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_STBY:</div><div class="line"> assert (0);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_SCAN:</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_PTR:</div><div class="line"> p = curr_program;</div><div class="line"></div><div class="line"> <span class="comment">/* According to EN 300 231 Section E.1 and Section E.3</span></div><div class="line"><span class="comment"> Example 12 the program should begin within one</span></div><div class="line"><span class="comment"> minute when PRF=1, so we start recording now. We</span></div><div class="line"><span class="comment"> will stop by PIL if we pick up a VPS or Teletext</span></div><div class="line"><span class="comment"> signal again before curr_program->end_time, but we</span></div><div class="line"><span class="comment"> will not return to VCR_STATE_PTR if PRF is still</span></div><div class="line"><span class="comment"> 1. */</span></div><div class="line"> msg (<span class="stringliteral">"Recording program %u using lost "</span></div><div class="line"> <span class="stringliteral">"PDC signal with PRF=1.\n"</span>,</div><div class="line"> p->index);</div><div class="line"></div><div class="line"> <span class="comment">/* Record for the scheduled duration... */</span></div><div class="line"> p->end_time = p->end_time - p->start_time + audience_time;</div><div class="line"> <span class="comment">/* ...plus one minute since PRF was set. */</span></div><div class="line"> p->end_time += 60 - MIN (vcr_state_since - timestamp,</div><div class="line"> 60.0);</div><div class="line"> p->start_time = audience_time;</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_REC);</div><div class="line"></div><div class="line"> <span class="comment">/* Now recording by timer. */</span></div><div class="line"> memset (&curr_pid, 0, <span class="keyword">sizeof</span> (curr_pid));</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_REC:</div><div class="line"> <span class="keywordflow">if</span> (delayed_stop_at < DBL_MAX) {</div><div class="line"> msg (<span class="stringliteral">"PDC signal lost; already stopping in "</span></div><div class="line"> <span class="stringliteral">"%d seconds.\n"</span>,</div><div class="line"> (<span class="keywordtype">int</span>)(delayed_stop_at - timestamp));</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (curr_program->start_time</div><div class="line"> == curr_program->end_time) {</div><div class="line"> <span class="comment">/* Since we don't know the program duration,</span></div><div class="line"><span class="comment"> we cannot record under timer control. We</span></div><div class="line"><span class="comment"> stop recording in 30 seconds as shown in EN</span></div><div class="line"><span class="comment"> 300 231 Annex E.3, Example 11, 16:20:10,</span></div><div class="line"><span class="comment"> but with an extra twist: If we receive</span></div><div class="line"><span class="comment"> curr_program->pil again within those 30</span></div><div class="line"><span class="comment"> seconds the stop will be canceled. */</span></div><div class="line"> stop_recording_in_30s (<span class="comment">/* pid */</span> NULL);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* Keep recording by timer. */</span></div><div class="line"> memset (&curr_pid, 0, <span class="keyword">sizeof</span> (curr_pid));</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">pil_no_longer_transmitted (<span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> vbi_bool mi;</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (vcr_state) {</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_STBY:</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_SCAN:</div><div class="line"> assert (0);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_PTR:</div><div class="line"> assert (!timer_control_mode);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"PIL %s is no longer present on LC %u.\n"</span>,</div><div class="line"> pil_str (curr_program->pil),</div><div class="line"> curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>);</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_SCAN);</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_REC:</div><div class="line"> assert (!timer_control_mode);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"PIL %s is no longer present on LC %u.\n"</span>,</div><div class="line"> pil_str (curr_program->pil),</div><div class="line"> curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (delayed_stop_at < DBL_MAX) {</div><div class="line"> msg (<span class="stringliteral">"Already stopping in %d seconds.\n"</span>,</div><div class="line"> (<span class="keywordtype">int</span>)(delayed_stop_at - timestamp));</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (NULL != pid</div><div class="line"> <span class="comment">/* EN 300 231 Annex E.3 Example 8. */</span></div><div class="line"> && !pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a></div><div class="line"> <span class="comment">/* EN 300 231 Section 6.2 p) and Annex E.3 Example 7 and</span></div><div class="line"><span class="comment"> 9. */</span></div><div class="line"> && (<a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea835908e1f719f7761589ac1f3b968cb4">VBI_PIL_INTERRUPTION</a> == pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a></div><div class="line"> || <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eaad30c6505f26ef2b6e26626afdff02dd">VBI_PIL_INHIBIT_TERMINATE</a> == pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>)) {</div><div class="line"> mi = pid-><a name="a21"></a><a class="code" href="structvbi__program__id.html#a9cccb9d3856e8b459d09e67cd12908d4">mi</a>;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* EN 300 231 is unclear about the expected response</span></div><div class="line"><span class="comment"> if a PIL with MI = 1 replaces a PIL with MI = 0 or</span></div><div class="line"><span class="comment"> vice versa. Section 6.2 p) suggests that only the</span></div><div class="line"><span class="comment"> MI flag of the old label determines when the</span></div><div class="line"><span class="comment"> program stops and Annex E.3 Example 1 to 7 are</span></div><div class="line"><span class="comment"> consistent with this interpretation, Example 10 is</span></div><div class="line"><span class="comment"> not. */</span></div><div class="line"> <span class="keywordflow">if</span> (0 == curr_pid.<a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> <span class="comment">/* Recording was started by timer. */</span></div><div class="line"> mi = TRUE;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> mi = curr_pid.<a class="code" href="structvbi__program__id.html#a9cccb9d3856e8b459d09e67cd12908d4">mi</a>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (mi) {</div><div class="line"> stop_recording_now ();</div><div class="line"> remove_program_if_ended (curr_program, pid);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> stop_recording_in_30s (pid);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/* Interruption or Recording Inhibit/Terminate service code. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">received_int_rit (<span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> <span class="keywordflow">switch</span> (vcr_state) {</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_STBY:</div><div class="line"> assert (0);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_SCAN:</div><div class="line"> disable_timer_control ();</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_PTR:</div><div class="line"> assert (!timer_control_mode);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> != curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>) {</div><div class="line"> msg (<span class="stringliteral">"Ignore %s/%02X with different LCI.\n"</span>,</div><div class="line"> pil_str (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>), pid-><a name="a22"></a><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_REC:</div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode) {</div><div class="line"> <span class="comment">/* Impossible to know if this service code</span></div><div class="line"><span class="comment"> refers to curr_program, so we keep</span></div><div class="line"><span class="comment"> recording for now. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> != curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>) {</div><div class="line"> msg (<span class="stringliteral">"Ignore %s/%02X with different LCI.\n"</span>,</div><div class="line"> pil_str (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>), pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> pil_no_longer_transmitted (pid);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">received_pil (<span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (vcr_state) {</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_STBY:</div><div class="line"> assert (0);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_SCAN:</div><div class="line"> disable_timer_control ();</div><div class="line"> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a>)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> p = find_program_by_pil (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_PTR:</div><div class="line"> assert (!timer_control_mode);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> != curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>) {</div><div class="line"> msg (<span class="stringliteral">"Ignore %s/%02X with different LCI.\n"</span>,</div><div class="line"> pil_str (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>), pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a>) {</div><div class="line"> pil_no_longer_transmitted (pid);</div><div class="line"></div><div class="line"> <span class="comment">/* This example does not support VCR</span></div><div class="line"><span class="comment"> reprogramming. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> != curr_pid.<a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> pil_no_longer_transmitted (pid);</div><div class="line"> p = find_program_by_pil (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a name="a23"></a><a class="code" href="structvbi__program__id.html#a35a130af99faf5714b96da7f3d08c48e">prf</a>) {</div><div class="line"> <span class="keywordflow">if</span> (timestamp >= vcr_state_since + 60) {</div><div class="line"> <span class="comment">/* EN 300 231 Section E.1,</span></div><div class="line"><span class="comment"> Section E.3 Example 12. */</span></div><div class="line"> msg (<span class="stringliteral">"Overriding stuck PRF flag.\n"</span>);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> msg (<span class="stringliteral">"Already prepared to record.\n"</span>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* PRF 1 -> 0, program starts now. */</span></div><div class="line"></div><div class="line"> start_recording_by_pil (curr_program, pid);</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_REC:</div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode) {</div><div class="line"> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a>) {</div><div class="line"> <span class="comment">/* Impossible to know if this service</span></div><div class="line"><span class="comment"> code refers to curr_program, so we</span></div><div class="line"><span class="comment"> keep recording for now. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> p = find_program_by_pil (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>);</div><div class="line"> <span class="keywordflow">if</span> (p == curr_program) {</div><div class="line"> disable_timer_control ();</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Continue recording using "</span></div><div class="line"> <span class="stringliteral">"VPS/PDC signal.\n"</span>);</div><div class="line"></div><div class="line"> curr_pid = *pid;</div><div class="line"></div><div class="line"> <span class="comment">/* Cancel a delayed stop because the</span></div><div class="line"><span class="comment"> program is evidently still</span></div><div class="line"><span class="comment"> running. */</span></div><div class="line"> delayed_stop_at = DBL_MAX;</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (NULL == p) {</div><div class="line"> <span class="comment">/* This program is not scheduled for</span></div><div class="line"><span class="comment"> recording but the network may</span></div><div class="line"><span class="comment"> transmit other PILs in parallel, so</span></div><div class="line"><span class="comment"> we allow some time to pick them up</span></div><div class="line"><span class="comment"> before we stop. */</span></div><div class="line"> stop_recording_in_30s (<span class="comment">/* pil */</span> NULL);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> disable_timer_control ();</div><div class="line"></div><div class="line"> <span class="comment">/* Perhaps in practice one should just</span></div><div class="line"><span class="comment"> open a new file and not restart</span></div><div class="line"><span class="comment"> capturing. */</span></div><div class="line"> stop_recording_now ();</div><div class="line"> }</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> != curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>) {</div><div class="line"> msg (<span class="stringliteral">"Ignore %s/%02X with different LCI.\n"</span>,</div><div class="line"> pil_str (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>), pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a>) {</div><div class="line"> pil_no_longer_transmitted (pid);</div><div class="line"></div><div class="line"> <span class="comment">/* This example does not support VCR</span></div><div class="line"><span class="comment"> reprogramming. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> == curr_pid.<a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> <span class="keywordflow">if</span> (delayed_stop_at < DBL_MAX) {</div><div class="line"> <span class="comment">/* We lost all PDC signals and</span></div><div class="line"><span class="comment"> timer_control() arranged for a</span></div><div class="line"><span class="comment"> delayed stop. Or we received an INT</span></div><div class="line"><span class="comment"> or RI/T code or a different PIL</span></div><div class="line"><span class="comment"> than curr_program->pil with</span></div><div class="line"><span class="comment"> MI=0. But now we receive</span></div><div class="line"><span class="comment"> curr_program->pil again. */</span></div><div class="line"> delayed_stop_at = DBL_MAX;</div><div class="line"> msg (<span class="stringliteral">"Delayed stop canceled.\n"</span>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* We lost all PDC signals and</span></div><div class="line"><span class="comment"> timer_control() started recording</span></div><div class="line"><span class="comment"> out of SCAN or PTR state, but now</span></div><div class="line"><span class="comment"> we receive curr_program->pil</span></div><div class="line"><span class="comment"> (again). Or this is just a</span></div><div class="line"><span class="comment"> retransmission of the PIL which</span></div><div class="line"><span class="comment"> started recording. Either way, we</span></div><div class="line"><span class="comment"> do not return to VCR_STATE_PTR if</span></div><div class="line"><span class="comment"> PRF is (still or again) 1. */</span></div><div class="line"> msg (<span class="stringliteral">"Already recording.\n"</span>);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> pil_no_longer_transmitted (pid);</div><div class="line"> <span class="keywordflow">if</span> (VCR_STATE_SCAN != vcr_state) {</div><div class="line"> <span class="comment">/* Stopping later. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> p = find_program_by_pil (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (NULL == p)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (pid-><a class="code" href="structvbi__program__id.html#a35a130af99faf5714b96da7f3d08c48e">prf</a>) {</div><div class="line"> prepare_to_record_by_pil (p, pid);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> start_recording_by_pil (p, pid);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line"><a name="_a24"></a><a class="code" href="structevent__handler.html">event_handler</a> (<a name="_a25"></a><a class="code" href="structvbi__event.html">vbi_event</a> * ev,</div><div class="line"> <span class="keywordtype">void</span> * user_data)</div><div class="line">{</div><div class="line"> <span class="keyword">const</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> *pid;</div><div class="line"> <a class="code" href="group__ProgramID.html#ga289ec10e87cef7655799144bc898f262">vbi_pid_channel</a> lci;</div><div class="line"></div><div class="line"> user_data = user_data; <span class="comment">/* unused, no warning please */</span></div><div class="line"></div><div class="line"> assert (VCR_STATE_STBY != vcr_state);</div><div class="line"></div><div class="line"> pid = ev-><a name="a26"></a>ev.<a name="a27"></a>prog_id;</div><div class="line"> lci = pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>;</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (lci) {</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>:</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262afdd62439879c03c78fce236bf7dcc081">VBI_PID_CHANNEL_LCI_1</a>:</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262ac9a4d6e6932f0f4fd2a19f71f07f4447">VBI_PID_CHANNEL_LCI_2</a>:</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a969d34250cbd4e60539740cc4f3398f1">VBI_PID_CHANNEL_LCI_3</a>:</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>:</div><div class="line"> <span class="comment">/* EN 300 231 Section 9.4.1: "When both line 16 (VPS)</span></div><div class="line"><span class="comment"> and Teletext-delivered labels are available</span></div><div class="line"><span class="comment"> simultaneously, decoders should default to the</span></div><div class="line"><span class="comment"> Teletext-delivered service;" */</span></div><div class="line"> <span class="keywordflow">if</span> (teletext_8302_available ())</div><div class="line"> <span class="keywordflow">goto</span> finish;</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">default</span>:</div><div class="line"> <span class="comment">/* Support for other sources not implemented yet. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Received PIL %s/%02X on LC %u.\n"</span>,</div><div class="line"> pil_str (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>), pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a>, lci);</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>) {</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eae7cbef095ebb79470785785bebdb2f8d">VBI_PIL_TIMER_CONTROL</a>:</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea4084b4cb78ad4667f7645101adebb3e1">VBI_PIL_CONTINUE</a>:</div><div class="line"> signal_or_service_lost ();</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea835908e1f719f7761589ac1f3b968cb4">VBI_PIL_INTERRUPTION</a>:</div><div class="line"> <span class="keywordflow">case</span> <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eaad30c6505f26ef2b6e26626afdff02dd">VBI_PIL_INHIBIT_TERMINATE</a>:</div><div class="line"> received_int_rit (pid);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">default</span>:</div><div class="line"> received_pil (pid);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> finish:</div><div class="line"> lc_state[lci].pil = pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a>;</div><div class="line"> lc_state[lci].last_at = timestamp;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> vbi_bool</div><div class="line">in_pil_validity_window (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (p = schedule; NULL != p; p = p->next) {</div><div class="line"> <span class="comment">/* The announced start and end time should fall within</span></div><div class="line"><span class="comment"> the PIL validity window, but just in case. */</span></div><div class="line"> <span class="keywordflow">if</span> ((audience_time >= p->start_time</div><div class="line"> && audience_time < p->end_time)</div><div class="line"> || (audience_time >= p->pil_valid_start</div><div class="line"> && audience_time < p->pil_valid_end))</div><div class="line"> <span class="keywordflow">return</span> TRUE;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span> FALSE;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">timer_control (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"></div><div class="line"> assert (timer_control_mode);</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (vcr_state) {</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_STBY:</div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_PTR:</div><div class="line"> assert (0);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_SCAN:</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> VCR_STATE_REC:</div><div class="line"> <span class="keywordflow">if</span> (delayed_stop_at < DBL_MAX) {</div><div class="line"> <span class="comment">/* Will stop later. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (audience_time >= curr_program->end_time) {</div><div class="line"> stop_recording_now ();</div><div class="line"></div><div class="line"> <span class="comment">/* We remove the program from the schedule as</span></div><div class="line"><span class="comment"> shown in EN 300 231 Annex E.3, Example 11,</span></div><div class="line"><span class="comment"> 01:58:00. However as the example itself</span></div><div class="line"><span class="comment"> demonstrates this is not in the best</span></div><div class="line"><span class="comment"> interest of the user. A better idea may be</span></div><div class="line"><span class="comment"> to keep the program scheduled until</span></div><div class="line"><span class="comment"> curr_program->pil_valid_end, in case the</span></div><div class="line"><span class="comment"> program is late or overrunning and we</span></div><div class="line"><span class="comment"> receive its PIL after all. */</span></div><div class="line"> remove_program_from_schedule (curr_program);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* Still running. */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (p = schedule; NULL != p; p = p->next) {</div><div class="line"> <span class="comment">/* Note if no program length has been specified</span></div><div class="line"><span class="comment"> (start_time == end_time) this function will not</span></div><div class="line"><span class="comment"> record the program. */</span></div><div class="line"> <span class="comment">/* We must also compare against p->end_time because we</span></div><div class="line"><span class="comment"> will not always remove the program from the</span></div><div class="line"><span class="comment"> schedule at that time. See</span></div><div class="line"><span class="comment"> remove_program_if_ended(). */</span></div><div class="line"> <span class="keywordflow">if</span> (audience_time >= p->start_time</div><div class="line"> && audience_time < p->end_time) {</div><div class="line"> start_recording_by_timer (p);</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">pdc_signal_check (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> ttx_chs =</div><div class="line"> ((1 << <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>) |</div><div class="line"> (1 << <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>) |</div><div class="line"> (1 << <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>) |</div><div class="line"> (1 << <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a51a53db4366781f100d5d02836d6452e">VBI_PID_CHANNEL_LCI_0</a>));</div><div class="line"> <span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> vps_ch =</div><div class="line"> (1 << <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>);</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> active_chs;</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> lost_chs;</div><div class="line"> <a class="code" href="group__ProgramID.html#ga289ec10e87cef7655799144bc898f262">vbi_pid_channel</a> i;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> <span class="comment">/* Determine if we lost signals. */</span></div><div class="line"></div><div class="line"> active_chs = 0;</div><div class="line"> lost_chs = 0;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (i = 0; i < <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>; ++i) {</div><div class="line"> <span class="keywordtype">double</span> timeout_at;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (0 == lc_state[i].pil)</div><div class="line"> <span class="keywordflow">continue</span>;</div><div class="line"></div><div class="line"> timeout_at = lc_state[i].last_at + signal_timeout[i];</div><div class="line"> <span class="keywordflow">if</span> (timestamp >= timeout_at) {</div><div class="line"> lost_chs |= 1 << i;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> active_chs |= 1 << i;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* For now only Teletext and VPS delivery is supported, so we</span></div><div class="line"><span class="comment"> don't check other channels. */</span></div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (0 == active_chs) {</div><div class="line"> <span class="keywordflow">if</span> (0 != lost_chs) {</div><div class="line"> msg (<span class="stringliteral">"All Teletext and VPS signals lost, "</span></div><div class="line"> <span class="stringliteral">"will fall back to timer control.\n"</span>);</div><div class="line"></div><div class="line"> signal_or_service_lost ();</div><div class="line"> }</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="keywordflow">if</span> (vps_ch == active_chs</div><div class="line"> && 0 != (lost_chs & ttx_chs)) {</div><div class="line"> msg (<span class="stringliteral">"Teletext signal lost, "</span></div><div class="line"> <span class="stringliteral">"will fall back to VPS.\n"</span>);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (curr_pid.<a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a></div><div class="line"> == lc_state[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>].pil) {</div><div class="line"> curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> = <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> ((VCR_STATE_PTR == vcr_state</div><div class="line"> || VCR_STATE_REC == vcr_state)</div><div class="line"> && 0 != curr_pid.<a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a></div><div class="line"> && 0 != (lost_chs & (1 << curr_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>))) {</div><div class="line"> <span class="comment">/* Note if multiple label channels are in use</span></div><div class="line"><span class="comment"> (Teletext only) a PIL may just "disappear"</span></div><div class="line"><span class="comment"> without a RI/T service code or other PIL</span></div><div class="line"><span class="comment"> subsequently transmitted on the same</span></div><div class="line"><span class="comment"> channel. */</span></div><div class="line"> pil_no_longer_transmitted (<span class="comment">/* pid */</span> NULL);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (0 != lost_chs) {</div><div class="line"> <span class="keywordflow">for</span> (i = 0; i < <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>; ++i) {</div><div class="line"> <span class="keywordflow">if</span> (0 == (lost_chs & (1 << i)))</div><div class="line"> <span class="keywordflow">continue</span>;</div><div class="line"></div><div class="line"> lc_state[i].pil = 0;</div><div class="line"> lc_state[i].last_at = timestamp;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">parse_test_file_line (time_t * timestamp,</div><div class="line"> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> * pid,</div><div class="line"> <span class="keyword">enum</span> vcr_state * exp_state,</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> line_counter,</div><div class="line"> <span class="keyword">const</span> <span class="keywordtype">char</span> * test_file_line)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>tm tm;</div><div class="line"> <span class="keyword">const</span> <span class="keywordtype">char</span> *s;</div><div class="line"> <span class="keywordtype">char</span> *s_end;</div><div class="line"> <span class="keyword">const</span> <span class="keywordtype">char</span> *detail;</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> ul;</div><div class="line"></div><div class="line"> <span class="comment">/* Test file format (based on examples in EN 300 231):</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> One line of text for each PID change, with 4 or 9 fields</span></div><div class="line"><span class="comment"> separated by one or more tabs or spaces:</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> 1. Name of the broadcasting network, e.g. BBC1.</span></div><div class="line"><span class="comment"> 2. Date of the change: yyyymmddThhmmss (local time)</span></div><div class="line"><span class="comment"> or yyyymmddThhmmssZ (UTC)</span></div><div class="line"><span class="comment"> Lines must be sorted by this date, oldest first. Dates</span></div><div class="line"><span class="comment"> must not repeat unless these lines have different LCI</span></div><div class="line"><span class="comment"> fields.</span></div><div class="line"><span class="comment"> 3. Label Channel Identifier (vbi_pid_channel): 0 ... n</span></div><div class="line"><span class="comment"> or the name VPS (channel 4).</span></div><div class="line"><span class="comment"> 4. Label Update Flag: 0 or 1.</span></div><div class="line"><span class="comment"> 5. Mode Identifier: 0 or 1 or x (any).</span></div><div class="line"><span class="comment"> 6. Prepare to Record Flag: 0 or 1 or x (any).</span></div><div class="line"><span class="comment"> 7. Program Identification Label: mmddThhmm or one of the</span></div><div class="line"><span class="comment"> names</span></div><div class="line"><span class="comment"> - TC (Timer Control code)</span></div><div class="line"><span class="comment"> - RI/T (Recording Inhibit/Terminate code)</span></div><div class="line"><span class="comment"> - INT (Interruption code)</span></div><div class="line"><span class="comment"> - CONT (Continuation code)</span></div><div class="line"><span class="comment"> - NSPV (No Specific PIL Value).</span></div><div class="line"><span class="comment"> A Program Type can be appended, separated by a slash:</span></div><div class="line"><span class="comment"> - /A to /Z (Series Code)</span></div><div class="line"><span class="comment"> - /NN (a hex number, e.g. /3F)</span></div><div class="line"><span class="comment"> 8. Channel or Network Identifier: a name like BBC1.</span></div><div class="line"><span class="comment"> 9. Expected VCR state:</span></div><div class="line"><span class="comment"> - STBY</span></div><div class="line"><span class="comment"> - SCAN</span></div><div class="line"><span class="comment"> - PTR</span></div><div class="line"><span class="comment"> - REC.</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> If fields 4 to 8 are omitted the transmission of the label</span></div><div class="line"><span class="comment"> on the given label channel ceases. If field 9 is omitted</span></div><div class="line"><span class="comment"> the same VCR state as before is expected.</span></div><div class="line"><span class="comment"></span></div><div class="line"><span class="comment"> All text after a number sign (#) is ignored.</span></div><div class="line"><span class="comment"> */</span></div><div class="line"></div><div class="line"> s = test_file_line;</div><div class="line"></div><div class="line"> <span class="comment">/* Network name ignored in this example. */</span></div><div class="line"> <span class="keywordflow">while</span> (isalnum (*s))</div><div class="line"> ++s;</div><div class="line"> detail = <span class="stringliteral">"channel field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (!isspace (*s))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> memset (&tm, 0, <span class="keyword">sizeof</span> (tm));</div><div class="line"> tm.tm_isdst = -1; <span class="comment">/* unknown */</span></div><div class="line"></div><div class="line"> s = strptime (s, <span class="stringliteral">"%n%Y%m%dT%H%M%S"</span>, &tm);</div><div class="line"> detail = <span class="stringliteral">"date field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (NULL == s)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'Z'</span> == *s) {</div><div class="line"> ++s;</div><div class="line"> *timestamp = timegm (&tm);</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> *timestamp = mktime (&tm);</div><div class="line"> }</div><div class="line"> <span class="keywordflow">if</span> ((time_t) -1 == *timestamp)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> memset (pid, 0, <span class="keyword">sizeof</span> (*pid));</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"VPS"</span>, 3)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> = <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a>;</div><div class="line"> s += 3;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 0);</div><div class="line"> detail = <span class="stringliteral">"LCI field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s</div><div class="line"> || ul >= (<span class="keywordtype">unsigned</span> <span class="keywordtype">long</span>) VBI_MAX_PID_CHANNELS)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a> = ul;</div><div class="line"> s = s_end;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (!isdigit (*s)) {</div><div class="line"> <span class="comment">/* Cease transmission on this label channel,</span></div><div class="line"><span class="comment"> pid->pil = 0. */</span></div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 0);</div><div class="line"> detail = <span class="stringliteral">"LUF field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s || ul > 1)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#abdd578b10dfa50e422cf90f200899d84">luf</a> = ul;</div><div class="line"> s = s_end;</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'x'</span> == *s) {</div><div class="line"> ++s;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 0);</div><div class="line"> detail = <span class="stringliteral">"MI field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s || ul > 1)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a9cccb9d3856e8b459d09e67cd12908d4">mi</a> = ul;</div><div class="line"> s = s_end;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'x'</span> == *s) {</div><div class="line"> ++s;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 0);</div><div class="line"> detail = <span class="stringliteral">"PRF field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s || ul > 1)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a35a130af99faf5714b96da7f3d08c48e">prf</a> = ul;</div><div class="line"> s = s_end;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"CONT"</span>, 4)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea4084b4cb78ad4667f7645101adebb3e1">VBI_PIL_CONTINUE</a>;</div><div class="line"> s += 4;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"END"</span>, 3)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a name="a28"></a><a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea6e6335917a64f23fc354338b033136a8">VBI_PIL_END</a>;</div><div class="line"> s += 3;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"INT"</span>, 3)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9ea835908e1f719f7761589ac1f3b968cb4">VBI_PIL_INTERRUPTION</a>;</div><div class="line"> s += 3;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"NSPV"</span>, 4)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eac9b7c435e58a2f76a87f1acb221630f9">VBI_PIL_NSPV</a>;</div><div class="line"> s += 4;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"RI/T"</span>, 4)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eaad30c6505f26ef2b6e26626afdff02dd">VBI_PIL_INHIBIT_TERMINATE</a>;</div><div class="line"> s += 4;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"TC"</span>, 2)) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a class="code" href="group__ProgramID.html#gga05589fbab0657f08285ebdfe93f5ec9eae7cbef095ebb79470785785bebdb2f8d">VBI_PIL_TIMER_CONTROL</a>;</div><div class="line"> s += 2;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 10);</div><div class="line"> detail = <span class="stringliteral">"PIL field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s</div><div class="line"> || ul % 100 > 31</div><div class="line"> || ul > 1531)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> s = s_end;</div><div class="line"> <span class="keywordflow">if</span> (ul > 0) {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> = <a name="a29"></a><a class="code" href="group__ProgramID.html#ga749715601c404fe3fb1d6273c950e9df">VBI_PIL</a> (ul / 100,</div><div class="line"> ul % 100, 0, 0);</div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'T'</span> != *s++)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> ul = strtoul (s, &s_end, 10);</div><div class="line"> <span class="keywordflow">if</span> (s_end == s</div><div class="line"> || ul % 100 > 63</div><div class="line"> || ul > 3163)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> s = s_end;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a30c0862c008de112f4222e1208038d58">pil</a> |= <a class="code" href="group__ProgramID.html#ga749715601c404fe3fb1d6273c950e9df">VBI_PIL</a> (0, 0,</div><div class="line"> ul / 100,</div><div class="line"> ul % 100);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'/'</span> == *s) {</div><div class="line"> <span class="keywordflow">do</span> ++s;</div><div class="line"> <span class="keywordflow">while</span> (isspace (*s));</div><div class="line"> <span class="keywordflow">if</span> (isalpha (s[0]) && 0x20 == s[1]) {</div><div class="line"> <span class="comment">/* Series code. This isn't magic, EN</span></div><div class="line"><span class="comment"> 300 231 just gives letters instead</span></div><div class="line"><span class="comment"> of the codes 0x80 ... 0xFF for</span></div><div class="line"><span class="comment"> easier reading. */</span></div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a> = 0x80 | *s++;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> ul = strtoul (s, &s_end, 16);</div><div class="line"> detail = <span class="stringliteral">"PTY field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (s_end == s || ul > 0xFF)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a> = ul;</div><div class="line"> s = s_end;</div><div class="line"> }</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a069f84e950d47f19a589cbe4a3603068">pty</a> = 0;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Network name ignored in this example. */</span></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">while</span> (isalnum (*s))</div><div class="line"> ++s;</div><div class="line"> detail = <span class="stringliteral">"CNI field"</span>;</div><div class="line"> <span class="keywordflow">if</span> (0 != *s && !isspace (*s))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> <span class="keywordflow">if</span> (<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a232efb61f9f576d83a778ad18b90e296">VBI_PID_CHANNEL_VPS</a> == pid-><a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>) {</div><div class="line"> pid-><a name="a30"></a><a class="code" href="structvbi__program__id.html#a5ac7312ef52b908f7b55996d6d12b3bb">cni_type</a> = VBI_CNI_TYPE_VPS;</div><div class="line"> pid-><a name="a31"></a><a class="code" href="structvbi__program__id.html#ab02c877d547812c4cc22df34e6b8794c">cni</a> = 0x1234;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#a5ac7312ef52b908f7b55996d6d12b3bb">cni_type</a> = VBI_CNI_TYPE_8302;</div><div class="line"> pid-><a class="code" href="structvbi__program__id.html#ab02c877d547812c4cc22df34e6b8794c">cni</a> = 0x1234;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'#'</span> == *s || 0 == *s) {</div><div class="line"> *exp_state = -1; <span class="comment">/* no change */</span></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"PTR"</span>, 3)) {</div><div class="line"> *exp_state = VCR_STATE_PTR;</div><div class="line"> s += 3;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"REC"</span>, 3)) {</div><div class="line"> *exp_state = VCR_STATE_REC;</div><div class="line"> s += 3;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"SCAN"</span>, 4)) {</div><div class="line"> *exp_state = VCR_STATE_SCAN;</div><div class="line"> s += 4;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (0 == strncmp (s, <span class="stringliteral">"STBY"</span>, 4)) {</div><div class="line"> *exp_state = VCR_STATE_STBY;</div><div class="line"> s += 4;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> detail = <span class="stringliteral">"VCR state field"</span>;</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'#'</span> == *s || 0 == *s)</div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> detail = <span class="stringliteral">"garbage at end of line"</span>;</div><div class="line"></div><div class="line"> invalid:</div><div class="line"> fprintf (stderr, <span class="stringliteral">"Error in test file line %u, %s:\n%s\n"</span>,</div><div class="line"> line_counter, detail, test_file_line);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">simulate_signals (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">static</span> <span class="keywordtype">char</span> buffer[256];</div><div class="line"> <span class="keyword">static</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> test_pid[<a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>];</div><div class="line"> <span class="keyword">static</span> <a class="code" href="structvbi__program__id.html">vbi_program_id</a> next_pid;</div><div class="line"> <span class="keyword">static</span> time_t next_event_time = 0;</div><div class="line"> <span class="keyword">static</span> <span class="keyword">enum</span> vcr_state next_exp_vcr_state = (<span class="keyword">enum</span> vcr_state) -1;</div><div class="line"> <span class="keyword">static</span> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> line_counter;</div><div class="line"> <a class="code" href="group__ProgramID.html#ga289ec10e87cef7655799144bc898f262">vbi_pid_channel</a> i;</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (timestamp >= next_event_time) {</div><div class="line"> <span class="keywordflow">if</span> (0 != buffer[0]) {</div><div class="line"> printf (<span class="stringliteral">"> %s"</span>, buffer);</div><div class="line"> test_pid[next_pid.<a class="code" href="structvbi__program__id.html#a90794327986da193ddb76d518331589d">channel</a>] = next_pid;</div><div class="line"> <span class="keywordflow">if</span> ((<span class="keyword">enum</span> vcr_state) -1 == next_exp_vcr_state)</div><div class="line"> test_exp_vcr_state = test_exp_vcr_state;</div><div class="line"> <span class="keywordflow">else</span></div><div class="line"> test_exp_vcr_state = next_exp_vcr_state;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (;;) {</div><div class="line"> <span class="keyword">const</span> <span class="keywordtype">char</span> *s;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (NULL == fgets (buffer, <span class="keyword">sizeof</span> (buffer),</div><div class="line"> stdin)) {</div><div class="line"> printf (<span class="stringliteral">"End of test file.\n"</span>);</div><div class="line"> next_event_time = INT_MAX;</div><div class="line"> quit = TRUE;</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> s = buffer;</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (isspace (*s))</div><div class="line"> ++s;</div><div class="line"> <span class="keywordflow">if</span> (0 == *s)</div><div class="line"> <span class="keywordflow">continue</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (<span class="charliteral">'#'</span> == *s) {</div><div class="line"> printf (<span class="stringliteral">"> %s"</span>, s);</div><div class="line"> <span class="keywordflow">continue</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> parse_test_file_line (&next_event_time,</div><div class="line"> &next_pid,</div><div class="line"> &next_exp_vcr_state,</div><div class="line"> line_counter, s);</div><div class="line"> ++line_counter;</div><div class="line"></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* See standby_loop(). */</span></div><div class="line"> audience_time = (time_t) timestamp;</div><div class="line"></div><div class="line"> <span class="comment">/* We stop recording before examining the received PIDs so we</span></div><div class="line"><span class="comment"> can respond to a new PID immediately. */</span></div><div class="line"> <span class="keywordflow">if</span> (VCR_STATE_REC == vcr_state</div><div class="line"> && timestamp >= delayed_stop_at) {</div><div class="line"> stop_recording_now ();</div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"> remove_program_if_ended (curr_program,</div><div class="line"> &delayed_stop_pid);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Note in reality PIDs may arrive in any order, with a delay</span></div><div class="line"><span class="comment"> of several frames between them. */</span></div><div class="line"> <span class="keywordflow">for</span> (i = 0; i < <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>; ++i) {</div><div class="line"> <span class="keywordflow">if</span> (0 != test_pid[i].pil) {</div><div class="line"> <a class="code" href="structvbi__event.html">vbi_event</a> ev;</div><div class="line"></div><div class="line"> memset (&ev, 0, <span class="keyword">sizeof</span> (ev));</div><div class="line"> ev.ev.prog_id = &test_pid[i];</div><div class="line"></div><div class="line"> <a class="code" href="structevent__handler.html">event_handler</a> (&ev, <span class="comment">/* user_data */</span> NULL);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">capture_and_decode_frame (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>timeval timeout;</div><div class="line"> <a name="_a32"></a><a class="code" href="structvbi__capture__buffer.html">vbi_capture_buffer</a> *sliced_buffer;</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> n_lines;</div><div class="line"> <span class="keywordtype">int</span> r;</div><div class="line"></div><div class="line"> <span class="comment">/* Don't wait more than two seconds for the driver</span></div><div class="line"><span class="comment"> to return data. */</span></div><div class="line"> timeout.tv_sec = 2;</div><div class="line"> timeout.tv_usec = 0;</div><div class="line"></div><div class="line"> r = <a name="a33"></a><a class="code" href="group__Device.html#ga58dcf2387b685b7c1c09a729f07893c8">vbi_capture_pull</a> (cap,</div><div class="line"> <span class="comment">/* raw_buffer */</span> NULL,</div><div class="line"> &sliced_buffer,</div><div class="line"> &timeout);</div><div class="line"> <span class="keywordflow">switch</span> (r) {</div><div class="line"> <span class="keywordflow">case</span> -1:</div><div class="line"> fprintf (stderr,</div><div class="line"> <span class="stringliteral">"VBI read error: %s.\n"</span>,</div><div class="line"> strerror (errno));</div><div class="line"> <span class="comment">/* Could be ignored, esp. EIO from some</span></div><div class="line"><span class="comment"> drivers. */</span></div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> 0: </div><div class="line"> fprintf (stderr, <span class="stringliteral">"VBI read timeout\n"</span>);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> 1: <span class="comment">/* success */</span></div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">default</span>:</div><div class="line"> assert (0);</div><div class="line"> }</div><div class="line"></div><div class="line"> timestamp = sliced_buffer->timestamp;</div><div class="line"> n_lines = sliced_buffer->size / <span class="keyword">sizeof</span> (<a name="_a34"></a><a class="code" href="structvbi__sliced.html">vbi_sliced</a>);</div><div class="line"></div><div class="line"> <span class="comment">/* See standby_loop(). */</span></div><div class="line"> audience_time = (time_t) timestamp;</div><div class="line"></div><div class="line"> <span class="comment">/* We stop recording before examining the received PIDs so we</span></div><div class="line"><span class="comment"> can respond to a new PID immediately. */</span></div><div class="line"> <span class="keywordflow">if</span> (VCR_STATE_REC == vcr_state</div><div class="line"> && timestamp >= delayed_stop_at) {</div><div class="line"> stop_recording_now ();</div><div class="line"> assert (VCR_STATE_SCAN == vcr_state);</div><div class="line"> remove_program_if_ended (curr_program,</div><div class="line"> &delayed_stop_pid);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Calls event_handler(). */</span></div><div class="line"> <a name="a35"></a><a class="code" href="group__Service.html#ga2e2201e3b2bab9b7fb1bb66ade716772">vbi_decode</a> (dec, (<a class="code" href="structvbi__sliced.html">vbi_sliced</a> *) sliced_buffer->data,</div><div class="line"> n_lines, timestamp);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">close_vbi_device (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <a name="a36"></a><a class="code" href="group__Device.html#gaec518b62f39b83c80b06cc0a4affbee6">vbi_capture_delete</a> (cap);</div><div class="line"> cap = NULL;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">open_vbi_device (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> vbi_service_set services;</div><div class="line"> <span class="keywordtype">char</span> *errstr;</div><div class="line"></div><div class="line"> services = (VBI_SLICED_TELETEXT_B |</div><div class="line"> VBI_SLICED_VPS);</div><div class="line"></div><div class="line"> cap = <a name="a37"></a><a class="code" href="group__Device.html#ga85ba4554b337ecb053168bb88f713e5f">vbi_capture_v4l2_new</a> (dev_name,</div><div class="line"> <span class="comment">/* buffers */</span> 5,</div><div class="line"> &services,</div><div class="line"> <span class="comment">/* strict */</span> 0,</div><div class="line"> &errstr,</div><div class="line"> <span class="comment">/* verbose */</span> FALSE);</div><div class="line"> <span class="keywordflow">if</span> (NULL == cap) {</div><div class="line"> fprintf (stderr,</div><div class="line"> <span class="stringliteral">"Cannot capture VBI data from %s "</span></div><div class="line"> <span class="stringliteral">"with V4L2 interface:\n"</span></div><div class="line"> <span class="stringliteral">"%s\n"</span>,</div><div class="line"> dev_name, errstr);</div><div class="line"></div><div class="line"> free (errstr);</div><div class="line"></div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/* We wait in this function until we receive the expected PIL(s) or a</span></div><div class="line"><span class="comment"> program starts and ends as scheduled, and record it. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">capture_loop (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordtype">double</span> last_timestamp;</div><div class="line"></div><div class="line"> assert (VCR_STATE_STBY == vcr_state);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (!test_mode)</div><div class="line"> open_vbi_device ();</div><div class="line"></div><div class="line"> <span class="comment">/* Reset the VBI decoder. */</span></div><div class="line"> <a name="a38"></a><a class="code" href="group__Service.html#ga76b4210fb90365e6f09426fcde7ebec6">vbi_channel_switched</a> (dec, 0);</div><div class="line"></div><div class="line"> change_vcr_state (VCR_STATE_SCAN);</div><div class="line"></div><div class="line"> last_timestamp = 0;</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (VCR_STATE_STBY != vcr_state && !quit) {</div><div class="line"> <span class="keywordflow">if</span> (test_mode) {</div><div class="line"> simulate_signals ();</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> capture_and_decode_frame ();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Once per second is enough. */</span></div><div class="line"> <span class="keywordflow">if</span> ((<span class="keywordtype">long</span>) last_timestamp != (<span class="keywordtype">long</span>) timestamp) {</div><div class="line"> <span class="keywordflow">if</span> (!timer_control_mode) {</div><div class="line"> <span class="comment">/* May enable timer control mode. */</span></div><div class="line"> pdc_signal_check ();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (timer_control_mode)</div><div class="line"> timer_control ();</div><div class="line"> }</div><div class="line"></div><div class="line"> last_timestamp = timestamp;</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (VCR_STATE_SCAN == vcr_state</div><div class="line"> && !in_pil_validity_window ()) {</div><div class="line"> change_vcr_state (VCR_STATE_STBY);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (test_mode) {</div><div class="line"> <span class="keywordflow">if</span> ((<span class="keyword">enum</span> vcr_state) -1 != test_exp_vcr_state</div><div class="line"> && test_exp_vcr_state != vcr_state) {</div><div class="line"> printf (<span class="stringliteral">"*** Unexpected VCR state %s\n"</span>,</div><div class="line"> vcr_state_name (vcr_state));</div><div class="line"></div><div class="line"> exit_code = EXIT_FAILURE;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Advance by one second. Note a VPS signal is</span></div><div class="line"><span class="comment"> transmitted on each frame, 25 times per</span></div><div class="line"><span class="comment"> second, but we simulate at most one PID</span></div><div class="line"><span class="comment"> change per second per label channel. */</span></div><div class="line"> ++timestamp;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (!test_mode)</div><div class="line"> close_vbi_device ();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/* We wait in this function until the starting time of the earliest</span></div><div class="line"><span class="comment"> program on the recording schedule is approaching. */</span></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">standby_loop (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordflow">while</span> (!quit) {</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"> time_t first_scan;</div><div class="line"></div><div class="line"> assert (VCR_STATE_STBY == vcr_state);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (test_mode) {</div><div class="line"> <span class="comment">/* Simulated current time. */</span></div><div class="line"> audience_time = (time_t) timestamp;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* The current time of the intended audience</span></div><div class="line"><span class="comment"> of the tuned in network according to the</span></div><div class="line"><span class="comment"> network. It may differ from system time if</span></div><div class="line"><span class="comment"> the system is not in sync with UTC or if we</span></div><div class="line"><span class="comment"> receive the TV signal with a delay. For</span></div><div class="line"><span class="comment"> simplicity we will not determine the offset</span></div><div class="line"><span class="comment"> in this example, see VBI_EVENT_LOCAL_TIME</span></div><div class="line"><span class="comment"> if you want to try that. */</span></div><div class="line"> audience_time = time (NULL);</div><div class="line"> }</div><div class="line"></div><div class="line"> remove_stale_programs_from_schedule ();</div><div class="line"> <span class="keywordflow">if</span> (NULL == schedule) {</div><div class="line"> printf (<span class="stringliteral">"Recording schedule is empty.\n"</span>);</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> first_scan = schedule->start_time;</div><div class="line"> <span class="keywordflow">for</span> (p = schedule; NULL != p; p = p->next) {</div><div class="line"> <span class="keywordflow">if</span> (p->start_time < first_scan)</div><div class="line"> first_scan = p->start_time;</div><div class="line"> <span class="keywordflow">if</span> (p->pil_valid_start < first_scan)</div><div class="line"> first_scan = p->pil_valid_start;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (first_scan > audience_time) {</div><div class="line"> <span class="keywordtype">char</span> buffer[80];</div><div class="line"> <span class="keyword">struct </span>tm tm;</div><div class="line"></div><div class="line"> memset (&tm, 0, <span class="keyword">sizeof</span> (tm));</div><div class="line"> localtime_r (&first_scan, &tm);</div><div class="line"> strftime (buffer, <span class="keyword">sizeof</span> (buffer),</div><div class="line"> <span class="stringliteral">"%Y-%m-%d %H:%M:%S %Z"</span>, &tm);</div><div class="line"></div><div class="line"> msg (<span class="stringliteral">"Sleeping until %s.\n"</span>, buffer);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (test_mode) {</div><div class="line"> audience_time = first_scan;</div><div class="line"> timestamp = first_scan;</div><div class="line"> } <span class="keywordflow">else</span> {</div><div class="line"> <span class="comment">/* In a loop because the sleep()</span></div><div class="line"><span class="comment"> function may abort earlier. */</span></div><div class="line"> sleep (first_scan - audience_time);</div><div class="line"></div><div class="line"> audience_time = time (NULL);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> capture_loop ();</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">reset_state (<span class="keywordtype">void</span>)</div><div class="line">{</div><div class="line"> <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> i;</div><div class="line"></div><div class="line"> audience_time = 0.0;</div><div class="line"> timestamp = 0.0;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (i = 0; i < <a class="code" href="group__ProgramID.html#gga289ec10e87cef7655799144bc898f262a4d686ec8bbbcbac78fb4526374cecb01">VBI_MAX_PID_CHANNELS</a>; ++i) {</div><div class="line"> lc_state[i].pil = 0; <span class="comment">/* none received */</span></div><div class="line"> lc_state[i].last_at = 0.0;</div><div class="line"> }</div><div class="line"></div><div class="line"> vcr_state = VCR_STATE_STBY;</div><div class="line"> vcr_state_since = 0.0;</div><div class="line"></div><div class="line"> timer_control_mode = FALSE;</div><div class="line"></div><div class="line"> delayed_stop_at = DBL_MAX;</div><div class="line"></div><div class="line"> test_exp_vcr_state = (<span class="keyword">enum</span> vcr_state) -1; <span class="comment">/* unknown */</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">add_program_to_schedule (<span class="keyword">const</span> <span class="keyword">struct</span> tm * start_tm,</div><div class="line"> <span class="keyword">const</span> <span class="keyword">struct</span> tm * end_tm,</div><div class="line"> <span class="keyword">const</span> <span class="keyword">struct</span> tm * pdc_tm)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>program *p;</div><div class="line"> <span class="keyword">struct </span>program **pp;</div><div class="line"> <span class="keyword">struct </span>tm tm;</div><div class="line"> time_t pil_time;</div><div class="line"></div><div class="line"> <span class="comment">/* Note PILs represent the originally announced start date of</span></div><div class="line"><span class="comment"> the program in the time zone of the intended audience. When</span></div><div class="line"><span class="comment"> we convert pdc_tm to a PIL we assume that zone is the same</span></div><div class="line"><span class="comment"> as the system time zone (TZ environment variable), and</span></div><div class="line"><span class="comment"> start_tm, end_tm and pdc_tm are also given relative to this</span></div><div class="line"><span class="comment"> time zone. We do not consider the case where a program</span></div><div class="line"><span class="comment"> straddles a daylight saving time discontinuity, e.g. starts</span></div><div class="line"><span class="comment"> in the CET zone and ends in the CEST zone. */</span></div><div class="line"></div><div class="line"> p = calloc (1, <span class="keyword">sizeof</span> (*p));</div><div class="line"> assert (NULL != p);</div><div class="line"></div><div class="line"> tm = *start_tm;</div><div class="line"> tm.tm_isdst = -1; <span class="comment">/* unknown */</span></div><div class="line"> p->start_time = mktime (&tm);</div><div class="line"> <span class="keywordflow">if</span> ((time_t) -1 == p->start_time) {</div><div class="line"> fprintf (stderr, <span class="stringliteral">"Invalid start time.\n"</span>);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line"></div><div class="line"> tm = *start_tm;</div><div class="line"> tm.tm_isdst = -1; <span class="comment">/* unknown */</span></div><div class="line"> tm.tm_hour = end_tm->tm_hour;</div><div class="line"> tm.tm_min = end_tm->tm_min;</div><div class="line"> <span class="keywordflow">if</span> (end_tm->tm_hour < start_tm->tm_hour) {</div><div class="line"> <span class="comment">/* mktime() should handle a 32nd. */</span></div><div class="line"> ++tm.tm_mday;</div><div class="line"> }</div><div class="line"> p->end_time = mktime (&tm);</div><div class="line"> <span class="keywordflow">if</span> ((time_t) -1 == p->end_time) {</div><div class="line"> fprintf (stderr, <span class="stringliteral">"Invalid end time.\n"</span>);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line"></div><div class="line"> tm = *start_tm;</div><div class="line"> tm.tm_isdst = -1; <span class="comment">/* unknown */</span></div><div class="line"> tm.tm_hour = pdc_tm->tm_hour;</div><div class="line"> tm.tm_min = pdc_tm->tm_min;</div><div class="line"> <span class="keywordflow">if</span> (pdc_tm->tm_hour >= start_tm->tm_hour + 12) {</div><div class="line"> <span class="comment">/* mktime() should handle a 0th. */</span></div><div class="line"> --tm.tm_mday;</div><div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (pdc_tm->tm_hour + 12 < start_tm->tm_hour) {</div><div class="line"> ++tm.tm_mday;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/* Normalize day and month. */</span></div><div class="line"> pil_time = mktime (&tm);</div><div class="line"> <span class="keywordflow">if</span> ((time_t) -1 == pil_time</div><div class="line"> || NULL == localtime_r (&pil_time, &tm)) {</div><div class="line"> fprintf (stderr, <span class="stringliteral">"Cannot determine PIL month/day.\n"</span>);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line"></div><div class="line"> p->pil = <a class="code" href="group__ProgramID.html#ga749715601c404fe3fb1d6273c950e9df">VBI_PIL</a> (tm.tm_mon + 1, <span class="comment">/* 1 ... 12 */</span></div><div class="line"> tm.tm_mday,</div><div class="line"> tm.tm_hour,</div><div class="line"> tm.tm_min);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (!<a name="a39"></a><a class="code" href="group__ProgramID.html#gac256d7e11100b73967b68cd90d578b0e">vbi_pil_validity_window</a> (&p->pil_valid_start,</div><div class="line"> &p->pil_valid_end,</div><div class="line"> p->pil,</div><div class="line"> p->start_time,</div><div class="line"> NULL <span class="comment">/* system tz */</span>)) {</div><div class="line"> fprintf (stderr, <span class="stringliteral">"Cannot determine PIL validity.\n"</span>);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line"></div><div class="line"> p->index = 0;</div><div class="line"> <span class="keywordflow">for</span> (pp = &schedule; NULL != *pp; pp = &(*pp)->next)</div><div class="line"> ++p->index;</div><div class="line"></div><div class="line"> *pp = p;</div><div class="line"></div><div class="line"> if (0) {</div><div class="line"> printf (<span class="stringliteral">"Program %u start: "</span>, p->index);</div><div class="line"> print_time (p->start_time);</div><div class="line"> printf (<span class="stringliteral">"End: "</span>);</div><div class="line"> print_time (p->end_time);</div><div class="line"> printf (<span class="stringliteral">"PIL: "</span>);</div><div class="line"> print_time (pil_time);</div><div class="line"> printf (<span class="stringliteral">"PIL valid from: "</span>);</div><div class="line"> print_time (p->pil_valid_start);</div><div class="line"> printf (<span class="stringliteral">"PIL valid until: "</span>);</div><div class="line"> print_time (p->pil_valid_end);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">usage (FILE * fp)</div><div class="line">{</div><div class="line"> fprintf (fp,</div><div class="line"><span class="stringliteral">"Please specify the start time of a program in the format\n"</span></div><div class="line"><span class="stringliteral">"YYYY-MM-DD HH:MM, the end time HH:MM and a VPS/PDC time HH:MM.\n"</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span></div><div class="line">parse_args (<span class="keywordtype">int</span> argc,</div><div class="line"> <span class="keywordtype">char</span> ** argv)</div><div class="line">{</div><div class="line"> <span class="keyword">struct </span>tm start_tm;</div><div class="line"> <span class="keyword">struct </span>tm end_tm;</div><div class="line"> <span class="keyword">struct </span>tm pdc_tm;</div><div class="line"></div><div class="line"> dev_name = <span class="stringliteral">"/dev/vbi"</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">for</span> (;;) {</div><div class="line"> <span class="keywordtype">int</span> c;</div><div class="line"></div><div class="line"> c = getopt (argc, argv, <span class="stringliteral">"d:ht"</span>);</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (-1 == c)</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">switch</span> (c) {</div><div class="line"> <span class="keywordflow">case</span> <span class="charliteral">'d'</span>:</div><div class="line"> dev_name = optarg;</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> <span class="charliteral">'h'</span>:</div><div class="line"> usage (stdout);</div><div class="line"> exit (EXIT_SUCCESS);</div><div class="line"></div><div class="line"> <span class="keywordflow">case</span> <span class="charliteral">'t'</span>:</div><div class="line"> test_mode = TRUE;</div><div class="line"> <span class="keywordflow">break</span>;</div><div class="line"></div><div class="line"> <span class="keywordflow">default</span>:</div><div class="line"> usage (stderr);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (argc - optind >= 4) {</div><div class="line"> memset (&start_tm, 0, <span class="keyword">sizeof</span> (<span class="keyword">struct</span> tm));</div><div class="line"> <span class="keywordflow">if</span> (NULL == strptime (argv[optind + 0], <span class="stringliteral">"%Y-%m-%d"</span>,</div><div class="line"> &start_tm))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"> <span class="keywordflow">if</span> (NULL == strptime (argv[optind + 1], <span class="stringliteral">"%H:%M"</span>,</div><div class="line"> &start_tm))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> memset (&end_tm, 0, <span class="keyword">sizeof</span> (<span class="keyword">struct</span> tm));</div><div class="line"> <span class="keywordflow">if</span> (NULL == strptime (argv[optind + 2], <span class="stringliteral">"%H:%M"</span>,</div><div class="line"> &end_tm))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> memset (&pdc_tm, 0, <span class="keyword">sizeof</span> (<span class="keyword">struct</span> tm));</div><div class="line"> <span class="keywordflow">if</span> (NULL == strptime (argv[optind + 3], <span class="stringliteral">"%H:%M"</span>,</div><div class="line"> &pdc_tm))</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> add_program_to_schedule (&start_tm, &end_tm, &pdc_tm);</div><div class="line"></div><div class="line"> optind += 4;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keywordflow">if</span> (argc != optind)</div><div class="line"> <span class="keywordflow">goto</span> invalid;</div><div class="line"></div><div class="line"> <span class="keywordflow">return</span>;</div><div class="line"></div><div class="line"> invalid:</div><div class="line"> usage (stderr);</div><div class="line"> exit (EXIT_FAILURE);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keywordtype">int</span></div><div class="line">main (<span class="keywordtype">int</span> argc,</div><div class="line"> <span class="keywordtype">char</span> ** argv)</div><div class="line">{</div><div class="line"> vbi_bool success;</div><div class="line"></div><div class="line"> setlocale (LC_ALL, <span class="stringliteral">""</span>);</div><div class="line"></div><div class="line"> parse_args (argc, argv);</div><div class="line"></div><div class="line"> exit_code = EXIT_SUCCESS;</div><div class="line"></div><div class="line"> dec = <a name="a40"></a><a class="code" href="group__Service.html#ga3c09b7fa196326f51409a91e2857ba5e">vbi_decoder_new</a> ();</div><div class="line"> assert (NULL != dec);</div><div class="line"></div><div class="line"> success = <a name="a41"></a><a class="code" href="group__Event.html#ga6692bb41583ca67af5701360f321e3d7">vbi_event_handler_register</a> (dec, VBI_EVENT_PROG_ID,</div><div class="line"> <a class="code" href="structevent__handler.html">event_handler</a>,</div><div class="line"> <span class="comment">/* user_data */</span> NULL);</div><div class="line"> assert (success);</div><div class="line"></div><div class="line"> reset_state ();</div><div class="line"></div><div class="line"> standby_loop ();</div><div class="line"></div><div class="line"> <a name="a42"></a><a class="code" href="group__Service.html#gab3f52f0b71746c9081c053a33cad7bce">vbi_decoder_delete</a> (dec);</div><div class="line"></div><div class="line"> <span class="keywordflow">while</span> (NULL != schedule)</div><div class="line"> remove_program_from_schedule (schedule);</div><div class="line"></div><div class="line"> exit (exit_code);</div><div class="line">}</div></div><!-- fragment --> </div><!-- contents --> <!-- start footer part --> <hr class="footer"/><address class="footer"><small> Generated by  <a href="http://www.doxygen.org/index.html"> <img class="footer" src="doxygen.png" alt="doxygen"/> </a> 1.8.11 </small></address> </body> </html>