diff --git a/Makefile b/Makefile index cd011db2..cd79dad8 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,8 @@ SRCS = src/main.c \ src/config2.c \ src/lang_codes.c \ src/lang_str.c \ - src/imagecache.c + src/imagecache.c \ + src/tvhtime.c SRCS += src/epggrab/module.c\ src/epggrab/channel.c\ diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 2dbe5474..18d6d7ad 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,7 @@ #include "psi.h" #include "notify.h" #include "cwc.h" +#include "tvhtime.h" #if TDT_TRACE #define TRACE(_pre, _fmt, ...)\ @@ -1074,6 +1076,54 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return 0; } +/* + * Time Offset table handler + */ +static int +dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, + uint8_t tableid, void *opaque) +{ + uint16_t mjd; + uint8_t hour, min, sec; + int year, mon, day; + struct tm utc; + + if (tableid != 0x73) + return -1; + + /* DVB format MJD, Hour, Min, Sec */ + mjd = (buf[0] << 8) | buf[1]; + hour = bcdtoint(buf[2]); + min = bcdtoint(buf[3]); + sec = bcdtoint(buf[4]); + + /* Convert MJD (using algo from EN 300 468 v1.13.1 Annex C) */ + year = (int)((mjd - 15078.2) / 365.25); + mon = (int)((mjd - 14956.1 - (int)(year * 365.25)) / 30.6001); + day = mjd - 14956 - (int)(year * 365.25) - (int)(mon * 30.6001); + if (mon == 14 || mon == 15) { + year++; + mon -= 12; + } + mon--; + + tvhlog(LOG_DEBUG, "tdt-tot", "time is %04d/%02d/%02d %02d:%02d:%02d", + year+1900, mon, day, hour, min, sec); + + /* Convert to UTC time_t */ + utc.tm_wday = 0; + utc.tm_yday = 0; + utc.tm_isdst = 0; + utc.tm_year = year; + utc.tm_mon = mon - 1; + utc.tm_mday = day; + utc.tm_hour = hour; + utc.tm_min = min; + utc.tm_sec = sec; + tvhtime_update(&utc); + + return 0; +} /** * Demux for default DVB tables that we want @@ -1097,6 +1147,10 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11", TDT_QUICKREQ | TDT_CRC, 0x11); + + /* Time Offset Table */ + + tdt_add(tdmi, 0, 0, dvb_tot_callback, NULL, "tot", TDT_CRC, 0x14); } diff --git a/src/tvhtime.c b/src/tvhtime.c new file mode 100644 index 00000000..66c55b89 --- /dev/null +++ b/src/tvhtime.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvhtime.h" +#include "tvheadend.h" +#include "settings.h" + +uint32_t tvhtime_update_enabled; +uint32_t tvhtime_ntp_enabled; +uint32_t tvhtime_tolerance; + +/* + * NTP processing + */ +#define NTPD_BASE 0x4e545030 /* "NTP0" */ +#define NTPD_UNIT 2 + +typedef struct +{ + int mode; /* 0 - if valid set + * use values, + * clear valid + * 1 - if valid set + * if count before and after read of values is equal, + * use values + * clear valid + */ + int count; + time_t clockTimeStampSec; + int clockTimeStampUSec; + time_t receiveTimeStampSec; + int receiveTimeStampUSec; + int leap; + int precision; + int nsamples; + int valid; + int pad[10]; +} ntp_shm_t; + +static ntp_shm_t * +ntp_shm_init ( void ) +{ + int shmid, unit, mode; + static ntp_shm_t *shmptr = NULL; + + if (shmptr != NULL) + return shmptr; + + unit = getuid() ? 2 : 0; + mode = getuid() ? 0666 : 0600; + + shmid = shmget((key_t)NTPD_BASE + unit, sizeof(ntp_shm_t), IPC_CREAT | mode); + if (shmid == -1) + return NULL; + + shmptr = shmat(shmid, 0, 0); + memset(shmptr, 0, sizeof(ntp_shm_t)); + if (shmptr) { + shmptr->mode = 1; + shmptr->precision = -1; + shmptr->nsamples = 1; + } + + return shmptr; +} + +/* + * Update time + */ +void +tvhtime_update ( struct tm *tm ) +{ + time_t now; + struct timeval tv; + ntp_shm_t *ntp_shm; + int64_t t1, t2; + + /* Current and reported time */ + now = mktime(tm); + gettimeofday(&tv, NULL); + + /* Delta */ + t1 = now * 1000000; + t2 = tv.tv_sec * 1000000 + tv.tv_usec; +#if NTP_TRACE + tvhlog(LOG_DEBUG, "ntp", "delta = %"PRId64" us\n", t2 - t1); +#endif + + /* Update local clock */ + if (tvhtime_update_enabled) + if (llabs(t2 - t1) > tvhtime_tolerance) + stime(&now); + + /* NTP */ + if (tvhtime_ntp_enabled) { + if (!(ntp_shm = ntp_shm_init())) + return; + + ntp_shm->valid = 0; + ntp_shm->count++; + ntp_shm->clockTimeStampSec = now; + ntp_shm->clockTimeStampUSec = 0; + ntp_shm->receiveTimeStampSec = tv.tv_sec; + ntp_shm->receiveTimeStampUSec = (int)tv.tv_usec; + ntp_shm->count++; + ntp_shm->valid = 1; + } +} + +/* Initialise */ +void tvhtime_init ( void ) +{ + htsmsg_t *m = hts_settings_load("tvhtime/config"); + if (htsmsg_get_u32(m, "update_enabled", &tvhtime_update_enabled)) + tvhtime_update_enabled = 0; + if (htsmsg_get_u32(m, "ntp_enabled", &tvhtime_ntp_enabled)) + tvhtime_ntp_enabled = 0; + if (htsmsg_get_u32(m, "tolerance", &tvhtime_tolerance)) + tvhtime_tolerance = 5000; +} + +static void tvhtime_save ( void ) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_u32(m, "update_enabled", tvhtime_update_enabled); + htsmsg_add_u32(m, "ntp_enabled", tvhtime_ntp_enabled); + htsmsg_add_u32(m, "tolerance", tvhtime_tolerance); + hts_settings_save(m, "tvhtime/config"); +} + +void tvhtime_set_update_enabled ( uint32_t on ) +{ + if (tvhtime_update_enabled == on) + return; + tvhtime_update_enabled = on; + tvhtime_save(); +} + +void tvhtime_set_ntp_enabled ( uint32_t on ) +{ + if (tvhtime_ntp_enabled == on) + return; + tvhtime_ntp_enabled = on; + tvhtime_save(); +} + +void tvhtime_set_tolerance ( uint32_t v ) +{ + if (tvhtime_tolerance == v) + return; + tvhtime_tolerance = v; + tvhtime_save(); +} diff --git a/src/tvhtime.h b/src/tvhtime.h new file mode 100644 index 00000000..3829ce52 --- /dev/null +++ b/src/tvhtime.h @@ -0,0 +1,34 @@ +/* + * TVheadend - time processing + * + * Copyright (C) 2013 Adam Sutton + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TVH_TIME_H__ +#define __TVH_TIME_H_ + +extern uint32_t tvhtime_update_enabled; +extern uint32_t tvhtime_ntp_enabled; +extern uint32_t tvhtime_tolerance; + +void tvhtime_init ( void ); +void tvhtime_update ( struct tm *now ); + +void tvhtime_set_update_enabled ( uint32_t on ); +void tvhtime_set_ntp_enabled ( uint32_t on ); +void tvhtime_set_tolerance ( uint32_t v ); + +#endif /* __TVH_TIME_H__ */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index c11ec87a..45ecfed1 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -49,6 +49,7 @@ #include "subscriptions.h" #include "imagecache.h" #include "timeshift.h" +#include "tvhtime.h" /** * @@ -1974,6 +1975,12 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) /* Misc */ pthread_mutex_lock(&global_lock); m = config_get_all(); + + /* Time */ + htsmsg_add_u32(m, "tvhtime_update_enabled", tvhtime_update_enabled); + htsmsg_add_u32(m, "tvhtime_ntp_enabled", tvhtime_ntp_enabled); + htsmsg_add_u32(m, "tvhtime_tolerance", tvhtime_tolerance); + pthread_mutex_unlock(&global_lock); /* Image cache */ @@ -2001,6 +2008,15 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) save |= config_set_language(str); if (save) config_save(); + + /* Time */ + if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_update_enabled"))) + tvhtime_set_update_enabled(!!str); + if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_ntp_enabled"))) + tvhtime_set_ntp_enabled(!!str); + if ((str = http_arg_get(&hc->hc_req_args, "tvhtime_tolerance"))) + tvhtime_set_tolerance(atoi(str)); + pthread_mutex_unlock(&global_lock); /* Image Cache */ diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index 183b8eef..a7d84bbb 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -39,7 +39,9 @@ tvheadend.miscconf = function() { root : 'config' }, [ 'muxconfpath', 'language', 'imagecache_enabled', 'imagecache_ok_period', - 'imagecache_fail_period', 'imagecache_ignore_sslcert']); + 'imagecache_fail_period', 'imagecache_ignore_sslcert', + 'tvhtime_update_enabled', 'tvhtime_ntp_enabled', + 'tvhtime_tolerance']); /* **************************************************************** * Form Fields @@ -75,6 +77,32 @@ tvheadend.miscconf = function() { fromLegend: 'Available' }); + /* + * Time/Date + */ + var tvhtimeUpdateEnabled = new Ext.form.Checkbox({ + name: 'tvhtime_update_enabled', + fieldLabel: 'Update time' + }); + + var tvhtimeNtpEnabled = new Ext.form.Checkbox({ + name: 'tvhtime_ntp_enabled', + fieldLabel: 'Enable NTP driver' + }); + + var tvhtimeTolerance = new Ext.form.NumberField({ + name: 'tvhtime_tolerance', + fieldLabel: 'Update tolerance (ms)' + }); + + var tvhtimePanel = new Ext.form.FieldSet({ + title: 'Time Update', + width: 700, + autoHeight: true, + collapsible: true, + items : [ tvhtimeUpdateEnabled, tvhtimeNtpEnabled, tvhtimeTolerance ] + }); + /* * Image cache */ @@ -140,7 +168,7 @@ tvheadend.miscconf = function() { defaultType : 'textfield', autoHeight : true, items : [ language, dvbscanPath, - imagecachePanel ], + imagecachePanel, tvhtimePanel ], tbar : [ saveButton, '->', helpButton ] });