From dc75437202d71b79af3b4c85c4d2f6983000d212 Mon Sep 17 00:00:00 2001 From: Richard Kunze Date: Wed, 26 Sep 2012 00:37:41 +0200 Subject: [PATCH 001/503] Fix race conditions in _epggrab_ota_finished() and epggrab_mux_next() --- src/epggrab/otamux.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 2dd9aa3e..e0e6b9e4 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -89,9 +89,10 @@ th_dvb_mux_instance_t *epggrab_mux_next ( th_dvb_adapter_t *tda ) epggrab_ota_mux_t *ota; time(&now); TAILQ_FOREACH(ota, &ota_mux_all, glob_link) { + if (ota->tdmi->tdmi_adapter != tda) continue; if (ota->interval + ota->completed > now) return NULL; if (!ota->is_reg) return NULL; - if (ota->tdmi->tdmi_adapter == tda) break; + break; } return ota ? ota->tdmi : NULL; } @@ -334,7 +335,21 @@ static void _epggrab_ota_finished ( epggrab_ota_mux_t *ota ) /* Reinsert into reg queue */ else { TAILQ_REMOVE(&ota_mux_all, ota, glob_link); - TAILQ_INSERT_SORTED(&ota_mux_all, ota, glob_link, _ota_time_cmp); + // Find the last queue entry that can run before ota + // (i.e _ota_time_cmp(ota, entry)>0) and re-insert ota + // directly after this entry. If no matching entry is + // found (i.e ota can run before any other entry), + // re-insert ota at the queue head. + epggrab_ota_mux_t *entry = NULL; + epggrab_ota_mux_t *tmp; + TAILQ_FOREACH(tmp, &ota_mux_all, glob_link) { + if(_ota_time_cmp(ota, tmp)>0) entry = tmp; + } + if (entry) { + TAILQ_INSERT_AFTER(&ota_mux_all, entry, ota, glob_link); + } else { + TAILQ_INSERT_HEAD(&ota_mux_all, ota, glob_link); + } } } From 7b6ed15eb9103dd4d70964bd37e6ea8bf5110221 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 14 Oct 2012 11:22:04 +0100 Subject: [PATCH 002/503] Fix open file handle bug and reduce mux config scan depth. Fixes #1325. --- src/muxes.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/muxes.c b/src/muxes.c index 8629d264..6783fd85 100644 --- a/src/muxes.c +++ b/src/muxes.c @@ -289,7 +289,10 @@ static void _muxes_load_file str++; } } - if (!reg) return; + if (!reg) { + fb_close(fp); + return; + } /* Network */ str = buf; @@ -338,19 +341,22 @@ static void _muxes_load_file * * Note: should we follow symlinks? */ -static void _muxes_load_dir ( const char *path, const char *type ) +static void _muxes_load_dir + ( const char *path, const char *type, int lvl ) { char p[256]; fb_dir *dir; fb_dirent *de; + if (lvl >= 3) return; if (!(dir = fb_opendir(path))) return; + lvl++; while ((de = fb_readdir(dir))) { if (*de->name == '.') continue; if (de->type == FB_DIR) { snprintf(p, sizeof(p), "%s/%s", path, de->name); - _muxes_load_dir(p, de->name); + _muxes_load_dir(p, de->name, lvl+1); } else if (type) { _muxes_load_file(type, dir, de->name); } @@ -371,5 +377,5 @@ void muxes_init ( void ) #else path = "/usr/share/dvb"; #endif - _muxes_load_dir(path, NULL); + _muxes_load_dir(path, NULL, 0); } From 71d6adfed00f7d3d3693dded526883054500d942 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 15 Oct 2012 10:35:11 +0100 Subject: [PATCH 003/503] Ensure ONID is updated at some point, this still may not work, but worth a shot. --- src/dvb/dvb.h | 2 ++ src/dvb/dvb_multiplex.c | 18 ++++++++++++++++++ src/dvb/dvb_tables.c | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index c29bef1a..60258e7c 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -364,6 +364,8 @@ void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid); +void dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid); + void dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled); void dvb_mux_set_satconf(th_dvb_mux_instance_t *tdmi, const char *scid, diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 357a2816..0c4c1273 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -865,6 +865,24 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid) notify_by_msg("dvbMux", m); } +/** + * + */ +void +dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid) +{ + htsmsg_t *m; + + tdmi->tdmi_network_id = onid; + + dvb_mux_save(tdmi); + + m = htsmsg_create_map(); + htsmsg_add_str(m, "id", tdmi->tdmi_identifier); + htsmsg_add_u32(m, "onid", tdmi->tdmi_network_id); + notify_by_msg("dvbMux", m); +} + /** * diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index dea5892e..7f4c2ebb 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -463,8 +463,10 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tsid = ptr[0] << 8 | ptr[1]; onid = ptr[5] << 8 | ptr[6]; if (tableid == 0x42) { - if(tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) + if(tdmi->tdmi_transport_stream_id != tsid) return -1; + if(!tdmi->tdmi_network_id) + dvb_mux_set_onid(tdmi, onid); } else { LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) if(tdmi->tdmi_transport_stream_id == tsid && From 08c07e983c68bcf1b69060e1dd70a09e6787fb69 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 15 Oct 2012 10:46:47 +0100 Subject: [PATCH 004/503] Add viasat baltic EPG handler. Fixes #1197. --- src/epggrab/module/eit.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 7efecf38..54925ccc 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -829,6 +829,7 @@ static void _eit_start if (!m->enabled) return; /* Freeview (switch to EIT, ignore if explicitly enabled) */ + // Note: do this as PID is the same if (!strcmp(m->id, "uk_freeview")) { m = (epggrab_module_ota_t*)epggrab_module_find_by_id("eit"); if (m->enabled) return; @@ -841,7 +842,7 @@ static void _eit_start ota->destroy = _eit_ota_destroy; } - /* Add PIDs (freesat uses non-standard) */ + /* Freesat (3002/3003) */ if (!strcmp("uk_freesat", m->id)) { #ifdef IGNORE_TOO_SLOW tdt_add(tdmi, NULL, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL); @@ -849,6 +850,12 @@ static void _eit_start #endif tdt_add(tdmi, NULL, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002, NULL); tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 3003, NULL); + + /* Viasat Baltic (0x39) */ + } else if (!strcmp("viasat_baltic", m->id)) { + tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 0x39, NULL); + + /* Standard (0x12) */ } else { tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 0x12, NULL); } @@ -892,6 +899,8 @@ void eit_init ( void ) _eit_start, _eit_enable, NULL); epggrab_module_ota_create(NULL, "uk_freeview", "UK: Freeview", 5, _eit_start, _eit_enable, NULL); + epggrab_module_ota_create(NULL, "viasat_baltic", "VIASAT: Baltic", 5, + _eit_start, _eit_enable, NULL); } void eit_load ( void ) From e52384b80d02d6a726dca9f1f343ec6098126c95 Mon Sep 17 00:00:00 2001 From: Joakim Hernberg Date: Wed, 10 Oct 2012 14:24:15 +0200 Subject: [PATCH 005/503] [PR-161] Add support for DiSEqC repeat and up to 64 lnbs. Fixes #1319. --- docs/html/config_dvb.html | 4 ++ src/dvb/diseqc.c | 106 ++++++++++++++++++------------------ src/dvb/diseqc.h | 5 +- src/dvb/dvb.h | 6 +- src/dvb/dvb_adapter.c | 17 ++++++ src/dvb/dvb_fe.c | 12 ++-- src/webui/extjs_dvb.c | 11 ++++ src/webui/static/app/dvb.js | 18 +++++- 8 files changed, 115 insertions(+), 64 deletions(-) diff --git a/docs/html/config_dvb.html b/docs/html/config_dvb.html index 5ef40f06..a772da80 100644 --- a/docs/html/config_dvb.html +++ b/docs/html/config_dvb.html @@ -257,6 +257,10 @@
Switchport
Port number to select for this configuration (numbering begins at 0). +
In DiSEqC 1.0/2.0 mode, ports 0-3 are valid. +
In DiSEqC 1.1/2.1 mode, ports 0-63 are valid. +
Use numbers 0-3 for LNBs behind first input on the uncommited switch, + then 4-7 and so on to support up to 64 ports using DiSEqC 1.1/2.1.
LNB type
Select the LNB type from the list of supported LNBs. If your LNB diff --git a/src/dvb/diseqc.c b/src/dvb/diseqc.c index f4468588..1c49ff22 100644 --- a/src/dvb/diseqc.c +++ b/src/dvb/diseqc.c @@ -5,34 +5,6 @@ #include "tvheadend.h" #include "diseqc.h" -//#define DISEQC_TRACE - -struct dvb_diseqc_master_cmd diseqc_commited_cmds[] = { - { { 0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf1, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf2, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf3, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf4, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf5, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf6, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf7, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf8, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xf9, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xfa, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xfb, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xfc, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xfd, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xfe, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x38, 0xff, 0x00, 0x00 }, 4 } -}; - -struct dvb_diseqc_master_cmd diseqc_uncommited_cmds[] = { - { { 0xe0, 0x10, 0x39, 0xf0, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x39, 0xf1, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x39, 0xf2, 0x00, 0x00 }, 4 }, - { { 0xe0, 0x10, 0x39, 0xf3, 0x00, 0x00 }, 4 } -}; - /*--------------------------------------------------------------------------*/ static inline void @@ -45,19 +17,42 @@ msleep(uint32_t msec) } int -diseqc_setup(int fe_fd, int input, int voltage, int band, int diseqc_ver) +diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, + __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len) { - int i = (input % 4) * 4 + voltage * 2 + (band ? 1 : 0); - int j = input / 4; - int err; + struct dvb_diseqc_master_cmd message; -#ifdef DISEQC_TRACE - tvhlog(LOG_INFO, "diseqc", - "fe_fd %i, input %i, voltage %i, band %i, diseqc_ver %i, i %i, j %i", - fe_fd, input, voltage, band, diseqc_ver, i, j); +#if DISEQC_TRACE + tvhlog(LOG_DEBUG, "diseqc", "sending %X %X %X %X %X %X", + framing_byte, address, cmd, data_1, data_2, data_3); #endif - /* check for invalid input number or diseqc command indexes */ - if(input < 0 || input >=16 || i < 0 || i >= 16 || j < 0 || j >= 4) + + message.msg[0] = framing_byte; + message.msg[1] = address; + message.msg[2] = cmd; + message.msg[3] = data_1; + message.msg[4] = data_2; + message.msg[5] = data_3; + message.msg_len = msg_len; + return ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message); +} + +int +diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, + uint32_t version, uint32_t repeats) +{ + int i = (lnb_num % 4) * 4 + voltage * 2 + (band ? 1 : 0); + int j = lnb_num / 4; + int k, err; + +#if DISEQC_TRACE + tvhlog(LOG_DEBUG, "diseqc", + "fe_fd %i, lnb_num %i, voltage %i, band %i, version %i, repeats %i", + fe_fd, lnb_num, voltage, band, version, repeats); +#endif + + /* verify lnb number and diseqc data */ + if(lnb_num < 0 || lnb_num >=64 || i < 0 || i >= 16 || j < 0 || j >= 16) return -1; /* turn off continuous tone */ @@ -70,29 +65,34 @@ diseqc_setup(int fe_fd, int input, int voltage, int band, int diseqc_ver) return err; msleep(15); - /* send uncommited command */ - if ((err = ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, - &diseqc_uncommited_cmds[j]))) - return err; + if (repeats == 0) { /* uncommited msg, wait 15ms, commited msg */ + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) + return err; + msleep(15); + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) + return err; + } else { /* commited msg, 25ms, uncommited msg, 25ms, commited msg, etc */ + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) + return err; + for (k = 0; k < repeats; k++) { + msleep(25); + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) + return err; + msleep(25); + if ((err = diseqc_send_msg(fe_fd, 0xE1, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) + return err; + } + } msleep(15); - /* send commited command */ - if ((err = ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, - &diseqc_commited_cmds[i]))) - return err; -#ifdef DISEQC_TRACE - tvhlog(LOG_INFO, "diseqc", "E0 10 39 F%X - E0 10 38 F%X sent", j, i); -#endif - msleep(15); - - /* send toneburst command */ + /* set toneburst */ if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) return err; msleep(15); /* set continuous tone */ - if ((ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) + if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) return err; return 0; } diff --git a/src/dvb/diseqc.h b/src/dvb/diseqc.h index fdabb6df..852a7948 100644 --- a/src/dvb/diseqc.h +++ b/src/dvb/diseqc.h @@ -7,7 +7,10 @@ /** * set up the switch to position/voltage/tone */ -int diseqc_setup(int fe_fd, int input, int voltage, int band, int diseqc_ver); +int diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, + __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len); +int diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, + uint32_t version, uint32_t repeats); int diseqc_voltage_off(int fe_fd); #endif diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 60258e7c..25e2ea2a 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -42,7 +42,7 @@ TAILQ_HEAD(dvb_satconf_queue, dvb_satconf); typedef struct dvb_satconf { char *sc_id; TAILQ_ENTRY(dvb_satconf) sc_adapter_link; - int sc_port; // diseqc switchport (0 - 15) + int sc_port; // diseqc switchport (0 - 63) char *sc_name; char *sc_comment; @@ -185,6 +185,7 @@ typedef struct th_dvb_adapter { uint32_t tda_sidtochan; uint32_t tda_nitoid; uint32_t tda_diseqc_version; + uint32_t tda_diseqc_repeats; uint32_t tda_disable_pmt_monitor; char *tda_displayname; @@ -314,6 +315,9 @@ void dvb_adapter_set_nitoid(th_dvb_adapter_t *tda, int nitoid); void dvb_adapter_set_diseqc_version(th_dvb_adapter_t *tda, unsigned int v); +void dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda, + unsigned int repeats); + void dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on); void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 6fc42092..ee9e8d49 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -93,6 +93,7 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "poweroff", tda->tda_poweroff); htsmsg_add_u32(m, "nitoid", tda->tda_nitoid); htsmsg_add_u32(m, "diseqc_version", tda->tda_diseqc_version); + htsmsg_add_u32(m, "diseqc_repeats", tda->tda_diseqc_repeats); htsmsg_add_u32(m, "extrapriority", tda->tda_extrapriority); htsmsg_add_u32(m, "skip_initialscan", tda->tda_skip_initialscan); htsmsg_add_u32(m, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); @@ -333,6 +334,21 @@ dvb_adapter_set_diseqc_version(th_dvb_adapter_t *tda, unsigned int v) tda_save(tda); } +/** + * sets the number of diseqc repeats to perform + */ +void +dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda, unsigned int repeats) +{ + if(tda->tda_diseqc_repeats == repeats) + return; + lock_assert(&global_lock); + tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" DiSEqC repeats set to: %i", + tda->tda_displayname, repeats); + tda->tda_diseqc_repeats = repeats; + tda_save(tda); +} + /** * */ @@ -572,6 +588,7 @@ dvb_adapter_init(uint32_t adapter_mask) htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff); htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid); htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version); + htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats); htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor); diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 97536abe..4830e2fc 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -507,13 +507,13 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) p->frequency = abs(p->frequency - lowfreq); } - if ((r = diseqc_setup(tda->tda_fe_fd, - port, - pol == POLARISATION_HORIZONTAL || - pol == POLARISATION_CIRCULAR_LEFT, - hiband, tda->tda_diseqc_version)) != 0) + if ((r = diseqc_setup(tda->tda_fe_fd, port, + pol == POLARISATION_HORIZONTAL || + pol == POLARISATION_CIRCULAR_LEFT, + hiband, tda->tda_diseqc_version, + tda->tda_diseqc_repeats)) != 0) tvhlog(LOG_ERR, "dvb", "diseqc setup failed %d\n", r); - } + } dvb_mux_nicename(buf, sizeof(buf), tdmi); diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 94adc399..59760abf 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -162,6 +162,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) ((const char *[]){"DiSEqC 1.0 / 2.0", "DiSEqC 1.1 / 2.1"}) [tda->tda_diseqc_version % 2]); + htsmsg_add_str(r, "diseqcrepeats", + ((const char *[]){"0", "1", "3"}) + [tda->tda_diseqc_repeats % 3]); htsmsg_add_u32(r, "extrapriority", tda->tda_extrapriority); out = json_single_record(r, "dvbadapters"); @@ -210,6 +213,14 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) dvb_adapter_set_diseqc_version(tda, 1); } + if((s = http_arg_get(&hc->hc_req_args, "diseqcrepeats")) != NULL) { + if(!strcmp(s, "0")) + dvb_adapter_set_diseqc_repeats(tda, 0); + else if(!strcmp(s, "1")) + dvb_adapter_set_diseqc_repeats(tda, 1); + else if(!strcmp(s, "2")) + dvb_adapter_set_diseqc_repeats(tda, 2); + } if((s = http_arg_get(&hc->hc_req_args, "extrapriority")) != NULL) dvb_adapter_set_extrapriority(tda, atoi(s)); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index a71e7ebb..5667e6f1 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1087,8 +1087,9 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { var confreader = new Ext.data.JsonReader({ root : 'dvbadapters' }, [ 'name', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', - 'qmon', 'skip_checksubscr', 'dumpmux', 'poweroff', 'sidtochan', 'nitoid', - 'extrapriority', 'disable_pmt_monitor', 'idleclose' ]); + 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'dumpmux', + 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', + ,'disable_pmt_monitor', 'idleclose' ]); function saveConfForm() { confform.getForm().submit({ @@ -1169,6 +1170,17 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { }); items.push(v); + v = new Ext.form.ComboBox({ + name : 'diseqcrepeats', + fieldLabel : 'DiSEqC repeats', + editable : false, + allowBlank : false, + mode : 'remote', + triggerAction : 'all', + store : [ '0', '1', '2' ] + }); + items.push(v); + v = new Ext.form.Checkbox({ fieldLabel : 'Turn off LNB when idle', name : 'poweroff' @@ -1293,7 +1305,7 @@ tvheadend.dvb_satconf = function(adapterId, lnbStore) { dataIndex : 'port', editor : new fm.NumberField({ minValue : 0, - maxValue : 15 + maxValue : 63 }) }, { header : "LNB type", From f4fcb278f8f0130a17b1e6dfd6c7b7a62672fe45 Mon Sep 17 00:00:00 2001 From: xhaggi Date: Sat, 13 Oct 2012 11:09:04 +0200 Subject: [PATCH 006/503] [PR-163] Added encoding ISO8859-15 for Astra 19.2E Sky Germany --- data/conf/charset | 91 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/data/conf/charset b/data/conf/charset index e597bb74..a04cb14b 100644 --- a/data/conf/charset +++ b/data/conf/charset @@ -236,5 +236,96 @@ "charset": "PL_AUTO", "sid": 10626, "description": "Hotbird 13.0 Disco TV" + }, + { + "tsid": 1, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 12070.50 H" + }, + { + "tsid": 2, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11797.50 H" + }, + { + "tsid": 3, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11719.50 H" + }, + { + "tsid": 4, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 12031.50 H" + }, + { + "tsid": 6, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11914.50 H" + }, + { + "tsid": 10, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11332.25 H" + }, + { + "tsid": 11, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 12382.50 H" + }, + { + "tsid": 12, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 12304.50 H" + }, + { + "tsid": 13, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11992.50 H" + }, + { + "tsid": 14, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11875.50 H" + }, + { + "tsid": 15, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 10920.75 H" + }, + { + "tsid": 17, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 11758.50 H" + }, + { + "tsid": 33, + "onid": 133, + "charset": "ISO8859-15", + "sid": 0, + "description": "Astra 19.2E Sky Germany 12480.00 V" } ] From 753f762de404131477349f9dcd91fd66a21644b9 Mon Sep 17 00:00:00 2001 From: Wojciech Myrda Date: Sun, 14 Oct 2012 13:42:05 +0200 Subject: [PATCH 007/503] [PR-165] Polish Character Encoding Fixes --- data/conf/charset | 208 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 20 deletions(-) diff --git a/data/conf/charset b/data/conf/charset index a04cb14b..5020aa8a 100644 --- a/data/conf/charset +++ b/data/conf/charset @@ -1,31 +1,73 @@ [ + { + "tsid": 1048, + "onid": 1, + "charset": "PL_AUTO", + "sid": 4310, + "description": "Astra 19.2E TV Trwam" + }, + { + "tsid": 1059, + "onid": 1, + "charset": "PL_AUTO", + "sid": 0, + "description": "Astra 19.2E TVP" + }, + { + "tsid": 1111, + "onid": 1, + "charset": "PL_AUTO", + "sid": 7269, + "description": "Astra 19.2E TV Trwam" + }, + { + "tsid": 8100, + "onid": 156, + "charset": "PL_AUTO", + "sid": 1500, + "description": "Eurobird 9.0E TV Polonia" + }, + { + "tsid": 8300, + "onid": 156, + "charset": "PL_AUTO", + "sid": 620, + "description": "Eurobird 9.0E TVN International" + }, { "tsid": 200, "onid": 318, "charset": "PL_AUTO", - "sid": 13878, - "description": "ESP2 NE Polish" + "sid": 13834, + "description": "Hotbird 13.0 Eutelsat Eurosport PL" }, { "tsid": 200, "onid": 318, "charset": "PL_AUTO", "sid": 13864, - "description": "EUROSPORT2 PL" + "description": "Hotbird 13.0 Eutelsat Eurosport 2 PL" }, { "tsid": 200, "onid": 318, "charset": "PL_AUTO", "sid": 13865, - "description": "Hotbird Eutelsat (Eurosport)" + "description": "Hotbird 13.0 Eutelsat Eurosport PL" + }, + { + "tsid": 200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 13878, + "description": "Hotbird 13.0 Eutelsat Eurosport 2 NE PL" }, { "tsid": 300, "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 Animal Planet HD" + "description": "Hotbird 13.0 ITI/Nka HD PL" }, { "tsid": 400, @@ -74,28 +116,35 @@ "onid": 318, "charset": "PL_AUTO", "sid": 13070, - "description": "Hotbird 13.0 Cyfra+ ESP HD PL" + "description": "Hotbird 13.0 Cyfra+ Eurosport HD PL" + }, + { + "tsid": 400, + "onid": 318, + "charset": "PL_AUTO", + "sid": 13080, + "description": "Hotbird 13.0 Cyfra+ Eurosport HD PL" }, { "tsid": 400, "onid": 318, "charset": "PL_AUTO", "sid": 13081, - "description": "EUROSPORT HD" + "description": "Hotbird 13.0 Cyfra+ Eurosport HD PL" }, { "tsid": 400, "onid": 318, "charset": "PL_AUTO", "sid": 13082, - "description": "EUROSPORT HD" + "description": "Hotbird 13.0 Cyfra+ Eurosport HD PL" }, { "tsid": 1000, "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 Grupa ITI" + "description": "Hotbird 13.0 ITI/Nka TVN" }, { "tsid": 1100, @@ -104,6 +153,27 @@ "sid": 0, "description": "Hotbird 13.0 Cyfra+" }, + { + "tsid": 1300, + "onid": 318, + "charset": "PL_AUTO", + "sid": 0, + "description": "Hotbird 13.0 ITI/Nka" + }, + { + "tsid": 1400, + "onid": 318, + "charset": "PL_AUTO", + "sid": 30, + "description": "Hotbird 13.0 Nick Junior" + }, + { + "tsid": 1400, + "onid": 318, + "charset": "PL_AUTO", + "sid": 31, + "description": "Hotbird 13.0 Nickelodeon HD" + }, { "tsid": 1500, "onid": 318, @@ -116,7 +186,7 @@ "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 EskaTV, TVN" + "description": "Hotbird 13.0 ITI/Nka EskaTV, TVN" }, { "tsid": 1800, @@ -132,6 +202,13 @@ "sid": 0, "description": "Hotbird 13.0 Cyfrowy Polsat" }, + { + "tsid": 7700, + "onid": 318, + "charset": "PL_AUTO", + "sid": 117, + "description": "Hotbird 13.0 Hot TV PL" + }, { "tsid": 7800, "onid": 113, @@ -150,8 +227,43 @@ "tsid": 8100, "onid": 318, "charset": "PL_AUTO", - "sid": 0, - "description": "Hotbird 13.0 Eutelsat (Universal)" + "sid": 14900, + "description": "Hotbird 13.0 Eutelsat Sport Klub PL" + }, + { + "tsid": 8100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 14901, + "description": "Hotbird 13.0 Eutelsat Universal CE" + }, + { + "tsid": 8100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 14902, + "description": "Hotbird 13.0 Eutelsat Sci-Fi CE" + }, + { + "tsid": 8100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 14910, + "description": "Hotbird 13.0 Eutelsat Sport Klub PL" + }, + { + "tsid": 8100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 14911, + "description": "Hotbird 13.0 Eutelsat Universal CE" + }, + { + "tsid": 8100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 14912, + "description": "Hotbird 13.0 Eutelsat Sci-Fi CE" }, { "tsid": 11000, @@ -160,6 +272,20 @@ "sid": 0, "description": "Hotbird 13.0 Cyfra+" }, + { + "tsid": 11100, + "onid": 318, + "charset": "PL_AUTO", + "sid": 4661, + "description": "Hotbird 13.0 Eutelsat Wedding TV" + }, + { + "tsid": 11200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 0, + "description": "Hotbird 13.0 Polsat Cyfrowy PPV" + }, { "tsid": 11400, "onid": 318, @@ -172,7 +298,7 @@ "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 BBC HD, ITI" + "description": "Hotbird 13.0 ITI/Nka" }, { "tsid": 11900, @@ -182,46 +308,74 @@ "description": "Hotbird 13.0 Cyfra+" }, { - "tsid": 12200, + "tsid": 12000, "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 Disney Channel Polska, TCM and other" + "description": "Hotbird 13.0 Polsat Cyfrowy" + }, + { + "tsid": 12200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 7457, + "description": "Hotbird 13.0 Globecast Cartoon Network PL / TCM CE" + }, + { + "tsid": 12200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 7466, + "description": "Hotbird 13.0 Globecast Disney Channel PL" + }, + { + "tsid": 12200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 7467, + "description": "Hotbird 13.0 Globecast Cartoon Network CE" + }, + { + "tsid": 12200, + "onid": 318, + "charset": "PL_AUTO", + "sid": 7468, + "description": "Hotbird 13.0 Globecast TCM CE" }, { "tsid": 12800, "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 Viacom ... MTV / VH1 Polska" + "description": "Hotbird 13.0 Viacom MTV / VH1 Polska" }, { "tsid": 13000, "onid": 318, "charset": "PL_AUTO", "sid": 0, - "description": "Hotbird 13.0 BBC Polska, TLC and other" + "description": "Hotbird 13.0 Globecast Orange PL" }, { "tsid": 13100, "onid": 318, "charset": "PL_AUTO", "sid": 7322, - "description": "TV5 Monde Europe" + "description": "Hotbird 13.0 TV5 Monde Europe" }, { "tsid": 13100, "onid": 318, "charset": "PL_AUTO", "sid": 7324, - "description": "Hotbird 13.0 Crime and Investigation" + "description": "Hotbird 13.0 Crime & Investigation" }, { "tsid": 13100, "onid": 318, "charset": "PL_AUTO", "sid": 7325, - "description": "Hotbird 13.0 Crime + Investigation" + "description": "Hotbird 13.0 Crime & Investigation" }, { "tsid": 13200, @@ -230,6 +384,20 @@ "sid": 0, "description": "Hotbird 13.0 Cyfrowy Polsat" }, + { + "tsid": 13400, + "onid": 318, + "charset": "PL_AUTO", + "sid": 4754, + "description": "Hotbird 13.0 TVR" + }, + { + "tsid": 15400, + "onid": 318, + "charset": "PL_AUTO", + "sid": 13511, + "description": "Hotbird 13.0 Rebel TV" + } { "tsid": 15700, "onid": 318, From 4b5ee3585ce243dee0aba06691b768e81996ac43 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 17 Sep 2012 16:13:27 +0100 Subject: [PATCH 008/503] Added some new transport/multiplex search routines and used them in epg modules. --- src/dvb/dvb.h | 9 +++++++++ src/dvb/dvb_multiplex.c | 21 +++++++++++++++++++++ src/dvb/dvb_transport.c | 35 +++++++++++++++++++++++++++++++++++ src/epggrab/module/eit.c | 14 ++++---------- src/epggrab/module/opentv.c | 21 ++------------------- 5 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 25e2ea2a..571431b5 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -399,6 +399,10 @@ int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, void dvb_mux_add_to_scan_queue (th_dvb_mux_instance_t *tdmi); +th_dvb_mux_instance_t *dvb_mux_find + (th_dvb_adapter_t *tda, const char *netname, uint16_t onid, uint16_t tsid, + int enabled ); + /** * DVB Transport (aka DVB service) */ @@ -412,6 +416,11 @@ struct service *dvb_transport_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier, int *save); +struct service *dvb_transport_find3 + (th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, + const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid, + int enabled, int epgprimary); + void dvb_transport_notify(struct service *t); void dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda); diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 0c4c1273..3b13f15e 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1262,3 +1262,24 @@ void dvb_mux_add_to_scan_queue ( th_dvb_mux_instance_t *tdmi ) tdmi->tdmi_scan_queue = &tda->tda_scan_queues[ti]; TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); } + +th_dvb_mux_instance_t *dvb_mux_find + ( th_dvb_adapter_t *tda, const char *netname, uint16_t onid, uint16_t tsid, + int enabled ) +{ + th_dvb_mux_instance_t *tdmi; + if (tda) { + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { + if (enabled && !tdmi->tdmi_enabled) continue; + if (onid && onid != tdmi->tdmi_network_id) continue; + if (tsid && tsid != tdmi->tdmi_transport_stream_id) continue; + if (netname && strcmp(netname, tdmi->tdmi_network ?: "")) continue; + return tdmi; + } + } else { + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + if ((tdmi = dvb_mux_find(tda, netname, onid, tsid, enabled))) + return tdmi; + } + return NULL; +} diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c index 7106d133..8552780d 100644 --- a/src/dvb/dvb_transport.c +++ b/src/dvb/dvb_transport.c @@ -383,6 +383,41 @@ dvb_grace_period(service_t *t) return 10; } +/* + * Find transport based on the DVB identification + */ +service_t * +dvb_transport_find3 + (th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, + const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid, + int enabled, int epgprimary) +{ + service_t *svc; + if (tdmi) { + LIST_FOREACH(svc, &tdmi->tdmi_transports, s_group_link) { + if (enabled && !svc->s_enabled) continue; + if (epgprimary && !service_is_primary_epg(svc)) continue; + if (sid == svc->s_dvb_service_id) return svc; + } + } else if (tda) { + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { + if (enabled && !tdmi->tdmi_enabled) continue; + if (onid && onid != tdmi->tdmi_network_id) continue; + if (tsid && tsid != tdmi->tdmi_transport_stream_id) continue; + if (netname && strcmp(netname, tdmi->tdmi_network ?: "")) continue; + if ((svc = dvb_transport_find3(tda, tdmi, NULL, 0, 0, sid, + enabled, epgprimary))) + return svc; + } + } else { + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + if ((svc = dvb_transport_find3(tda, NULL, netname, onid, tsid, + sid, enabled, epgprimary))) + return svc; + } + return NULL; +} + /** * Find a transport based on 'serviceid' on the given mux diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 54925ccc..3da8da57 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -713,11 +713,8 @@ static int _eit_callback // Note: tableid=0x4f,0x60-0x6f is other TS // so must find the tdmi if(tableid == 0x4f || tableid >= 0x60) { - tda = tdmi->tdmi_adapter; - LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) - if(tdmi->tdmi_transport_stream_id == tsid && - tdmi->tdmi_network_id == onid) - break; + tda = tdmi->tdmi_adapter; + tdmi = dvb_mux_find(tda, NULL, onid, tsid, 1); } else { if (tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) { @@ -733,11 +730,8 @@ static int _eit_callback if(!tdmi) goto done; /* Get service */ - svc = dvb_transport_find(tdmi, sid, 0, NULL); - if (!svc || !svc->s_enabled || !svc->s_ch) goto done; - - /* Ignore (not primary EPG service) */ - if (!service_is_primary_epg(svc)) goto done; + svc = dvb_transport_find3(NULL, tdmi, NULL, 0, 0, sid, 1, 1); + if (!svc || !svc->s_ch) goto done; /* Register as interesting */ if (tableid < 0x50) diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 7a1fbba6..e3d8c1c2 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -189,23 +189,6 @@ static epggrab_channel_t *_opentv_find_epggrab_channel (epggrab_module_t*)mod); } -static service_t *_opentv_find_service ( int onid, int tsid, int sid ) -{ - th_dvb_adapter_t *tda; - th_dvb_mux_instance_t *tdmi; - service_t *t = NULL; - TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { - LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { - if (tdmi->tdmi_transport_stream_id != tsid) continue; - if (tdmi->tdmi_network_id != onid) continue; - LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) { - if (t->s_dvb_service_id == sid) return t; - } - } - } - return NULL; -} - /* ************************************************************************ * OpenTV event processing * ***********************************************************************/ @@ -442,8 +425,8 @@ static void _opentv_parse_channels cnum = ((int)buf[i+5] << 8) | buf[i+6]; /* Find the service */ - svc = _opentv_find_service(onid, tsid, sid); - if (svc && svc->s_ch && service_is_primary_epg(svc)) { + svc = dvb_transport_find3(NULL, NULL, NULL, onid, tsid, sid, 1, 1); + if (svc && svc->s_ch) { ec =_opentv_find_epggrab_channel(mod, cid, 1, &save); ecl = LIST_FIRST(&ec->channels); if (!ecl) { From f276a17db53b20d759fc5ced0bc738456d935563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 15 Oct 2012 14:04:09 +0200 Subject: [PATCH 009/503] Add option to run HTSP on an additional port --- src/htsp.c | 5 ++++- src/main.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index 789c036b..5bca4661 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -49,7 +49,7 @@ * Datatypes and variables * *************************************************************************/ -static void *htsp_server; +static void *htsp_server, *htsp_server_2; #define HTSP_PROTO_VERSION 6 @@ -1525,7 +1525,10 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, void htsp_init(void) { + extern int htsp_port_extra; htsp_server = tcp_server_create(htsp_port, htsp_serve, NULL); + if(htsp_port_extra) + htsp_server_2 = tcp_server_create(htsp_port_extra, htsp_serve, NULL); } /* ************************************************************************** diff --git a/src/main.c b/src/main.c index 66d863fc..770d71f1 100644 --- a/src/main.c +++ b/src/main.c @@ -75,6 +75,7 @@ int log_debug_to_console; int webui_port; int htsp_port; +int htsp_port_extra; char *tvheadend_cwd; static void @@ -278,7 +279,7 @@ main(int argc, char **argv) // make sure the timezone is set tzset(); - while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:")) != -1) { + while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; @@ -315,6 +316,9 @@ main(int argc, char **argv) case 'e': htsp_port = atoi(optarg); break; + case 'E': + htsp_port_extra = atoi(optarg); + break; case 'u': usernam = optarg; break; From fbf5de8ab85ebb881820af0c2dc1609458bb3ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 15 Oct 2012 14:30:50 +0200 Subject: [PATCH 010/503] HTSP: Allow a subscriber to request timestamps in 90kHz timebase --- src/htsp.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index 5bca4661..f371183c 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -169,6 +169,8 @@ typedef struct htsp_subscription { int hs_dropstats[PKT_NTYPES]; + int hs_90khz; + } htsp_subscription_t; /* ************************************************************************** @@ -1099,7 +1101,7 @@ htsp_method_getTicket(htsp_connection_t *htsp, htsmsg_t *in) static htsmsg_t * htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) { - uint32_t chid, sid, weight; + uint32_t chid, sid, weight, req90khz; channel_t *ch; htsp_subscription_t *hs; @@ -1113,18 +1115,24 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) return htsp_error("Requested channel does not exist"); weight = htsmsg_get_u32_or_default(in, "weight", 150); + req90khz = htsmsg_get_u32_or_default(in, "90khz", 0); /* * We send the reply now to avoid the user getting the 'subscriptionStart' * async message before the reply to 'subscribe'. */ - htsp_reply(htsp, in, htsmsg_create_map()); + htsmsg_t *rep = htsmsg_create_map(); + if(req90khz) + htsmsg_add_u32(rep, "90khz", 1); + + htsp_reply(htsp, in, rep); /* Initialize the HTSP subscription structure */ hs = calloc(1, sizeof(htsp_subscription_t)); hs->hs_htsp = htsp; + hs->hs_90khz = req90khz; htsp_init_queue(&hs->hs_q, 0); hs->hs_sid = sid; @@ -1753,16 +1761,16 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) if(pkt->pkt_pts != PTS_UNSET) { - int64_t pts = ts_rescale(pkt->pkt_pts, 1000000); + int64_t pts = hs->hs_90khz ? pkt->pkt_pts : ts_rescale(pkt->pkt_pts, 1000000); htsmsg_add_s64(m, "pts", pts); } if(pkt->pkt_dts != PTS_UNSET) { - int64_t dts = ts_rescale(pkt->pkt_dts, 1000000); + int64_t dts = hs->hs_90khz ? pkt->pkt_dts : ts_rescale(pkt->pkt_dts, 1000000); htsmsg_add_s64(m, "dts", dts); } - uint32_t dur = ts_rescale(pkt->pkt_duration, 1000000); + uint32_t dur = hs->hs_90khz ? pkt->pkt_duration : ts_rescale(pkt->pkt_duration, 1000000); htsmsg_add_u32(m, "duration", dur); pkt = pkt_merge_header(pkt); From fba8be65b369fb391789ba31ea6120f2f04fd027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 15 Oct 2012 14:33:37 +0200 Subject: [PATCH 011/503] HTSP: Allow a subscriber to set queue depth --- src/htsp.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index f371183c..531e9fff 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -171,8 +171,12 @@ typedef struct htsp_subscription { int hs_90khz; + int hs_queue_depth; + } htsp_subscription_t; +#define HTSP_DEFAULT_QUEUE_DEPTH 500000 + /* ************************************************************************** * Support routines * *************************************************************************/ @@ -1133,6 +1137,8 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) hs->hs_htsp = htsp; hs->hs_90khz = req90khz; + hs->hs_queue_depth = htsmsg_get_u32_or_default(in, "queueDepth", + HTSP_DEFAULT_QUEUE_DEPTH); htsp_init_queue(&hs->hs_q, 0); hs->hs_sid = sid; @@ -1739,9 +1745,9 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) int64_t ts; int qlen = hs->hs_q.hmq_payload; - if((qlen > 500000 && pkt->pkt_frametype == PKT_B_FRAME) || - (qlen > 750000 && pkt->pkt_frametype == PKT_P_FRAME) || - (qlen > 1500000)) { + if((qlen > hs->hs_queue_depth && pkt->pkt_frametype == PKT_B_FRAME) || + (qlen > hs->hs_queue_depth * 2 && pkt->pkt_frametype == PKT_P_FRAME) || + (qlen > hs->hs_queue_depth * 3)) { hs->hs_dropstats[pkt->pkt_frametype]++; From 39555ec53fa92cde53a5233dac6e580eafba4ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 16 Oct 2012 11:30:44 +0100 Subject: [PATCH 012/503] HTSP: Add ability for clients to ask for normalized timestamps Normalized timestamps will never wrap and always start at 0 --- src/htsp.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index 531e9fff..fc2bce35 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -40,6 +40,7 @@ #include "psi.h" #include "htsmsg_binary.h" #include "epg.h" +#include "plumbing/tsfix.h" #include #include "settings.h" @@ -162,6 +163,7 @@ typedef struct htsp_subscription { th_subscription_t *hs_s; // Temporary streaming_target_t hs_input; + streaming_target_t *hs_tsfix; htsp_msg_q_t hs_q; @@ -251,6 +253,8 @@ htsp_subscription_destroy(htsp_connection_t *htsp, htsp_subscription_t *hs) { LIST_REMOVE(hs, hs_link); subscription_unsubscribe(hs->hs_s); + if(hs->hs_tsfix != NULL) + tsfix_destroy(hs->hs_tsfix); htsp_flush_queue(htsp, &hs->hs_q); free(hs); } @@ -1105,7 +1109,7 @@ htsp_method_getTicket(htsp_connection_t *htsp, htsmsg_t *in) static htsmsg_t * htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) { - uint32_t chid, sid, weight, req90khz; + uint32_t chid, sid, weight, req90khz, normts; channel_t *ch; htsp_subscription_t *hs; @@ -1120,14 +1124,21 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) weight = htsmsg_get_u32_or_default(in, "weight", 150); req90khz = htsmsg_get_u32_or_default(in, "90khz", 0); + normts = htsmsg_get_u32_or_default(in, "normts", 0); /* * We send the reply now to avoid the user getting the 'subscriptionStart' * async message before the reply to 'subscribe'. + * + * Send some opiotanl boolean flags back to the subscriber so it can infer + * if we support those + * */ htsmsg_t *rep = htsmsg_create_map(); if(req90khz) htsmsg_add_u32(rep, "90khz", 1); + if(normts) + htsmsg_add_u32(rep, "normts", 1); htsp_reply(htsp, in, rep); @@ -1145,9 +1156,18 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) LIST_INSERT_HEAD(&htsp->htsp_subscriptions, hs, hs_link); streaming_target_init(&hs->hs_input, htsp_streaming_input, hs, 0); + streaming_target_t *st; + + if(normts) { + hs->hs_tsfix = tsfix_create(&hs->hs_input); + st = hs->hs_tsfix; + } else { + st = &hs->hs_input; + } + hs->hs_s = subscription_create_from_channel(ch, weight, htsp->htsp_logname, - &hs->hs_input, 0); + st, 0); return NULL; } From a40aad8ff24bbbde283aa17ef375c8b0ccde5bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 18 Oct 2012 11:04:10 +0200 Subject: [PATCH 013/503] Rewrite atomic.h to depend on gcc intrinsics only --- src/atomic.h | 87 ++-------------------------------------------------- 1 file changed, 3 insertions(+), 84 deletions(-) diff --git a/src/atomic.h b/src/atomic.h index a9e1485e..1c3efb73 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -16,16 +16,7 @@ * along with this program. If not, see . */ -#ifndef HTSATOMIC_H__ -#define HTSATOMIC_H__ - -/** - * Atomically add 'incr' to *ptr and return the previous value - */ - - - -#if defined(linux) && __GNUC__ >= 4 && __GNUC_MINOR__ >=3 +#pragma once static inline int atomic_add(volatile int *ptr, int incr) @@ -33,80 +24,8 @@ atomic_add(volatile int *ptr, int incr) return __sync_fetch_and_add(ptr, incr); } -#elif defined(__i386__) || defined(__x86_64__) static inline int -atomic_add(volatile int *ptr, int incr) +atomic_exchange(volatile int *ptr, int new) { - int r; - asm volatile("lock; xaddl %0, %1" : - "=r"(r), "=m"(*ptr) : "0" (incr), "m" (*ptr) : "memory"); - return r; + return __sync_lock_test_and_set(ptr, new); } -#elif defined(WII) - -#include - -static inline int -atomic_add(volatile int *ptr, int incr) -{ - int r, level; - - /* - * Last time i checked libogc's context switcher did not do the - * necessary operations to clear locks held by lwarx/stwcx. - * Thus we need to resort to other means - */ - - _CPU_ISR_Disable(level); - - r = *ptr; - *ptr = *ptr + incr; - - _CPU_ISR_Restore(level); - - return r; -} -#elif defined(__ppc__) || defined(__PPC__) - -/* somewhat based on code from darwin gcc */ -static inline int -atomic_add (volatile int *ptr, int incr) -{ - int tmp, res; - asm volatile("0:\n" - "lwarx %1,0,%2\n" - "add%I3 %0,%1,%3\n" - "stwcx. %0,0,%2\n" - "bne- 0b\n" - : "=&r"(tmp), "=&b"(res) - : "r" (ptr), "Ir"(incr) - : "cr0", "memory"); - - return res; -} - -#elif defined(__arm__) - -static inline int -atomic_add(volatile int *ptr, int val) -{ - int a, b, c; - - asm volatile( "0:\n\t" - "ldr %0, [%3]\n\t" - "add %1, %0, %4\n\t" - "swp %2, %1, [%3]\n\t" - "cmp %0, %2\n\t" - "swpne %1, %2, [%3]\n\t" - "bne 0b" - : "=&r" (a), "=&r" (b), "=&r" (c) - : "r" (ptr), "r" (val) - : "cc", "memory"); - return a; -} - -#else -#error Missing atomic ops -#endif - -#endif /* HTSATOMIC_H__ */ From 84ac2ebfdb8ec5fe092abc94f92943e1e3126ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 18 Oct 2012 11:04:45 +0200 Subject: [PATCH 014/503] Add notify_reload() helper --- src/dvr/dvr_autorec.c | 6 ++---- src/notify.c | 9 +++++++++ src/notify.h | 2 ++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 447cbcba..e4902d92 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -520,10 +520,8 @@ _dvr_autorec_add(const char *config_name, htsmsg_destroy(m); /* Notify web clients that we have messed with the tables */ - - m = htsmsg_create_map(); - htsmsg_add_u32(m, "reload", 1); - notify_by_msg("autorec", m); + + notify_reload("autorec"); dvr_autorec_changed(dae); } diff --git a/src/notify.c b/src/notify.c index bdad26ef..b2d8701a 100644 --- a/src/notify.c +++ b/src/notify.c @@ -33,3 +33,12 @@ notify_by_msg(const char *class, htsmsg_t *m) comet_mailbox_add_message(m, 0); htsmsg_destroy(m); } + + +void +notify_reload(const char *class) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_u32(m, "reload", 1); + notify_by_msg(class, m); +} diff --git a/src/notify.h b/src/notify.h index cf278393..1a41a43a 100644 --- a/src/notify.h +++ b/src/notify.h @@ -23,4 +23,6 @@ void notify_by_msg(const char *class, htsmsg_t *m); +void notify_reload(const char *class); + #endif /* NOTIFY_H_ */ From 86a9626346bb8e323a1d2f90653d6146e8cb6fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 18 Oct 2012 11:06:02 +0200 Subject: [PATCH 015/503] Add pkt_err to th_pkt_t This will be set if the packet consists of damaged data --- src/packet.h | 1 + src/parsers.c | 46 +++++++++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/packet.h b/src/packet.h index 836c2d55..fedc1402 100644 --- a/src/packet.h +++ b/src/packet.h @@ -49,6 +49,7 @@ typedef struct th_pkt { uint8_t pkt_channels; uint8_t pkt_sri; + uint8_t pkt_err; uint16_t pkt_aspect_num; uint16_t pkt_aspect_den; diff --git a/src/parsers.c b/src/parsers.c index d72b0f0f..9b4768d9 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -119,7 +119,8 @@ static int parse_eac3(service_t *t, elementary_stream_t *st, size_t len, static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, uint32_t next_startcode, int sc_offset); -static void parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt); +static void parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt, + int errors); static int parse_pes_header(service_t *t, elementary_stream_t *st, const uint8_t *buf, size_t len); @@ -266,7 +267,7 @@ parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data, pkt = parse_latm_audio_mux_element(t, st, d + 3, muxlen); if(pkt != NULL) - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, st->es_buf.sb_err); p += muxlen + 3; } else { @@ -413,6 +414,8 @@ depacketize(service_t *t, elementary_stream_t *st, size_t len, buf += hlen; len -= hlen; + st->es_buf_a.sb_err = st->es_buf.sb_err; + sbuf_append(&st->es_buf_a, buf, len); return 0; } @@ -425,7 +428,8 @@ depacketize(service_t *t, elementary_stream_t *st, size_t len, */ static void makeapkt(service_t *t, elementary_stream_t *st, const void *buf, - int len, int64_t dts, int duration, int channels, int sri) + int len, int64_t dts, int duration, int channels, int sri, + int errors) { th_pkt_t *pkt = pkt_alloc(buf, len, dts, dts); @@ -435,7 +439,7 @@ makeapkt(service_t *t, elementary_stream_t *st, const void *buf, pkt->pkt_channels = channels; pkt->pkt_sri = sri; - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, errors); st->es_curdts = PTS_UNSET; st->es_nextdts = dts + duration; @@ -471,7 +475,7 @@ mp4a_valid_frame(const uint8_t *buf) } static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, - uint32_t next_startcode, int sc_offset) + uint32_t next_startcode, int sc_offset) { int i, len; const uint8_t *buf; @@ -505,7 +509,9 @@ static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, int channels = ((p[2] & 0x01) << 2) | ((p[3] & 0xc0) >> 6); - makeapkt(t, st, p, fsize, dts, duration, channels, sri); + makeapkt(t, st, p, fsize, dts, duration, channels, sri, + st->es_buf_a.sb_err); + st->es_buf_a.sb_err = 0; sbuf_cut(&st->es_buf_a, i + fsize); goto again; } @@ -565,7 +571,9 @@ parse_mpa2(service_t *t, elementary_stream_t *st) mpa_valid_frame(buf + i + fsize)) { makeapkt(t, st, buf + i, fsize, dts, duration, - channels, mpa_sri[(buf[i+2] >> 2) & 3]); + channels, mpa_sri[(buf[i+2] >> 2) & 3], + st->es_buf_a.sb_err); + st->es_buf_a.sb_err = 0; sbuf_cut(&st->es_buf_a, i + fsize); goto again; } @@ -705,7 +713,9 @@ parse_ac3(service_t *t, elementary_stream_t *st, size_t ilen, int lfeon = read_bits(&bs, 1); int channels = acmodtab[acmod] + lfeon; - makeapkt(t, st, p, fsize, dts, duration, channels, sri); + makeapkt(t, st, p, fsize, dts, duration, channels, sri, + st->es_buf_a.sb_err); + st->es_buf_a.sb_err = 0; sbuf_cut(&st->es_buf_a, i + fsize); goto again; } @@ -775,7 +785,9 @@ parse_eac3(service_t *t, elementary_stream_t *st, size_t ilen, if(dts != PTS_UNSET && len >= i + fsize + 6 && eac3_valid_frame(p + fsize)) { - makeapkt(t, st, p, fsize, dts, duration, channels, sri); + makeapkt(t, st, p, fsize, dts, duration, channels, sri, + st->es_buf_a.sb_err); + st->es_buf_a.sb_err = 0; sbuf_cut(&st->es_buf_a, i + fsize); goto again; } @@ -1104,7 +1116,7 @@ parse_mpeg2video(service_t *t, elementary_stream_t *st, size_t len, st->es_buf.sb_ptr - 4); pkt->pkt_duration = st->es_frame_duration; - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, st->es_buf.sb_err); st->es_curpkt = NULL; st->es_buf.sb_data = malloc(st->es_buf.sb_size); @@ -1240,7 +1252,7 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len, pkt->pkt_payload = pktbuf_make(st->es_buf.sb_data, st->es_buf.sb_ptr - 4); - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, st->es_buf.sb_err); st->es_curpkt = NULL; st->es_buf.sb_data = malloc(st->es_buf.sb_size); @@ -1268,8 +1280,7 @@ parse_subtitles(service_t *t, elementary_stream_t *st, const uint8_t *data, if(start) { /* Payload unit start */ st->es_parser_state = 1; - st->es_buf.sb_err = 0; - st->es_buf.sb_ptr = 0; + sbuf_reset(&st->es_buf); } if(st->es_parser_state == 0) @@ -1313,7 +1324,7 @@ parse_subtitles(service_t *t, elementary_stream_t *st, const uint8_t *data, if(buf[psize - 1] == 0xff) { pkt = pkt_alloc(buf, psize - 1, st->es_curpts, st->es_curdts); pkt->pkt_commercial = t->s_tt_commercial_advice; - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, st->es_buf.sb_err); } } } @@ -1331,7 +1342,6 @@ parse_teletext(service_t *t, elementary_stream_t *st, const uint8_t *data, const uint8_t *d; if(start) { st->es_parser_state = 1; - st->es_buf.sb_err = 0; st->es_parser_ptr = 0; sbuf_reset(&st->es_buf); } @@ -1363,7 +1373,7 @@ parse_teletext(service_t *t, elementary_stream_t *st, const uint8_t *data, pkt = pkt_alloc(buf, psize, st->es_curpts, st->es_curdts); pkt->pkt_commercial = t->s_tt_commercial_advice; - parser_deliver(t, st, pkt); + parser_deliver(t, st, pkt, st->es_buf.sb_err); } } @@ -1371,8 +1381,10 @@ parse_teletext(service_t *t, elementary_stream_t *st, const uint8_t *data, * */ static void -parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt) +parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt, int error) { + pkt->pkt_err = error; + if(SCT_ISAUDIO(st->es_type) && pkt->pkt_pts != PTS_UNSET && (t->s_current_pts == PTS_UNSET || pkt->pkt_pts > t->s_current_pts || From 014f55a4637e67d07f585ebb804b0dadef434ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 18 Oct 2012 11:09:33 +0200 Subject: [PATCH 016/503] Add a status tab to the UI Currently it will display active subscriptions with info about total errors and current bandwidth --- src/dvr/dvr_rec.c | 5 +- src/htsp.c | 5 +- src/main.c | 2 + src/subscriptions.c | 128 +++++++++++++++++++++++++++- src/subscriptions.h | 21 ++++- src/webui/extjs.c | 38 +++++++++ src/webui/static/app/status.js | 134 ++++++++++++++++++++++++++++++ src/webui/static/app/tvheadend.js | 5 ++ src/webui/webui.c | 3 +- 9 files changed, 333 insertions(+), 8 deletions(-) create mode 100644 src/webui/static/app/status.js diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 9ba9e103..a46987fb 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -86,7 +86,10 @@ dvr_rec_subscribe(dvr_entry_t *de) } de->de_s = subscription_create_from_channel(de->de_channel, weight, - buf, st, flags); + buf, st, flags, + NULL, "DVR", + lang_str_get(de->de_title, + NULL)); pthread_create(&de->de_thread, NULL, dvr_thread, de); } diff --git a/src/htsp.c b/src/htsp.c index fc2bce35..99813b0a 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -1167,7 +1167,10 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) hs->hs_s = subscription_create_from_channel(ch, weight, htsp->htsp_logname, - st, 0); + st, 0, + htsp->htsp_peername, + htsp->htsp_username, + htsp->htsp_clientname); return NULL; } diff --git a/src/main.c b/src/main.c index 770d71f1..9cca79fd 100644 --- a/src/main.c +++ b/src/main.c @@ -415,6 +415,8 @@ main(int argc, char **argv) channels_init(); + subscription_init(); + access_init(createdefault); tcp_server_init(); diff --git a/src/subscriptions.c b/src/subscriptions.c index 4249a382..0f816ba2 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -36,6 +36,9 @@ #include "streaming.h" #include "channels.h" #include "service.h" +#include "htsmsg.h" +#include "notify.h" +#include "atomic.h" struct th_subscription_list subscriptions; static gtimer_t subscription_reschedule_timer; @@ -129,12 +132,16 @@ subscription_unlink_service(th_subscription_t *s, int reason) } +/** + * + */ static void subscription_reschedule_cb(void *aux) { subscription_reschedule(); } + /** * */ @@ -212,9 +219,13 @@ subscription_unsubscribe(th_subscription_t *s) streaming_msg_free(s->ths_start_message); free(s->ths_title); + free(s->ths_hostname); + free(s->ths_username); + free(s->ths_client); free(s); subscription_reschedule(); + notify_reload("subscriptions"); } @@ -261,6 +272,14 @@ subscription_input(void *opauqe, streaming_message_t *sm) streaming_msg_free(sm); return; } + + if(sm->sm_type == SMT_PACKET) { + th_pkt_t *pkt = sm->sm_data; + if(pkt->pkt_err) + s->ths_total_err++; + s->ths_bytes += pkt->pkt_payload->pb_size; + } + streaming_target_deliver(s->ths_output, sm); } @@ -282,10 +301,12 @@ subscription_input_direct(void *opauqe, streaming_message_t *sm) */ static th_subscription_t * subscription_create(int weight, const char *name, streaming_target_t *st, - int flags, int direct) + int flags, int direct, const char *hostname, + const char *username, const char *client) { th_subscription_t *s = calloc(1, sizeof(th_subscription_t)); int reject = 0; + static int tally; if(flags & SUBSCRIPTION_RAW_MPEGTS) reject |= SMT_TO_MASK(SMT_PACKET); // Reject parsed frames @@ -298,11 +319,17 @@ subscription_create(int weight, const char *name, streaming_target_t *st, s->ths_weight = weight; s->ths_title = strdup(name); + s->ths_hostname = hostname ? strdup(hostname) : NULL; + s->ths_username = username ? strdup(username) : NULL; + s->ths_client = client ? strdup(client) : NULL; s->ths_total_err = 0; s->ths_output = st; s->ths_flags = flags; time(&s->ths_start); + + s->ths_id = ++tally; + LIST_INSERT_SORTED(&subscriptions, s, ths_global_link, subscription_sort); return s; @@ -315,9 +342,11 @@ subscription_create(int weight, const char *name, streaming_target_t *st, th_subscription_t * subscription_create_from_channel(channel_t *ch, unsigned int weight, const char *name, streaming_target_t *st, - int flags) + int flags, const char *hostname, + const char *username, const char *client) { - th_subscription_t *s = subscription_create(weight, name, st, flags, 0); + th_subscription_t *s = subscription_create(weight, name, st, flags, 0, + hostname, username, client); s->ths_channel = ch; LIST_INSERT_HEAD(&ch->ch_subscriptions, s, ths_channel_link); @@ -349,6 +378,7 @@ subscription_create_from_channel(channel_t *ch, unsigned int weight, service_source_info_free(&si); } + notify_reload("subscriptions"); return s; } @@ -360,7 +390,8 @@ th_subscription_t * subscription_create_from_service(service_t *t, const char *name, streaming_target_t *st, int flags) { - th_subscription_t *s = subscription_create(INT32_MAX, name, st, flags, 1); + th_subscription_t *s = subscription_create(INT32_MAX, name, st, flags, 1, + NULL, NULL, NULL); source_info_t si; int r; @@ -391,6 +422,7 @@ subscription_create_from_service(service_t *t, const char *name, service_source_info_free(&si); subscription_link_service(s, t); + notify_reload("subscriptions"); return s; } @@ -478,3 +510,91 @@ subscription_dummy_join(const char *id, int first) tvhlog(LOG_NOTICE, "subscription", "Dummy join %s ok", id); } + + + +/** + * + */ +htsmsg_t * +subscription_create_msg(th_subscription_t *s) +{ + htsmsg_t *m = htsmsg_create_map(); + + htsmsg_add_u32(m, "id", s->ths_id); + htsmsg_add_u32(m, "start", s->ths_start); + htsmsg_add_u32(m, "errors", s->ths_total_err); + + const char *state; + switch(s->ths_state) { + default: + state = "Idle"; + break; + + case SUBSCRIPTION_TESTING_SERVICE: + state = "Testing"; + break; + + case SUBSCRIPTION_GOT_SERVICE: + state = "Running"; + break; + + case SUBSCRIPTION_BAD_SERVICE: + state = "Bad"; + break; + } + + + htsmsg_add_str(m, "state", state); + + if (s->ths_hostname && s->ths_username && s->ths_client) { + htsmsg_add_str(m, "hostname", s->ths_hostname); + htsmsg_add_str(m, "username", s->ths_username); + htsmsg_add_str(m, "title", s->ths_client); + } else { + htsmsg_add_str(m, "title", s->ths_title); + } + + if(s->ths_channel != NULL) + htsmsg_add_str(m, "channel", s->ths_channel->ch_name); + + if(s->ths_service != NULL) + htsmsg_add_str(m, "service", s->ths_service->s_nicename); + + return m; +} + + +static gtimer_t every_sec; + +/** + * + */ +static void +every_sec_cb(void *aux) +{ + th_subscription_t *s; + gtimer_arm(&every_sec, every_sec_cb, NULL, 1); + + LIST_FOREACH(s, &subscriptions, ths_global_link) { + int errors = s->ths_total_err; + int bw = atomic_exchange(&s->ths_bytes, 0); + + htsmsg_t *m = subscription_create_msg(s); + htsmsg_delete_field(m, "errors"); + htsmsg_add_u32(m, "errors", errors); + htsmsg_add_u32(m, "bw", bw); + htsmsg_add_u32(m, "updateEntry", 1); + notify_by_msg("subscriptions", m); + } +} + + +/** + * + */ +void +subscription_init(void) +{ + gtimer_arm(&every_sec, every_sec_cb, NULL, 1); +} diff --git a/src/subscriptions.h b/src/subscriptions.h index 52738041..ff5d1d1d 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -19,9 +19,14 @@ #ifndef SUBSCRIPTIONS_H #define SUBSCRIPTIONS_H +extern struct th_subscription_list subscriptions; + #define SUBSCRIPTION_RAW_MPEGTS 0x1 typedef struct th_subscription { + + int ths_id; + LIST_ENTRY(th_subscription) ths_global_link; int ths_weight; @@ -46,6 +51,7 @@ typedef struct th_subscription { char *ths_title; /* display title */ time_t ths_start; /* time when subscription started */ int ths_total_err; /* total errors during entire subscription */ + int ths_bytes; // Reset every second to get aprox. bandwidth streaming_target_t ths_input; @@ -55,12 +61,19 @@ typedef struct th_subscription { streaming_message_t *ths_start_message; + char *ths_hostname; + char *ths_username; + char *ths_client; + + } th_subscription_t; /** * Prototypes */ +void subscription_init(void); + void subscription_unsubscribe(th_subscription_t *s); void subscription_set_weight(th_subscription_t *s, unsigned int weight); @@ -71,7 +84,10 @@ th_subscription_t *subscription_create_from_channel(struct channel *ch, unsigned int weight, const char *name, streaming_target_t *st, - int flags); + int flags, + const char *hostname, + const char *username, + const char *client); th_subscription_t *subscription_create_from_service(struct service *t, @@ -89,4 +105,7 @@ void subscription_dummy_join(const char *id, int first); int subscriptions_active(void); +struct htsmsg; +struct htsmsg *subscription_create_msg(th_subscription_t *s); + #endif /* SUBSCRIPTIONS_H */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index cf318fc5..7ede18ae 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -46,6 +46,7 @@ #include "epggrab/private.h" #include "config2.h" #include "lang_codes.h" +#include "subscriptions.h" /** * @@ -138,6 +139,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) extjs_load(hq, "static/app/dvr.js"); extjs_load(hq, "static/app/epggrab.js"); extjs_load(hq, "static/app/config.js"); + extjs_load(hq, "static/app/status.js"); /** * Finally, the app itself @@ -1386,6 +1388,41 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque) return 0; } + +/** + * + */ +static int +extjs_subscriptions(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + htsmsg_t *out, *array; + th_subscription_t *s; + + pthread_mutex_lock(&global_lock); + + if(http_access_verify(hc, ACCESS_ADMIN)) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_UNAUTHORIZED; + } + + out = htsmsg_create_map(); + array = htsmsg_create_list(); + + LIST_FOREACH(s, &subscriptions, ths_global_link) + htsmsg_add_msg(array, NULL, subscription_create_msg(s)); + + pthread_mutex_unlock(&global_lock); + + htsmsg_add_msg(out, "entries", array); + + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); + http_output_content(hc, "text/x-json; charset=UTF-8"); + return 0; +} + + /** * */ @@ -1878,6 +1915,7 @@ extjs_start(void) http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE); http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE); http_path_add("/dvrlist", NULL, extjs_dvrlist, ACCESS_WEB_INTERFACE); + http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE); http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE); http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE); http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE); diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js new file mode 100644 index 00000000..44f999d2 --- /dev/null +++ b/src/webui/static/app/status.js @@ -0,0 +1,134 @@ +/** + * + */ +tvheadend.status = function() { + + tvheadend.subsStore = new Ext.data.JsonStore({ + root : 'entries', + totalProperty : 'totalCount', + fields : [ { + name : 'id' + }, { + name : 'hostname' + }, { + name : 'username' + }, { + name : 'title' + }, { + name : 'channel' + }, { + name : 'service' + }, { + name : 'state' + }, { + name : 'errors' + }, { + name : 'bw' + }, { + name : 'start', + type : 'date', + dateFormat : 'U' /* unix time */ + } ], + url : 'subscriptions', + autoLoad : true, + id : 'id' + }); + + + + tvheadend.comet.on('subscriptions', function(m) { + + if (m.reload != null) tvheadend.subsStore.reload(); + + if (m.updateEntry != null) { + r = tvheadend.subsStore.getById(m.id) + if (typeof r === 'undefined') { + tvheadend.subsStore.reload(); + return; + } + + r.data.channel = m.channel; + r.data.service = m.service; + r.data.state = m.state; + r.data.errors = m.errors; + r.data.bw = m.bw + + tvheadend.subsStore.afterEdit(r); + tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r, + Ext.data.Record.COMMIT); + } + }); + + function renderDate(value) { + var dt = new Date(value); + return dt.format('D j M H:i'); + } + + function renderBw(value) { + return parseInt(value / 125); + } + + var subsCm = new Ext.grid.ColumnModel([{ + width : 50, + id : 'hostname', + header : "Hostname", + dataIndex : 'hostname' + }, { + width : 50, + id : 'username', + header : "Username", + dataIndex : 'username' + }, { + width : 80, + id : 'title', + header : "Title", + dataIndex : 'title' + }, { + width : 50, + id : 'channel', + header : "Channel", + dataIndex : 'channel' + }, { + width : 200, + id : 'service', + header : "Service", + dataIndex : 'service', + }, { + width : 50, + id : 'start', + header : "Start", + dataIndex : 'start', + renderer : renderDate + }, { + width : 50, + id : 'state', + header : "State", + dataIndex : 'state' + }, { + width : 50, + id : 'errors', + header : "Errors", + dataIndex : 'errors' + }, { + width : 50, + id : 'bw', + header : "Bandwidth (kb/s)", + dataIndex : 'bw', + renderer: renderBw + } ]); + + var panel = new Ext.grid.GridPanel({ + loadMask : true, + stripeRows : true, + disableSelection : true, + title : 'Active subscriptions', + iconCls : 'eye', + store : tvheadend.subsStore, + cm : subsCm, + viewConfig : { + forceFit : true + } + }); + return panel; +} + diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index ab942852..3b44f6ca 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -244,6 +244,11 @@ function accessUpdate(o) { tvheadend.rootTabPanel.add(tvheadend.confpanel); } + if (o.admin == true && tvheadend.statuspanel == null) { + tvheadend.statuspanel = new tvheadend.status; + tvheadend.rootTabPanel.add(tvheadend.statuspanel); + } + if (tvheadend.aboutPanel == null) { tvheadend.aboutPanel = new Ext.Panel({ border : false, diff --git a/src/webui/webui.c b/src/webui/webui.c index 6c1041e7..c4f0b787 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -607,7 +607,8 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) } pthread_mutex_lock(&global_lock); - s = subscription_create_from_channel(ch, priority, "HTTP", st, flags); + s = subscription_create_from_channel(ch, priority, "HTTP", st, flags, + NULL, NULL, NULL); pthread_mutex_unlock(&global_lock); if(s) { From 8c78d6a7cc72815bab1d4575a231185ef3875e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 23:20:55 +0200 Subject: [PATCH 017/503] messure bandwidth of MPEGTS packets --- src/subscriptions.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/subscriptions.c b/src/subscriptions.c index 0f816ba2..ca5b1675 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -278,6 +278,9 @@ subscription_input(void *opauqe, streaming_message_t *sm) if(pkt->pkt_err) s->ths_total_err++; s->ths_bytes += pkt->pkt_payload->pb_size; + } else if(sm->sm_type == SMT_MPEGTS) { + pktbuf_t *pb = sm->sm_data; + s->ths_bytes += pb->pb_size; } streaming_target_deliver(s->ths_output, sm); From 18f05081126574661650045e6f727d302a7bd1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 23:23:47 +0200 Subject: [PATCH 018/503] fixed commercial detection on the swedish channel TV4. --- src/teletext.c | 52 ++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/teletext.c b/src/teletext.c index c559cac1..2bb07e1e 100644 --- a/src/teletext.c +++ b/src/teletext.c @@ -469,30 +469,29 @@ teletext_input(service_t *t, elementary_stream_t *st, const uint8_t *tsb) * Swedish TV4 rundown dump (page 192) * - Starttid Titel L{ngd | 20 83 53 - ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 94 2c 2c - 23:47:05 Reklam block 00:04:15 | 8d 83 32 + Starttid Titel L{ngd | 3 3 53 + ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 14 2c 2c + 19:00:00 TV4Nyheterna 00:14:00 | d 7 31 + | 20 20 20 + ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 14 2c 2c + 19:14:00 Lokala nyheter 00:03:00 | 1 1 31 + 19:17:00 TV4Nyheterna 19.00 2 00:02:02 | 7 7 31 + 19:19:02 Vinjett 00:00:03 | 6 6 31 + 19:19:05 Reklam 00:02:30 | 3 3 31 + 19:21:35 Vinjett 00:00:06 | 6 6 31 + 19:21:41 LOKAL BILLBOARD 00:00:16 | 1 1 31 + 19:21:57 Lokalt v{der 00:01:00 | 1 1 31 + 19:22:57 S4NVMT1553 00:00:15 | 6 6 31 + 19:23:12 V{der 00:02:00 | 7 7 31 + 19:25:12 Trailer 00:01:00 | 2 2 31 + 19:26:12 Vinjett 00:00:03 | 6 6 31 + 19:26:15 Reklam 00:02:20 | 3 3 31 + 19:28:35 Vinjett 00:00:07 | 6 6 31 + 19:28:42 Hall} 16:9 00:00:30 | 7 7 31 + ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 14 2c 2c + | 20 20 20 +TV4 | 54 56 34 | 20 20 20 - ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 94 2c 2c - 23:47:30 HV3 BENGT OCH PETRA 00:00:03 | 20 86 32 - 23:47:34 S-6/6 : Spoons I 6 ( 00:10:23 | 20 87 32 - 23:57:57 RV3 FOLK I TRAPPAN 00:00:03 | 20 86 32 - 23:59:36 Reklam block 00:02:50 | 20 83 32 - 00:00:01 LINEUP13 BENGT OCH P 00:00:13 | 20 86 30 - 00:00:14 AABILO6123 00:00:13 | 20 86 30 - 00:00:28 S-4/6 : Allo Allo IV 00:10:28 | 20 87 30 - 00:10:57 RV3 VITA RENA PRICKA 00:00:03 | 20 86 30 - 00:11:00 LOKAL REKLAM 00:01:31 | 20 81 30 - 00:16:37 Reklam block 00:04:25 | 20 83 30 - 00:16:58 HV3 BYGGLOV 2 00:00:03 | 20 86 30 - 00:17:51 Trailer block 00:01:20 | 20 82 30 - 00:18:21 S-4/6 : Allo Allo IV 00:14:14 | 20 87 30 - 00:32:36 AABILO6123 00:00:13 | 20 86 30 - ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, | 94 2c 2c - se {ven rundown.tv4.se | 83 73 65 - | 83 20 20 - 23:43 | 83 20 20 - */ @@ -521,9 +520,8 @@ teletext_rundown_copy(tt_private_t *ttp, tt_mag_t *ttm) { /* Sanity check */ if(memcmp((char *)ttm->ttm_page + 2, "Starttid", strlen("Starttid")) || - memcmp((char *)ttm->ttm_page + 11,"Titel", strlen("Titel")) || - memcmp((char *)ttm->ttm_page + 20 * 40 + 9, - "rundown.tv4.se", strlen("rundown.tv4.se"))) + memcmp((char *)ttm->ttm_page + 11, "Titel", strlen("Titel")) || + memcmp((char *)ttm->ttm_page + 21 * 40, "TV4", strlen("TV4"))) return; memcpy(ttp->ttp_rundown, ttm->ttm_page, 23 * 40); @@ -544,7 +542,7 @@ teletext_rundown_scan(service_t *t, tt_private_t *ttp) for(i = 0; i < 23; i++) { l = ttp->ttp_rundown + 40 * i; - if((l[1] & 0xf0) != 0x80 || !is_tt_clock(l + 32) || !is_tt_clock(l + 2)) + if((l[1] & 0xf0) != 0x00 || !is_tt_clock(l + 32) || !is_tt_clock(l + 2)) continue; if(!memcmp(l + 11, "Nyhetspuff", strlen("Nyhetspuff"))) From 5b07fa5b35cd744f1371eb816a1c60fa2709bfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 17:44:33 +0100 Subject: [PATCH 019/503] Log the reason for stoping a http stream. --- src/webui/webui.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index c4f0b787..7945bfd5 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -180,10 +180,10 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, //Check socket status getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); if(err) { - tvhlog(LOG_DEBUG, "webui", "Client hung up, exit streaming"); + tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); run = 0; }else if(timeouts >= 20) { - tvhlog(LOG_WARNING, "webui", "Timeout waiting for packets"); + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, timeout waiting for packets", hc->hc_url_orig); run = 0; } } @@ -212,12 +212,14 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, case SMT_STOP: muxer_close(mux); + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, + streaming_code2txt(sm->sm_code)); run = 0; break; case SMT_SERVICE_STATUS: if(getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, &err, &errlen)) { - tvhlog(LOG_DEBUG, "webui", "Client hung up, exit streaming"); + tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); run = 0; } break; @@ -226,19 +228,24 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, break; case SMT_NOSTART: - tvhlog(LOG_DEBUG, "webui", "Couldn't start stream for %s", hc->hc_url_orig); + tvhlog(LOG_WARNING, "webui", "Couldn't start streaming %s, %s", hc->hc_url_orig, + streaming_code2txt(sm->sm_code)); run = 0; break; case SMT_EXIT: - muxer_close(mux); + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, + streaming_code2txt(sm->sm_code)); run = 0; break; } + streaming_msg_free(sm); - if(mux->m_errors) + if(mux->m_errors) { + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, muxer reported errors", hc->hc_url_orig); run = 0; + } } muxer_destroy(mux); From 0d4c30b7da80686a0aa451ee2362537dd8f219b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 17:45:09 +0100 Subject: [PATCH 020/503] make sure the muxer is closed only when its been initilized. --- src/webui/webui.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index 7945bfd5..1c3972b0 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -144,6 +144,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, { streaming_message_t *sm; int run = 1; + int started = 0; muxer_t *mux = NULL; int timeouts = 0; struct timespec ts; @@ -208,10 +209,11 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, http_output_content(hc, muxer_mime(mux, sm->sm_data)); muxer_init(mux, sm->sm_data, name); + + started = 1; break; case SMT_STOP: - muxer_close(mux); tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, streaming_code2txt(sm->sm_code)); run = 0; @@ -248,6 +250,9 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } } + if(started) + muxer_close(mux); + muxer_destroy(mux); } From 77a6f4df8d263a4f8723f789648cbda382c165bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 17:42:04 +0200 Subject: [PATCH 021/503] Let the muxer deref packets all the time. If there is an error, the packet will be droped. --- src/dvr/dvr_rec.c | 4 ++-- src/dvr/mkmux.c | 3 +-- src/muxer_pass.c | 3 +-- src/webui/webui.c | 5 ++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index a46987fb..c030d0cf 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -445,8 +445,8 @@ dvr_thread(void *aux) if(dispatch_clock > de->de_start - (60 * de->de_start_extra)) { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); - if(!muxer_write_pkt(de->de_mux, sm->sm_data)) - sm->sm_data = NULL; + muxer_write_pkt(de->de_mux, sm->sm_data); + sm->sm_data = NULL; } break; diff --git a/src/dvr/mkmux.c b/src/dvr/mkmux.c index 93fa24fc..cf95e95e 100644 --- a/src/dvr/mkmux.c +++ b/src/dvr/mkmux.c @@ -899,8 +899,7 @@ mk_mux_write_pkt(mk_mux_t *mkm, struct th_pkt *pkt) mk_write_frame_i(mkm, t, pkt); } - if(!mkm->error) - pkt_ref_dec(pkt); + pkt_ref_dec(pkt); return mkm->error; } diff --git a/src/muxer_pass.c b/src/muxer_pass.c index e5866f92..af73cc30 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -231,8 +231,7 @@ pass_muxer_write_pkt(muxer_t *m, void *data) break; } - if(!pm->pm_error) - pktbuf_ref_dec(pb); + pktbuf_ref_dec(pb); return pm->pm_error; } diff --git a/src/webui/webui.c b/src/webui/webui.c index 1c3972b0..14766b74 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -199,9 +199,8 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: - if(!muxer_write_pkt(mux, sm->sm_data)) - sm->sm_data = NULL; - + muxer_write_pkt(mux, sm->sm_data); + sm->sm_data = NULL; break; case SMT_START: From f3da081bbae7b9fb1af8c73728db6011900a64ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 18 Oct 2012 18:33:53 +0200 Subject: [PATCH 022/503] free http streaming ts buffer when a service stops --- src/service.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/service.c b/src/service.c index 4c3e8fdc..c338a98f 100644 --- a/src/service.c +++ b/src/service.c @@ -153,6 +153,8 @@ service_stop(service_t *t) TAILQ_FOREACH(st, &t->s_components, es_link) stream_clean(st); + sbuf_free(&t->s_tsbuf); + t->s_status = SERVICE_IDLE; pthread_mutex_unlock(&t->s_stream_mutex); From bb119bc879d22b49d31a451b5c30736df23fd156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 21 Aug 2012 21:20:23 +0100 Subject: [PATCH 023/503] reconfigure muxes when stream source changes (usually on pmt update). --- src/dvr/dvr_rec.c | 2 ++ src/muxer.c | 13 +++++++++++++ src/muxer.h | 3 +++ src/muxer_pass.c | 15 +++++++++++++-- src/muxer_tvh.c | 16 ++++++++++++++++ src/webui/webui.c | 10 +++++++--- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index c030d0cf..cf8319e0 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -468,6 +468,8 @@ dvr_thread(void *aux) "dvr", "Recording completed: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL)); + } else if(sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) { + muxer_reconfigure(de->de_mux, sm->sm_data); } else { if(de->de_last_error != sm->sm_code) { diff --git a/src/muxer.c b/src/muxer.c index ac69fe92..cd3071fe 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -225,6 +225,19 @@ muxer_init(muxer_t *m, const struct streaming_start *ss, const char *name) } +/** + * sanity wrapper arround m_reconfigure() + */ +int +muxer_reconfigure(muxer_t *m, const struct streaming_start *ss) +{ + if(!m || !ss) + return -1; + + return m->m_reconfigure(m, ss); +} + + /** * sanity wrapper arround m_open_file() */ diff --git a/src/muxer.h b/src/muxer.h index 1f167192..6fdeb918 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -42,6 +42,8 @@ typedef struct muxer { int (*m_init) (struct muxer *, // Init The muxer with streams const struct streaming_start *, const char *); + int (*m_reconfigure)(struct muxer *, // Reconfigure the muxer on + const struct streaming_start *); // stream changes int (*m_close) (struct muxer *); // Close the muxer void (*m_destroy) (struct muxer *); // Free the memory int (*m_write_meta) (struct muxer *, struct epg_broadcast *); // Append epg data @@ -65,6 +67,7 @@ muxer_t *muxer_create(struct service *s, muxer_container_type_t mc); int muxer_open_file (muxer_t *m, const char *filename); int muxer_open_stream (muxer_t *m, int fd); int muxer_init (muxer_t *m, const struct streaming_start *ss, const char *name); +int muxer_reconfigure (muxer_t *m, const struct streaming_start *ss); int muxer_close (muxer_t *m); int muxer_destroy (muxer_t *m); int muxer_write_meta (muxer_t *m, struct epg_broadcast *eb); diff --git a/src/muxer_pass.c b/src/muxer_pass.c index af73cc30..b61aa400 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -90,10 +90,10 @@ pass_muxer_mime(muxer_t* m, const struct streaming_start *ss) /** - * Init the passthrough muxer with streams + * Generate the pmt and pat from a streaming start message */ static int -pass_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) +pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) { pass_muxer_t *pm = (pass_muxer_t*)m; @@ -128,6 +128,16 @@ pass_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) } +/** + * Init the passthrough muxer with streams + */ +static int +pass_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) +{ + return pass_muxer_reconfigure(m, ss); +} + + /** * Open the muxer as a stream muxer (using a non-seekable socket) */ @@ -303,6 +313,7 @@ pass_muxer_create(service_t *s, muxer_container_type_t mc) pm->m_open_stream = pass_muxer_open_stream; pm->m_open_file = pass_muxer_open_file; pm->m_init = pass_muxer_init; + pm->m_reconfigure = pass_muxer_reconfigure; pm->m_mime = pass_muxer_mime; pm->m_write_meta = pass_muxer_write_meta; pm->m_write_pkt = pass_muxer_write_pkt; diff --git a/src/muxer_tvh.c b/src/muxer_tvh.c index 7708b206..6d5a0de1 100644 --- a/src/muxer_tvh.c +++ b/src/muxer_tvh.c @@ -79,6 +79,21 @@ tvh_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) } +/** + * Multisegment matroska files do exist but I am not sure if they are supported + * by many media players. For now, we'll treat it as an error. + */ +static int +tvh_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) +{ + tvh_muxer_t *tm = (tvh_muxer_t*)m; + + tm->m_errors++; + + return -1; +} + + /** * Open the muxer as a stream muxer (using a non-seekable socket) */ @@ -196,6 +211,7 @@ tvh_muxer_create(muxer_container_type_t mc) tm->m_open_file = tvh_muxer_open_file; tm->m_mime = tvh_muxer_mime; tm->m_init = tvh_muxer_init; + tm->m_reconfigure = tvh_muxer_reconfigure; tm->m_write_meta = tvh_muxer_write_meta; tm->m_write_pkt = tvh_muxer_write_pkt; tm->m_close = tvh_muxer_close; diff --git a/src/webui/webui.c b/src/webui/webui.c index 14766b74..fb22dbd0 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -213,9 +213,13 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, break; case SMT_STOP: - tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, - streaming_code2txt(sm->sm_code)); - run = 0; + if(sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) { + muxer_reconfigure(mux, sm->sm_data); + } else { + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, + streaming_code2txt(sm->sm_code)); + run = 0; + } break; case SMT_SERVICE_STATUS: From 899e833cab64da5ae466f257f1a94133f1ec8616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Thu, 18 Oct 2012 14:36:16 +0200 Subject: [PATCH 024/503] Fix a memory leak in avc_convert_pkt --- src/avc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/avc.c b/src/avc.c index afad9e4a..0a473f15 100644 --- a/src/avc.c +++ b/src/avc.c @@ -186,8 +186,6 @@ avc_convert_pkt(th_pkt_t *src) pkt->pkt_header = NULL; pkt->pkt_payload = NULL; - pkt->pkt_payload = malloc(sizeof(pktbuf_t)); - pkt->pkt_payload->pb_refcount=1; if (src->pkt_header) { sbuf_t headers; sbuf_init(&headers); From 9c1a72bc7ad5c3200fe9748bdeca2af790a7077a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 19 Oct 2012 14:51:25 +0100 Subject: [PATCH 025/503] Correct typos in the encodings list. Fixes #1338. --- data/conf/charset | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/conf/charset b/data/conf/charset index 5020aa8a..e9579f8b 100644 --- a/data/conf/charset +++ b/data/conf/charset @@ -17,7 +17,7 @@ "tsid": 1111, "onid": 1, "charset": "PL_AUTO", - "sid": 7269, + "sid": 7296, "description": "Astra 19.2E TV Trwam" }, { @@ -397,7 +397,7 @@ "charset": "PL_AUTO", "sid": 13511, "description": "Hotbird 13.0 Rebel TV" - } + }, { "tsid": 15700, "onid": 318, From faa9690d240860137d5ea576eeb5b782b64d6bbd Mon Sep 17 00:00:00 2001 From: Danmark Date: Tue, 16 Oct 2012 14:03:35 +0300 Subject: [PATCH 026/503] [PR-166] - channelId was misspelled in error message --- src/htsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index 99813b0a..4225b774 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -1,6 +1,6 @@ /* * tvheadend, HTSP interface - * Copyright (C) 2007 Andreas Öman + * Copyright (C) 2007 Andreas Öman * * 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 @@ -1114,7 +1114,7 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) htsp_subscription_t *hs; if(htsmsg_get_u32(in, "channelId", &chid)) - return htsp_error("Missing argument 'channeId'"); + return htsp_error("Missing argument 'channelId'"); if(htsmsg_get_u32(in, "subscriptionId", &sid)) return htsp_error("Missing argument 'subscriptionId'"); From ab422204c80f95346a7e7a6ba50fe1fe5b8b725b Mon Sep 17 00:00:00 2001 From: Joakim Hernberg Date: Thu, 18 Oct 2012 18:02:38 +0200 Subject: [PATCH 027/503] [PR-170] add support for Ku Band lnb with lof 11300 KHz. Fixes #1332. --- src/dvb/dvb_satconf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dvb/dvb_satconf.c b/src/dvb/dvb_satconf.c index 94a44e3a..ba607ef5 100644 --- a/src/dvb/dvb_satconf.c +++ b/src/dvb/dvb_satconf.c @@ -296,6 +296,7 @@ dvb_lnblist_get(void) add_to_lnblist(array, "C-Band"); add_to_lnblist(array, "C-Multi"); add_to_lnblist(array, "Circular 10750"); + add_to_lnblist(array, "Ku 11300"); return array; } @@ -338,5 +339,9 @@ dvb_lnb_get_frequencies(const char *id, int *f_low, int *f_hi, int *f_switch) *f_low = 10750000; *f_hi = 0; *f_switch = 0; + } else if(!strcmp(id, "Ku 11300")) { + *f_low = 11300000; + *f_hi = 0; + *f_switch = 0; } } From 72d90ee2cd23e076aef39a6f5c0547d538258e29 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 19 Oct 2012 17:21:37 +0100 Subject: [PATCH 028/503] Stop disabled muxes being re-added to the scan list on startup. --- src/dvb/dvb_multiplex.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 3b13f15e..ac7e6c5d 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -294,12 +294,14 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, dvb_transport_load(tdmi, identifier); dvb_mux_notify(tdmi); - if(enabled && initialscan) { - tda->tda_initial_num_mux++; - tdmi->tdmi_table_initial = 1; - mux_link_initial(tda, tdmi); - } else { - dvb_mux_add_to_scan_queue(tdmi); + if(enabled) { + if(initialscan) { + tda->tda_initial_num_mux++; + tdmi->tdmi_table_initial = 1; + mux_link_initial(tda, tdmi); + } else { + dvb_mux_add_to_scan_queue(tdmi); + } } return tdmi; From 3236c4ac62667ad404ae345a1ea48a2044de29e3 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 19 Oct 2012 21:45:53 +0100 Subject: [PATCH 029/503] SID to channel number config option was not being stored or loaded. --- src/dvb/dvb_adapter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index ee9e8d49..f58eb1b5 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -88,6 +88,7 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "idlescan", tda->tda_idlescan); htsmsg_add_u32(m, "idleclose", tda->tda_idleclose); htsmsg_add_u32(m, "skip_checksubscr", tda->tda_skip_checksubscr); + htsmsg_add_u32(m, "sidtochan", tda->tda_sidtochan); htsmsg_add_u32(m, "qmon", tda->tda_qmon); htsmsg_add_u32(m, "dump_muxes", tda->tda_dump_muxes); htsmsg_add_u32(m, "poweroff", tda->tda_poweroff); @@ -583,6 +584,7 @@ dvb_adapter_init(uint32_t adapter_mask) htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan); htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose); htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr); + htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan); htsmsg_get_u32(c, "qmon", &tda->tda_qmon); htsmsg_get_u32(c, "dump_muxes", &tda->tda_dump_muxes); htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff); From 3efdfbf0af94e822072ab466d0511df14affb32b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 23 Oct 2012 12:03:15 +0100 Subject: [PATCH 030/503] Ensure scan file download is properly validated and fix to cope with new PPA name due to launchpad crap! --- support/launchpad-ppa | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/support/launchpad-ppa b/support/launchpad-ppa index 43f8f453..cf3c0762 100755 --- a/support/launchpad-ppa +++ b/support/launchpad-ppa @@ -5,9 +5,15 @@ # environment variables # +# Terminate +function die +{ + echo $* + exit 1 +} + # CMD CMD=$(basename $0) -echo $CMD # Configuration TVH_ROOT=$(cd $(dirname $0)/..; pwd) @@ -29,15 +35,14 @@ VERFILE=$TVH_ROOT/src/version.c VER=$($TVH_ROOT/support/version $VERFILE) # Fetch scan files -./support/getmuxlist +./support/getmuxlist || die "failed to fetch dvb-scan files" # For each distro for d in $TVH_DIST; do V=${VER}~${d} - echo $V # Create changelog - $TVH_ROOT/support/changelog "$CHANGELOG" "$d" "$VER" + $TVH_ROOT/support/changelog "$CHANGELOG" "$d" "$VER" || exit 1 # Build source package dpkg-buildpackage -I.git* -S -sgpg -pgpg || exit 1 @@ -51,7 +56,7 @@ for d in $TVH_DIST; do # Upload else - dput tvh-$PPA ../tvheadend_${V}_source.changes || exit 1 + dput $DPUT_OPT tvh-${PPA} ../tvheadend_${V}_source.changes || exit 1 fi done From ae31362eec9f62d4806b1729e4cadc20c5221cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Tue, 23 Oct 2012 13:23:54 +0200 Subject: [PATCH 031/503] [PR-171] Streaming queue size protection --- src/streaming.c | 39 ++++++++++++++++++++++++++++++++++++++- src/streaming.h | 2 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/streaming.c b/src/streaming.c index 7b4559fe..2c3c0dad 100755 --- a/src/streaming.c +++ b/src/streaming.c @@ -52,7 +52,14 @@ streaming_queue_deliver(void *opauqe, streaming_message_t *sm) streaming_queue_t *sq = opauqe; pthread_mutex_lock(&sq->sq_mutex); - TAILQ_INSERT_TAIL(&sq->sq_queue, sm, sm_link); + + /* queue size protection */ + int queue_size = streaming_queue_size(&sq->sq_queue); + if (queue_size > 1500000) + streaming_msg_free(sm); + else + TAILQ_INSERT_TAIL(&sq->sq_queue, sm, sm_link); + pthread_cond_signal(&sq->sq_cond); pthread_mutex_unlock(&sq->sq_mutex); } @@ -331,6 +338,36 @@ streaming_queue_clear(struct streaming_message_queue *q) } +/** + * + */ +int streaming_queue_size(struct streaming_message_queue *q) +{ + streaming_message_t *sm; + int size = 0; + + TAILQ_FOREACH(sm, q, sm_link) { + if (sm->sm_type == SMT_PACKET) + { + th_pkt_t *pkt = sm->sm_data; + if (pkt && pkt->pkt_payload) + { + size += pkt->pkt_payload->pb_size; + } + } + else if (sm->sm_type == SMT_MPEGTS) + { + pktbuf_t *pkt_payload = sm->sm_data; + if (pkt_payload) + { + size += pkt_payload->pb_size; + } + } + } + return size; +} + + /** * */ diff --git a/src/streaming.h b/src/streaming.h index eb2919c9..cee24cf8 100644 --- a/src/streaming.h +++ b/src/streaming.h @@ -73,6 +73,8 @@ void streaming_queue_init(streaming_queue_t *sq, int reject_filter); void streaming_queue_clear(struct streaming_message_queue *q); +int streaming_queue_size(struct streaming_message_queue *q); + void streaming_queue_deinit(streaming_queue_t *sq); void streaming_target_connect(streaming_pad_t *sp, streaming_target_t *st); From a06ee4fba9eb94a86f2fbaca42ced4b5e972ce93 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 23 Oct 2012 13:42:06 +0100 Subject: [PATCH 032/503] [PR-171] Update stream queue size protection to be flexible. The queue size limits are now configurable in the queue init function. For now only HTTP queues are bounded, the others should not be necessary. --- src/streaming.c | 21 +++++++++++++++++---- src/streaming.h | 5 ++++- src/tvheadend.h | 7 ++++--- src/webui/webui.c | 22 ++++++++++++++++++---- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/streaming.c b/src/streaming.c index 2c3c0dad..bf1614f8 100755 --- a/src/streaming.c +++ b/src/streaming.c @@ -54,8 +54,10 @@ streaming_queue_deliver(void *opauqe, streaming_message_t *sm) pthread_mutex_lock(&sq->sq_mutex); /* queue size protection */ - int queue_size = streaming_queue_size(&sq->sq_queue); - if (queue_size > 1500000) + // TODO: would be better to update size as we go, but this would + // require updates elsewhere to ensure all removals from the queue + // are covered (new function) + if (sq->sq_maxsize && streaming_queue_size(&sq->sq_queue) >= sq->sq_maxsize) streaming_msg_free(sm); else TAILQ_INSERT_TAIL(&sq->sq_queue, sm, sm_link); @@ -69,13 +71,24 @@ streaming_queue_deliver(void *opauqe, streaming_message_t *sm) * */ void -streaming_queue_init(streaming_queue_t *sq, int reject_filter) +streaming_queue_init2(streaming_queue_t *sq, int reject_filter, size_t maxsize) { streaming_target_init(&sq->sq_st, streaming_queue_deliver, sq, reject_filter); pthread_mutex_init(&sq->sq_mutex, NULL); pthread_cond_init(&sq->sq_cond, NULL); TAILQ_INIT(&sq->sq_queue); + + sq->sq_maxsize = maxsize; +} + +/** + * + */ +void +streaming_queue_init(streaming_queue_t *sq, int reject_filter) +{ + streaming_queue_init2(sq, reject_filter, 0); // 0 = unlimited } @@ -341,7 +354,7 @@ streaming_queue_clear(struct streaming_message_queue *q) /** * */ -int streaming_queue_size(struct streaming_message_queue *q) +size_t streaming_queue_size(struct streaming_message_queue *q) { streaming_message_t *sm; int size = 0; diff --git a/src/streaming.h b/src/streaming.h index cee24cf8..5dcda7db 100644 --- a/src/streaming.h +++ b/src/streaming.h @@ -71,9 +71,12 @@ void streaming_target_init(streaming_target_t *st, void streaming_queue_init(streaming_queue_t *sq, int reject_filter); +void streaming_queue_init2 + (streaming_queue_t *sq, int reject_filter, size_t maxsize); + void streaming_queue_clear(struct streaming_message_queue *q); -int streaming_queue_size(struct streaming_message_queue *q); +size_t streaming_queue_size(struct streaming_message_queue *q); void streaming_queue_deinit(streaming_queue_t *sq); diff --git a/src/tvheadend.h b/src/tvheadend.h index f7bb0f7e..ae785555 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -332,9 +332,10 @@ typedef struct streaming_queue { streaming_target_t sq_st; - pthread_mutex_t sq_mutex; /* Protects sp_queue */ - pthread_cond_t sq_cond; /* Condvar for signalling new - packets */ + pthread_mutex_t sq_mutex; /* Protects sp_queue */ + pthread_cond_t sq_cond; /* Condvar for signalling new packets */ + + size_t sq_maxsize; /* Max queue size (bytes) */ struct streaming_message_queue sq_queue; diff --git a/src/webui/webui.c b/src/webui/webui.c index fb22dbd0..e404d858 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -541,6 +541,8 @@ http_stream_service(http_connection_t *hc, service_t *service) dvr_config_t *cfg; muxer_container_type_t mc; int flags; + const char *str; + size_t qsize ; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -548,14 +550,19 @@ http_stream_service(http_connection_t *hc, service_t *service) mc = cfg->dvr_mc; } + if ((str = http_arg_get(&hc->hc_req_args, "qsize"))) + qsize = atoll(str); + else + qsize = 1500000; + if(mc == MC_PASS) { - streaming_queue_init(&sq, SMT_PACKET); + streaming_queue_init2(&sq, SMT_PACKET, qsize); gh = NULL; tsfix = NULL; st = &sq.sq_st; flags = SUBSCRIPTION_RAW_MPEGTS; } else { - streaming_queue_init(&sq, 0); + streaming_queue_init2(&sq, 0, qsize); gh = globalheaders_create(&sq.sq_st); tsfix = tsfix_create(gh); st = tsfix; @@ -600,6 +607,8 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) int priority = 100; int flags; muxer_container_type_t mc; + char *str; + size_t qsize; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -607,14 +616,19 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) mc = cfg->dvr_mc; } + if ((str = http_arg_get(&hc->hc_req_args, "qsize"))) + qsize = atoll(str); + else + qsize = 1500000; + if(mc == MC_PASS) { - streaming_queue_init(&sq, SMT_PACKET); + streaming_queue_init2(&sq, SMT_PACKET, qsize); gh = NULL; tsfix = NULL; st = &sq.sq_st; flags = SUBSCRIPTION_RAW_MPEGTS; } else { - streaming_queue_init(&sq, 0); + streaming_queue_init2(&sq, 0, qsize); gh = globalheaders_create(&sq.sq_st); tsfix = tsfix_create(gh); st = tsfix; From 8191e132f83f8078d13f5e9c049c99bafb5264a7 Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Wed, 17 Oct 2012 10:08:22 +0200 Subject: [PATCH 033/503] [PR-167] campt: support for OSCam; fixes capmt: add oscam mode to the webui and config capmt: add support for oscam-dvbapi (boxtype=pc) capmt: add multiple adapter support in oscam mode capmt: fix sending provider id in PMT data capmt: fix handling multiple CAIDs on the same ECM pid --- docs/html/config_capmt.html | 9 + src/capmt.c | 524 +++++++++++++++++++--------- src/webui/static/app/capmteditor.js | 12 +- 3 files changed, 372 insertions(+), 173 deletions(-) diff --git a/docs/html/config_capmt.html b/docs/html/config_capmt.html index 23c5e8c1..26e04a48 100644 --- a/docs/html/config_capmt.html +++ b/docs/html/config_capmt.html @@ -49,6 +49,15 @@ This module will communicate the received control-words back to Tvheadend via Port 9000 +
OSCam mode +
If selected, connection will be made directly to oscam without using LD_PRELOAD hack.
+ Port 9000 will be used automatically.
+ The following lines are required in [dvbapi] section of oscam.conf file: +
+
boxtype = pc
+ pmt_mode = 4 +
+
Comment
Allows the administrator to set a comment only visible in this editor. It does not serve any active purpose. diff --git a/src/capmt.c b/src/capmt.c index 18cdf2da..fb015fb1 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include #include "tvheadend.h" #include "dvb/dvb.h" @@ -67,6 +70,15 @@ #define CW_DUMP(buf, len, format, ...) \ printf(format, __VA_ARGS__); int j; for (j = 0; j < len; ++j) printf("%02X ", buf[j]); printf("\n"); +#pragma GCC diagnostic ignored "-Warray-bounds" +#define MAX_CA 4 +#define MAX_INDEX 64 +#define KEY_SIZE 8 +#define INFO_SIZE (2+KEY_SIZE+KEY_SIZE) +#define EVEN_OFF (2) +#define ODD_OFF (2+KEY_SIZE) +static unsigned char ca_info[MAX_CA][MAX_INDEX][INFO_SIZE]; + /** * */ @@ -82,7 +94,7 @@ static pthread_cond_t capmt_config_changed; typedef struct capmt_descriptor { uint8_t cad_type; uint8_t cad_length; - uint8_t cad_data[16]; + uint8_t cad_data[17]; } __attribute__((packed)) capmt_descriptor_t; /** @@ -108,6 +120,8 @@ typedef struct capmt_caid_ecm { uint16_t cce_caid; /** ecm pid */ uint16_t cce_ecmpid; + /** provider id */ + uint32_t cce_providerid; /** last ecm size */ uint32_t cce_ecmsize; /** last ecm buffer */ @@ -172,10 +186,11 @@ typedef struct capmt { int capmt_port; char *capmt_comment; char *capmt_id; + int capmt_oscam; /* capmt sockets */ int capmt_sock; - int capmt_sock_ca0; + int capmt_sock_ca0[MAX_CA]; /* thread flags */ int capmt_connected; @@ -266,35 +281,121 @@ static void handle_ca0(capmt_t* capmt) { capmt_service_t *ct; service_t *t; - int ret; + int ret, bufsize; + int *request; + ca_descr_t *ca; + ca_pid_t *cpd; + int process_key, process_next, cai; + int i, j; - uint8_t invalid[8], buffer[20], *even, *odd; + if (capmt->capmt_oscam) + bufsize = sizeof(int) + sizeof(ca_descr_t); + else + bufsize = 18; + + uint8_t invalid[8], buffer[bufsize], *even, *odd; uint16_t seq; memset(invalid, 0, 8); tvhlog(LOG_INFO, "capmt", "got connection from client ..."); + i = 0; while (capmt->capmt_running) { + process_key = 0; - ret = recv(capmt->capmt_sock_ca0, buffer, 18, MSG_WAITALL); + // receiving data from UDP socket + if (!capmt->capmt_oscam) { + ret = recv(capmt->capmt_sock_ca0[0], buffer, bufsize, MSG_WAITALL); - if (ret < 0) - tvhlog(LOG_ERR, "capmt", "error receiving over socket"); - else if (ret == 0) { - // normal socket shutdown - tvhlog(LOG_INFO, "capmt", "normal socket shutdown"); - break; - } - - /* get control words */ - seq = buffer[0] | ((uint16_t)buffer[1] << 8); - even = &buffer[2]; - odd = &buffer[10]; + if (ret < 0) + tvhlog(LOG_ERR, "capmt", "error receiving over socket"); + else if (ret == 0) { + // normal socket shutdown + tvhlog(LOG_INFO, "capmt", "normal socket shutdown"); + break; + } + } else { + process_next = 0; + if (capmt->capmt_sock_ca0[i] > 0) { + ret = recv(capmt->capmt_sock_ca0[i], buffer, bufsize, MSG_DONTWAIT); + if (ret < 0) + process_next = 1; + else if (ret == 0) { + // normal socket shutdown + tvhlog(LOG_INFO, "capmt", "normal socket shutdown"); + close(capmt->capmt_sock_ca0[i]); + capmt->capmt_sock_ca0[i] = -1; + int still_left = 0; + for (j = 0; j < MAX_CA; j++) { + if (capmt->capmt_sock_ca0[j] > 0) { + still_left = 1; + break; + } + } + if (still_left) //this socket is closed but there are others active + process_next = 1; + else //all sockets closed + break; + } + } else + process_next = 1; + + if (process_next) { + i++; + if (i >= MAX_CA) + i = 0; + usleep(10 * 1000); + continue; + } + } + + // parsing data + if (capmt->capmt_oscam) { + cai = i; + request = (int *) &buffer; + if (*request == CA_SET_PID) { + cpd = (ca_pid_t *)&buffer[sizeof(int)]; + tvhlog(LOG_DEBUG, "capmt", "CA_SET_PID cai %d req %d (%d %04x)", cai, *request, cpd->index, cpd->pid); + + if (cpd->index >=0 && cpd->index < MAX_INDEX) { + ca_info[cai][cpd->index][0] = (cpd->pid >> 0) & 0xff; + ca_info[cai][cpd->index][1] = (cpd->pid >> 8) & 0xff; + } else if (cpd->index == -1) { + memset(&ca_info[cai], 0, sizeof(ca_info[cai])); + } else + tvhlog(LOG_ERR, "capmt", "Invalid index %d in CA_SET_PID (%d) for ca id %d", cpd->index, MAX_INDEX, cai); + } else if (*request == CA_SET_DESCR) { + ca = (ca_descr_t *)&buffer[sizeof(int)]; + tvhlog(LOG_DEBUG, "capmt", "CA_SET_DESCR cai %d req %d par %d idx %d %02x%02x%02x%02x%02x%02x%02x%02x", cai, *request, ca->parity, ca->index, ca->cw[0], ca->cw[1], ca->cw[2], ca->cw[3], ca->cw[4], ca->cw[5], ca->cw[6], ca->cw[7]); + + if(ca->parity==0) { + memcpy(&ca_info[cai][ca->index][EVEN_OFF],ca->cw,KEY_SIZE); // even key + process_key = 1; + } else if(ca->parity==1) { + memcpy(&ca_info[cai][ca->index][ODD_OFF],ca->cw,KEY_SIZE); // odd key + process_key = 1; + } else + tvhlog(LOG_ERR, "capmt", "Invalid parity %d in CA_SET_DESCR for ca id %d", ca->parity, cai); + + seq = ca_info[cai][ca->index][0] | ((uint16_t)ca_info[cai][ca->index][1] << 8); + even = &ca_info[cai][ca->index][EVEN_OFF]; + odd = &ca_info[cai][ca->index][ODD_OFF]; + } + } else { + /* get control words */ + seq = buffer[0] | ((uint16_t)buffer[1] << 8); + even = &buffer[2]; + odd = &buffer[10]; + process_key = 1; + } + + // processing key + if (process_key) { LIST_FOREACH(ct, &capmt->capmt_services, ct_link) { t = ct->ct_service; - if(ret < 18) { + if(ret < bufsize) { if(ct->ct_keystate != CT_FORBIDDEN) { tvhlog(LOG_ERR, "capmt", "Can not descramble service \"%s\", access denied", t->s_svcname); @@ -317,11 +418,31 @@ handle_ca0(capmt_t* capmt) { ct->ct_keystate = CT_RESOLVED; } + } } tvhlog(LOG_INFO, "capmt", "connection from client closed ..."); } +static int +capmt_create_udp_socket(int *socket, int port) +{ + *socket = tvh_socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + + struct sockaddr_in serv_addr; + serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + serv_addr.sin_port = htons( (unsigned short int)port); + serv_addr.sin_family = AF_INET; + + if (bind(*socket, (const struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) + { + perror("[CapmtServer] ERROR binding to ca0"); + return 0; + } + else + return 1; +} + /** * */ @@ -330,11 +451,13 @@ capmt_thread(void *aux) { capmt_t *capmt = aux; struct timespec ts; - int d; + int d, i, bind_ok = 0; + th_dvb_adapter_t *tda; while (capmt->capmt_running) { capmt->capmt_sock = -1; - capmt->capmt_sock_ca0 = -1; + for (i = 0; i < MAX_CA; i++) + capmt->capmt_sock_ca0[i] = -1; capmt->capmt_connected = 0; pthread_mutex_lock(&global_lock); @@ -356,16 +479,21 @@ capmt_thread(void *aux) capmt->capmt_connected = 1; /* open connection to emulated ca0 device */ - capmt->capmt_sock_ca0 = tvh_socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - - struct sockaddr_in serv_addr; - serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - serv_addr.sin_port = htons( (unsigned short int)capmt->capmt_port); - serv_addr.sin_family = AF_INET; - - if (bind(capmt->capmt_sock_ca0, (const struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) - perror("[CapmtServer] ERROR binding to ca0"); - else + if (!capmt->capmt_oscam) { + bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[0], capmt->capmt_port); + } else { + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + if (tda->tda_rootpath) { //if rootpath is NULL then can't rely on tda_adapter_num because it is always 0 + if (tda->tda_adapter_num > MAX_CA) { + tvhlog(LOG_ERR, "capmt", "adapter number > MAX_CA"); + continue; + } + tvhlog(LOG_INFO, "capmt", "Creating capmt UDP socket for adapter %d", tda->tda_adapter_num); + bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[tda->tda_adapter_num], 9000 + tda->tda_adapter_num); + } + } + } + if (bind_ok) handle_ca0(capmt); } else tvhlog(LOG_ERR, "capmt", "Error connecting to %s: %s", capmt->capmt_sockfile, strerror(errno)); @@ -375,8 +503,9 @@ capmt_thread(void *aux) /* close opened sockets */ if (capmt->capmt_sock > 0) close(capmt->capmt_sock); - if (capmt->capmt_sock_ca0 > 0) - close(capmt->capmt_sock_ca0); + for (i = 0; i < MAX_CA; i++) + if (capmt->capmt_sock_ca0[i] > 0) + close(capmt->capmt_sock_ca0[i]); /* schedule reconnection */ if(subscriptions_active()) { @@ -408,144 +537,192 @@ capmt_table_input(struct th_descrambler *td, struct service *t, capmt_service_t *ct = (capmt_service_t *)td; capmt_t *capmt = ct->ct_capmt; int adapter_num = t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num; + int total_caids = 0, current_caid = 0; caid_t *c; - c = LIST_FIRST(&st->es_caids); - if(c == NULL) - return; + LIST_FOREACH(c, &st->es_caids, link) { + total_caids++; + } - if(len > 4096) - return; + LIST_FOREACH(c, &st->es_caids, link) { + current_caid++; - switch(data[0]) { - case 0x80: - case 0x81: - { - /* ECM */ - if (ct->ct_caid_last == -1) - ct->ct_caid_last = c->caid; - - uint16_t caid = c->caid; - /* search ecmpid in list */ - capmt_caid_ecm_t *cce, *cce2; - LIST_FOREACH(cce, &ct->ct_caid_ecm, cce_link) - if (cce->cce_caid == caid) - break; + if(c == NULL) + return; - if (!cce) + if(len > 4096) + return; + + switch(data[0]) { + case 0x80: + case 0x81: { - tvhlog(LOG_DEBUG, "capmt", - "New caid 0x%04X for service \"%s\"", c->caid, t->s_svcname); + /* ECM */ + if (ct->ct_caid_last == -1) + ct->ct_caid_last = c->caid; - /* ecmpid not already seen, add it to list */ - cce = calloc(1, sizeof(capmt_caid_ecm_t)); - cce->cce_caid = c->caid; - cce->cce_ecmpid = st->es_pid; - LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link); - } + uint16_t caid = c->caid; + /* search ecmpid in list */ + capmt_caid_ecm_t *cce, *cce2; + LIST_FOREACH(cce, &ct->ct_caid_ecm, cce_link) + if (cce->cce_caid == caid) + break; - if ((cce->cce_ecmsize == len) && !memcmp(cce->cce_ecm, data, len)) - break; /* key already sent */ + if (!cce) + { + tvhlog(LOG_DEBUG, "capmt", + "New caid 0x%04X for service \"%s\"", c->caid, t->s_svcname); - if(capmt->capmt_sock == -1) { - /* New key, but we are not connected (anymore), can not descramble */ - ct->ct_keystate = CT_UNKNOWN; + /* ecmpid not already seen, add it to list */ + cce = calloc(1, sizeof(capmt_caid_ecm_t)); + cce->cce_caid = c->caid; + cce->cce_ecmpid = st->es_pid; + cce->cce_providerid = c->providerid; + LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link); + } + + if ((cce->cce_ecmsize == len) && !memcmp(cce->cce_ecm, data, len)) + break; /* key already sent */ + + if(capmt->capmt_sock == -1) { + /* New key, but we are not connected (anymore), can not descramble */ + ct->ct_keystate = CT_UNKNOWN; + break; + } + + uint16_t sid = t->s_dvb_service_id; + uint16_t ecmpid = st->es_pid; + uint16_t transponder = 0; + + /* don't do too much requests */ + if (current_caid == total_caids && caid != ct->ct_caid_last) + return; + + static uint8_t pmtversion = 1; + + /* buffer for capmt */ + int pos = 0; + uint8_t buf[4094]; + + capmt_header_t head = { + .capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 }, + .capmt_list_management = CAPMT_LIST_ONLY, + .program_number = sid, + .version_number = 0, + .current_next_indicator = 0, + .program_info_length = 0, + .capmt_cmd_id = CAPMT_CMD_OK_DESCRAMBLING, + }; + memcpy(&buf[pos], &head, sizeof(head)); + pos += sizeof(head); + + if (capmt->capmt_oscam) + { + capmt_descriptor_t dmd = { + .cad_type = CAPMT_DESC_DEMUX, + .cad_length = 0x02, + .cad_data = { + 0, adapter_num }}; + memcpy(&buf[pos], &dmd, dmd.cad_length + 2); + pos += dmd.cad_length + 2; + } + + capmt_descriptor_t prd = { + .cad_type = CAPMT_DESC_PRIVATE, + .cad_length = 0x08, + .cad_data = { 0x00, 0x00, 0x00, 0x00, + sid >> 8, sid & 0xFF, + transponder >> 8, transponder & 0xFF + }}; + memcpy(&buf[pos], &prd, prd.cad_length + 2); + pos += prd.cad_length + 2; + + if (!capmt->capmt_oscam) + { + capmt_descriptor_t dmd = { + .cad_type = CAPMT_DESC_DEMUX, + .cad_length = 0x02, + .cad_data = { + 1 << adapter_num, adapter_num }}; + memcpy(&buf[pos], &dmd, dmd.cad_length + 2); + pos += dmd.cad_length + 2; + } + + capmt_descriptor_t ecd = { + .cad_type = CAPMT_DESC_PID, + .cad_length = 0x02, + .cad_data = { + ecmpid >> 8, ecmpid & 0xFF }}; + memcpy(&buf[pos], &ecd, ecd.cad_length + 2); + pos += ecd.cad_length + 2; + + LIST_FOREACH(cce2, &ct->ct_caid_ecm, cce_link) { + capmt_descriptor_t cad = { + .cad_type = 0x09, + .cad_length = 0x04, + .cad_data = { + cce2->cce_caid >> 8, cce2->cce_caid & 0xFF, + cce2->cce_ecmpid >> 8 | 0xE0, cce2->cce_ecmpid & 0xFF}}; + if (cce2->cce_providerid) { //we need to add provider ID to the data + if (cce2->cce_caid >> 8 == 0x01) { + cad.cad_length = 0x11; + cad.cad_data[4] = cce2->cce_providerid >> 8; + cad.cad_data[5] = cce2->cce_providerid & 0xffffff; + } else if (cce2->cce_caid >> 8 == 0x05) { + cad.cad_length = 0x0f; + cad.cad_data[10] = 0x14; + cad.cad_data[11] = cce2->cce_providerid >> 24; + cad.cad_data[12] = cce2->cce_providerid >> 16; + cad.cad_data[13] = cce2->cce_providerid >> 8; + cad.cad_data[14] = cce2->cce_providerid & 0xffffff; + } else if (cce2->cce_caid >> 8 == 0x18) { + cad.cad_length = 0x07; + cad.cad_data[5] = cce2->cce_providerid >> 8; + cad.cad_data[6] = cce2->cce_providerid & 0xffffff; + } else if (cce2->cce_caid >> 8 == 0x4a) { + cad.cad_length = 0x05; + cad.cad_data[4] = cce2->cce_providerid & 0xffffff; + } else + tvhlog(LOG_WARNING, "capmt", "Unknown CAID type, don't know where to put provider ID"); + } + memcpy(&buf[pos], &cad, cad.cad_length + 2); + pos += cad.cad_length + 2; + tvhlog(LOG_DEBUG, "capmt", "adding ECMPID=0x%X (%d), CAID=0x%X (%d) PROVID=0x%X (%d)", + cce2->cce_ecmpid, cce2->cce_ecmpid, + cce2->cce_caid, cce2->cce_caid, + cce2->cce_providerid, cce2->cce_providerid); + } + + uint8_t end[] = { + 0x01, (ct->ct_seq >> 8) & 0xFF, ct->ct_seq & 0xFF, 0x00, 0x06 }; + memcpy(&buf[pos], end, sizeof(end)); + pos += sizeof(end); + buf[10] = ((pos - 5 - 12) & 0xF00) >> 8; + buf[11] = ((pos - 5 - 12) & 0xFF); + buf[4] = ((pos - 6) >> 8); + buf[5] = ((pos - 6) & 0xFF); + + buf[7] = sid >> 8; + buf[8] = sid & 0xFF; + + memcpy(cce->cce_ecm, data, len); + cce->cce_ecmsize = len; + + if(ct->ct_keystate != CT_RESOLVED) + tvhlog(LOG_INFO, "capmt", + "Trying to obtain key for service \"%s\"",t->s_svcname); + + buf[9] = pmtversion; + pmtversion = (pmtversion + 1) & 0x1F; + + capmt_send_msg(capmt, buf, pos); break; } - - uint16_t sid = t->s_dvb_service_id; - uint16_t ecmpid = st->es_pid; - uint16_t transponder = 0; - - /* don't do too much requests */ - if (caid != ct->ct_caid_last) - return; - - static uint8_t pmtversion = 1; - - /* buffer for capmt */ - int pos = 0; - uint8_t buf[4094]; - - capmt_header_t head = { - .capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 }, - .capmt_list_management = CAPMT_LIST_ONLY, - .program_number = sid, - .version_number = 0, - .current_next_indicator = 0, - .program_info_length = 0, - .capmt_cmd_id = CAPMT_CMD_OK_DESCRAMBLING, - }; - memcpy(&buf[pos], &head, sizeof(head)); - pos += sizeof(head); - - capmt_descriptor_t prd = { - .cad_type = CAPMT_DESC_PRIVATE, - .cad_length = 0x08, - .cad_data = { 0x00, 0x00, 0x00, 0x00, - sid >> 8, sid & 0xFF, - transponder >> 8, transponder & 0xFF - }}; - memcpy(&buf[pos], &prd, prd.cad_length + 2); - pos += prd.cad_length + 2; - - capmt_descriptor_t dmd = { - .cad_type = CAPMT_DESC_DEMUX, - .cad_length = 0x02, - .cad_data = { - 1 << adapter_num, adapter_num }}; - memcpy(&buf[pos], &dmd, dmd.cad_length + 2); - pos += dmd.cad_length + 2; - - capmt_descriptor_t ecd = { - .cad_type = CAPMT_DESC_PID, - .cad_length = 0x02, - .cad_data = { - ecmpid >> 8, ecmpid & 0xFF }}; - memcpy(&buf[pos], &ecd, ecd.cad_length + 2); - pos += ecd.cad_length + 2; - - LIST_FOREACH(cce2, &ct->ct_caid_ecm, cce_link) { - capmt_descriptor_t cad = { - .cad_type = 0x09, - .cad_length = 0x04, - .cad_data = { - cce2->cce_caid >> 8, cce2->cce_caid & 0xFF, - cce2->cce_ecmpid >> 8 | 0xE0, cce2->cce_ecmpid & 0xFF}}; - memcpy(&buf[pos], &cad, cad.cad_length + 2); - pos += cad.cad_length + 2; - } - - uint8_t end[] = { - 0x01, (ct->ct_seq >> 8) & 0xFF, ct->ct_seq & 0xFF, 0x00, 0x06 }; - memcpy(&buf[pos], end, sizeof(end)); - pos += sizeof(end); - buf[10] = ((pos - 5 - 12) & 0xF00) >> 8; - buf[11] = ((pos - 5 - 12) & 0xFF); - buf[4] = ((pos - 6) >> 8); - buf[5] = ((pos - 6) & 0xFF); - - - buf[7] = sid >> 8; - buf[8] = sid & 0xFF; - - memcpy(cce->cce_ecm, data, len); - cce->cce_ecmsize = len; - - if(ct->ct_keystate != CT_RESOLVED) - tvhlog(LOG_INFO, "capmt", - "Trying to obtain key for service \"%s\"",t->s_svcname); - - buf[9] = pmtversion; - pmtversion = (pmtversion + 1) & 0x1F; - - capmt_send_msg(capmt, buf, pos); + default: + /* EMM */ break; - } - default: - /* EMM */ - break; + } } } @@ -623,21 +800,24 @@ capmt_service_start(service_t *t) ct->ct_seq = capmt->capmt_seq++; TAILQ_FOREACH(st, &t->s_components, es_link) { - caid_t *c = LIST_FIRST(&st->es_caids); - if(c == NULL) - continue; + caid_t *c; + LIST_FOREACH(c, &st->es_caids, link) { + if(c == NULL) + continue; - tvhlog(LOG_DEBUG, "capmt", - "New caid 0x%04X for service \"%s\"", c->caid, t->s_svcname); + tvhlog(LOG_DEBUG, "capmt", + "New caid 0x%04X for service \"%s\"", c->caid, t->s_svcname); - /* add it to list */ - cce = calloc(1, sizeof(capmt_caid_ecm_t)); - cce->cce_caid = c->caid; - cce->cce_ecmpid = st->es_pid; - LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link); + /* add it to list */ + cce = calloc(1, sizeof(capmt_caid_ecm_t)); + cce->cce_caid = c->caid; + cce->cce_ecmpid = st->es_pid; + cce->cce_providerid = c->providerid; + LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link); - /* sending request will be based on first seen caid */ - ct->ct_caid_last = -1; + /* sending request will be based on first seen caid */ + ct->ct_caid_last = -1; + } } ct->ct_keys = get_key_struct(); @@ -726,6 +906,7 @@ capmt_record_build(capmt_t *capmt) htsmsg_add_str(e, "camdfilename", capmt->capmt_sockfile ?: ""); htsmsg_add_u32(e, "port", capmt->capmt_port); + htsmsg_add_u32(e, "oscam", !!capmt->capmt_oscam); htsmsg_add_str(e, "comment", capmt->capmt_comment ?: ""); return e; @@ -753,7 +934,10 @@ capmt_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate if(!htsmsg_get_u32(values, "port", &u32)) capmt->capmt_port = u32; - + + if(!htsmsg_get_u32(values, "oscam", &u32)) + capmt->capmt_oscam = u32; + if((s = htsmsg_get_str(values, "comment")) != NULL) { free(capmt->capmt_comment); capmt->capmt_comment = strdup(s); diff --git a/src/webui/static/app/capmteditor.js b/src/webui/static/app/capmteditor.js index 9503c599..6f0c73ea 100644 --- a/src/webui/static/app/capmteditor.js +++ b/src/webui/static/app/capmteditor.js @@ -7,6 +7,12 @@ tvheadend.capmteditor = function() { width : 60 }); + var oscamColumn = new Ext.grid.CheckColumn({ + header : "OSCam mode", + dataIndex : 'oscam', + width : 60 + }); + function setMetaAttr(meta, record) { var enabled = record.get('enabled'); if (!enabled) return; @@ -43,7 +49,7 @@ tvheadend.capmteditor = function() { editor : new fm.TextField({ allowBlank : false }) - }, { + }, oscamColumn, { header : "Comment", dataIndex : 'comment', width : 400, @@ -55,7 +61,7 @@ tvheadend.capmteditor = function() { } ]}); var rec = Ext.data.Record.create([ 'enabled', 'connected', 'camdfilename', - 'port', 'comment' ]); + 'port', 'oscam', 'comment' ]); store = new Ext.data.JsonStore({ root : 'entries', @@ -77,5 +83,5 @@ tvheadend.capmteditor = function() { }); return new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec, - [ enabledColumn ], store, 'config_capmt.html', 'key'); + [ enabledColumn, oscamColumn ], store, 'config_capmt.html', 'key'); } From ae4f5f44ff6608cf354c7d5cd83d9b9e6aacfa37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 24 Oct 2012 14:21:10 +0200 Subject: [PATCH 034/503] added source type (MPEGTS etc) to source_info. --- src/dvb/dvb_transport.c | 2 ++ src/iptv_input.c | 1 + src/service.h | 1 + src/tvheadend.h | 1 + src/v4l.c | 1 + 5 files changed, 6 insertions(+) diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c index 8552780d..2b840c85 100644 --- a/src/dvb/dvb_transport.c +++ b/src/dvb/dvb_transport.c @@ -355,6 +355,8 @@ dvb_transport_setsourceinfo(service_t *t, struct source_info *si) lock_assert(&global_lock); + si->si_type = S_MPEG_TS; + if(tdmi->tdmi_adapter->tda_rootpath != NULL) si->si_device = strdup(tdmi->tdmi_adapter->tda_rootpath); diff --git a/src/iptv_input.c b/src/iptv_input.c index 2f65398e..314e5b59 100644 --- a/src/iptv_input.c +++ b/src/iptv_input.c @@ -471,6 +471,7 @@ iptv_service_setsourceinfo(service_t *t, struct source_info *si) char straddr[INET6_ADDRSTRLEN]; memset(si, 0, sizeof(struct source_info)); + si->si_type = S_MPEG_TS; si->si_adapter = t->s_iptv_iface ? strdup(t->s_iptv_iface) : NULL; if(t->s_iptv_group.s_addr != 0) { si->si_mux = strdup(inet_ntoa(t->s_iptv_group)); diff --git a/src/service.h b/src/service.h index ad357226..bf10d74e 100644 --- a/src/service.h +++ b/src/service.h @@ -226,6 +226,7 @@ typedef struct service { */ enum { S_MPEG_TS, + S_MPEG_PS, S_OTHER, } s_source_type; diff --git a/src/tvheadend.h b/src/tvheadend.h index ae785555..c0790606 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -53,6 +53,7 @@ typedef struct source_info { char *si_mux; char *si_provider; char *si_service; + int si_type; } source_info_t; static inline void diff --git a/src/v4l.c b/src/v4l.c index 032ce060..a7b86075 100644 --- a/src/v4l.c +++ b/src/v4l.c @@ -324,6 +324,7 @@ v4l_service_setsourceinfo(service_t *t, struct source_info *si) char buf[64]; memset(si, 0, sizeof(struct source_info)); + si->si_type = S_MPEG_PS; si->si_adapter = strdup(t->s_v4l_adapter->va_displayname); snprintf(buf, sizeof(buf), "%d Hz", t->s_v4l_frequency); From deea444c8633b581a71a0895dd73adcb3ca6739e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 24 Oct 2012 14:22:00 +0200 Subject: [PATCH 035/503] added pmt pid to streaming_start --- src/service.c | 1 + src/streaming.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/service.c b/src/service.c index c338a98f..1f8fe565 100644 --- a/src/service.c +++ b/src/service.c @@ -908,6 +908,7 @@ service_build_stream_start(service_t *t) ss->ss_refcount = 1; ss->ss_pcr_pid = t->s_pcr_pid; + ss->ss_pmt_pid = t->s_pmt_pid; return ss; } diff --git a/src/streaming.h b/src/streaming.h index 5dcda7db..75dc7857 100644 --- a/src/streaming.h +++ b/src/streaming.h @@ -54,6 +54,7 @@ typedef struct streaming_start { source_info_t ss_si; uint16_t ss_pcr_pid; + uint16_t ss_pmt_pid; streaming_start_component_t ss_components[0]; From dcfaad1a41a88acdb6a980a7f62acf02f3b10c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 24 Oct 2012 15:25:43 +0200 Subject: [PATCH 036/503] send the packet type to muxer along with the actual packet so we can check if the cast is correct --- src/dvr/dvr_rec.c | 2 +- src/muxer.c | 4 ++-- src/muxer.h | 6 ++++-- src/muxer_pass.c | 11 +++++++---- src/muxer_tvh.c | 6 +++++- src/webui/webui.c | 2 +- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index cf8319e0..ee0d906b 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -445,7 +445,7 @@ dvr_thread(void *aux) if(dispatch_clock > de->de_start - (60 * de->de_start_extra)) { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); - muxer_write_pkt(de->de_mux, sm->sm_data); + muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); sm->sm_data = NULL; } break; diff --git a/src/muxer.c b/src/muxer.c index cd3071fe..2afdf08a 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -308,12 +308,12 @@ muxer_write_meta(muxer_t *m, struct epg_broadcast *eb) * sanity wrapper arround m_write_pkt() */ int -muxer_write_pkt(muxer_t *m, void *data) +muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) { if(!m || !data) return -1; - return m->m_write_pkt(m, data); + return m->m_write_pkt(m, smt, data); } diff --git a/src/muxer.h b/src/muxer.h index 6fdeb918..f6bf2116 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -47,7 +47,9 @@ typedef struct muxer { int (*m_close) (struct muxer *); // Close the muxer void (*m_destroy) (struct muxer *); // Free the memory int (*m_write_meta) (struct muxer *, struct epg_broadcast *); // Append epg data - int (*m_write_pkt) (struct muxer *, void *); // Append a media packet + int (*m_write_pkt) (struct muxer *, // Append a media packet + streaming_message_type_t, + void *); int m_errors; // Number of errors muxer_container_type_t m_container; // The type of the container @@ -71,7 +73,7 @@ int muxer_reconfigure (muxer_t *m, const struct streaming_start *ss); int muxer_close (muxer_t *m); int muxer_destroy (muxer_t *m); int muxer_write_meta (muxer_t *m, struct epg_broadcast *eb); -int muxer_write_pkt (muxer_t *m, void *data); +int muxer_write_pkt (muxer_t *m, streaming_message_type_t smt, void *data); const char* muxer_mime (muxer_t *m, const struct streaming_start *ss); const char* muxer_suffix (muxer_t *m, const struct streaming_start *ss); diff --git a/src/muxer_pass.c b/src/muxer_pass.c index b61aa400..2a1804e3 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "tvheadend.h" #include "streaming.h" @@ -227,17 +228,19 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) * Write a packet directly to the file descriptor */ static int -pass_muxer_write_pkt(muxer_t *m, void *data) +pass_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) { pktbuf_t *pb = (pktbuf_t*)data; pass_muxer_t *pm = (pass_muxer_t*)m; - switch(pm->m_container) { - case MC_MPEGTS: + assert(smt == SMT_MPEGTS); + + switch(smt) { + case SMT_MPEGTS: pass_muxer_write_ts(m, pb); break; default: - //NOP + //TODO: add support for v4l (MPEG-PS) break; } diff --git a/src/muxer_tvh.c b/src/muxer_tvh.c index 6d5a0de1..dd335145 100644 --- a/src/muxer_tvh.c +++ b/src/muxer_tvh.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include + #include "tvheadend.h" #include "streaming.h" #include "epg.h" @@ -132,11 +134,13 @@ tvh_muxer_open_file(muxer_t *m, const char *filename) * Write a packet to the muxer */ static int -tvh_muxer_write_pkt(muxer_t *m, void *data) +tvh_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) { th_pkt_t *pkt = (th_pkt_t*)data; tvh_muxer_t *tm = (tvh_muxer_t*)m; + assert(smt == SMT_PACKET); + if(mk_mux_write_pkt(tm->tm_ref, pkt)) { tm->m_errors++; return -1; diff --git a/src/webui/webui.c b/src/webui/webui.c index e404d858..e00b517f 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -199,7 +199,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: - muxer_write_pkt(mux, sm->sm_data); + muxer_write_pkt(mux, sm->sm_type, sm->sm_data); sm->sm_data = NULL; break; From 0cbaa02441a1ce15cf502515d556105342e09114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 24 Oct 2012 15:32:06 +0200 Subject: [PATCH 037/503] decoupled the muxer from the service link. --- src/dvr/dvr_rec.c | 2 +- src/muxer.c | 4 ++-- src/muxer.h | 2 +- src/muxer_pass.c | 38 ++++++++++++++++++++------------------ src/muxer_pass.h | 2 +- src/webui/webui.c | 2 +- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index ee0d906b..ffe43011 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -327,7 +327,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) int i; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); - de->de_mux = muxer_create(de->de_s->ths_service, de->de_mc); + de->de_mux = muxer_create(de->de_mc); if(!de->de_mux) { dvr_rec_fatal_error(de, "Unable to create muxer"); return; diff --git a/src/muxer.c b/src/muxer.c index 2afdf08a..763219f4 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -163,11 +163,11 @@ muxer_container_txt2type(const char *str) * Create a new muxer */ muxer_t* -muxer_create(service_t *s, muxer_container_type_t mc) +muxer_create(muxer_container_type_t mc) { muxer_t *m; - m = pass_muxer_create(s, mc); + m = pass_muxer_create(mc); if(!m) m = tvh_muxer_create(mc); diff --git a/src/muxer.h b/src/muxer.h index f6bf2116..77767f3e 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -63,7 +63,7 @@ const char* muxer_container_mimetype(muxer_container_type_t mc, int v const char* muxer_container_suffix (muxer_container_type_t mc, int video); // Muxer factory -muxer_t *muxer_create(struct service *s, muxer_container_type_t mc); +muxer_t *muxer_create(muxer_container_type_t mc); // Wrapper functions int muxer_open_file (muxer_t *m, const char *filename); diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 2a1804e3..73053438 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -51,7 +51,6 @@ typedef struct pass_muxer { /* TS muxing */ uint8_t *pm_pat; uint8_t *pm_pmt; - uint16_t pm_pmt_pid; uint32_t pm_ic; // Injection counter uint32_t pm_pc; // Packet counter } pass_muxer_t; @@ -66,8 +65,10 @@ pass_muxer_mime(muxer_t* m, const struct streaming_start *ss) int i; int has_audio; int has_video; + muxer_container_type_t mc; const streaming_start_component_t *ssc; - + const source_info_t *si = &ss->ss_si; + has_audio = 0; has_video = 0; @@ -81,10 +82,17 @@ pass_muxer_mime(muxer_t* m, const struct streaming_start *ss) has_audio |= SCT_ISAUDIO(ssc->ssc_type); } + if(si->si_type == S_MPEG_TS) + mc = MC_MPEGTS; + else if(si->si_type == S_MPEG_PS) + mc = MC_MPEGPS; + else + mc = MC_UNKNOWN; + if(has_video) - return muxer_container_mimetype(m->m_container, 1); + return muxer_container_mimetype(mc, 1); else if(has_audio) - return muxer_container_mimetype(m->m_container, 0); + return muxer_container_mimetype(mc, 0); else return muxer_container_mimetype(MC_UNKNOWN, 0); } @@ -97,16 +105,16 @@ static int pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) { pass_muxer_t *pm = (pass_muxer_t*)m; + const source_info_t *si = &ss->ss_si; - if(pm->m_container == MC_MPEGTS) { - + if(si->si_type == S_MPEG_TS) { memset(pm->pm_pat, 0xff, 188); pm->pm_pat[0] = 0x47; pm->pm_pat[1] = 0x40; pm->pm_pat[2] = 0x00; pm->pm_pat[3] = 0x10; pm->pm_pat[4] = 0x00; - if(psi_build_pat(NULL, pm->pm_pat+5, 183, pm->pm_pmt_pid) < 0) { + if(psi_build_pat(NULL, pm->pm_pat+5, 183, ss->ss_pmt_pid) < 0) { pm->m_errors++; tvhlog(LOG_ERR, "pass", "%s: Unable to build pat", pm->pm_filename); return -1; @@ -114,8 +122,8 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) memset(pm->pm_pmt, 0xff, 188); pm->pm_pmt[0] = 0x47; - pm->pm_pmt[1] = 0x40 | (pm->pm_pmt_pid >> 8); - pm->pm_pmt[2] = 0x00 | (pm->pm_pmt_pid >> 0); + pm->pm_pmt[1] = 0x40 | (ss->ss_pmt_pid >> 8); + pm->pm_pmt[2] = 0x00 | (ss->ss_pmt_pid >> 0); pm->pm_pmt[3] = 0x10; pm->pm_pmt[4] = 0x00; if(psi_build_pmt(ss, pm->pm_pmt+5, 183, ss->ss_pcr_pid) < 0) { @@ -305,7 +313,7 @@ pass_muxer_destroy(muxer_t *m) * Create a new passthrough muxer */ muxer_t* -pass_muxer_create(service_t *s, muxer_container_type_t mc) +pass_muxer_create(muxer_container_type_t mc) { pass_muxer_t *pm; @@ -323,14 +331,8 @@ pass_muxer_create(service_t *s, muxer_container_type_t mc) pm->m_close = pass_muxer_close; pm->m_destroy = pass_muxer_destroy; - if(s->s_type == SERVICE_TYPE_V4L) { - pm->m_container = MC_MPEGPS; - } else { - pm->m_container = MC_MPEGTS; - pm->pm_pmt_pid = s->s_pmt_pid; - pm->pm_pat = malloc(188); - pm->pm_pmt = malloc(188); - } + pm->pm_pat = malloc(188); + pm->pm_pmt = malloc(188); return (muxer_t *)pm; } diff --git a/src/muxer_pass.h b/src/muxer_pass.h index b3dc9c88..919c2104 100644 --- a/src/muxer_pass.h +++ b/src/muxer_pass.h @@ -21,6 +21,6 @@ #include "muxer.h" -muxer_t* pass_muxer_create(struct service *s, muxer_container_type_t mc); +muxer_t* pass_muxer_create(muxer_container_type_t mc); #endif diff --git a/src/webui/webui.c b/src/webui/webui.c index e00b517f..a4c084b3 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -153,7 +153,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, socklen_t errlen = sizeof(err); const char *name; - mux = muxer_create(s->ths_service, mc); + mux = muxer_create(mc); if(muxer_open_stream(mux, hc->hc_fd)) run = 0; From 83a631e6d85833d35f3d7c1b358ac5b252a2257e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 24 Oct 2012 15:44:42 +0200 Subject: [PATCH 038/503] try to handle reconfiguration fo sources properly --- src/dvr/dvr_rec.c | 35 +++++++++++++++++++++-------------- src/webui/webui.c | 17 ++++++++++------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index ffe43011..ba5c8cb9 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -319,7 +319,7 @@ dvr_rec_set_state(dvr_entry_t *de, dvr_rs_state_t newstate, int error) /** * */ -static void +static int dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) { const source_info_t *si = &ss->ss_si; @@ -330,28 +330,28 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) de->de_mux = muxer_create(de->de_mc); if(!de->de_mux) { dvr_rec_fatal_error(de, "Unable to create muxer"); - return; + return -1; } if(pvr_generate_filename(de, ss) != 0) { dvr_rec_fatal_error(de, "Unable to create directories"); - return; + return -1; } if(muxer_open_file(de->de_mux, de->de_filename)) { dvr_rec_fatal_error(de, "Unable to open file"); - return; + return -1; } if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) { dvr_rec_fatal_error(de, "Unable to init file"); - return; + return -1; } if(cfg->dvr_flags & DVR_TAG_FILES) { if(muxer_write_meta(de->de_mux, de->de_bcast)) { dvr_rec_fatal_error(de, "Unable to write meta data"); - return; + return -1; } } @@ -412,6 +412,8 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) ch, ssc->ssc_disabled ? "" : ""); } + + return 0; } @@ -425,6 +427,7 @@ dvr_thread(void *aux) streaming_queue_t *sq = &de->de_sq; streaming_message_t *sm; int run = 1; + int started = 0; pthread_mutex_lock(&sq->sq_mutex); @@ -451,10 +454,17 @@ dvr_thread(void *aux) break; case SMT_START: - pthread_mutex_lock(&global_lock); - dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0); - dvr_rec_start(de, sm->sm_data); - pthread_mutex_unlock(&global_lock); + if(!started) { + pthread_mutex_lock(&global_lock); + dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0); + if(dvr_rec_start(de, sm->sm_data) == 0) + started = 1; + pthread_mutex_unlock(&global_lock); + } else if(muxer_reconfigure(de->de_mux, sm->sm_data) < 0) { + tvhlog(LOG_WARNING, + "dvr", "Unable to reconfigure the recording \"%s\"", + de->de_filename ?: lang_str_get(de->de_title, NULL)); + } break; case SMT_STOP: @@ -468,10 +478,7 @@ dvr_thread(void *aux) "dvr", "Recording completed: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL)); - } else if(sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) { - muxer_reconfigure(de->de_mux, sm->sm_data); - } else { - + } else if(sm->sm_code != SM_CODE_SOURCE_RECONFIGURED) { if(de->de_last_error != sm->sm_code) { dvr_rec_set_state(de, DVR_RS_ERROR, sm->sm_code); diff --git a/src/webui/webui.c b/src/webui/webui.c index a4c084b3..85936146 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -204,18 +204,21 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, break; case SMT_START: - tvhlog(LOG_DEBUG, "webui", "Start streaming %s", hc->hc_url_orig); + if(!started) { + tvhlog(LOG_DEBUG, "webui", "Start streaming %s", hc->hc_url_orig); + http_output_content(hc, muxer_mime(mux, sm->sm_data)); - http_output_content(hc, muxer_mime(mux, sm->sm_data)); - muxer_init(mux, sm->sm_data, name); + if(muxer_init(mux, sm->sm_data, name) < 0) + run = 0; - started = 1; + started = 1; + } else if(muxer_reconfigure(mux, sm->sm_data) < 0) { + tvhlog(LOG_WARNING, "webui", "Unable to reconfigure stream %s", hc->hc_url_orig); + } break; case SMT_STOP: - if(sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) { - muxer_reconfigure(mux, sm->sm_data); - } else { + if(sm->sm_code != SM_CODE_SOURCE_RECONFIGURED) { tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, streaming_code2txt(sm->sm_code)); run = 0; From e049f5dae1cd7d60f9283499aba307da5c03d9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 25 Oct 2012 12:39:36 +0200 Subject: [PATCH 039/503] show user-agent, username and ip in the subscription status for HTTP subscriptions --- src/subscriptions.c | 9 ++++++--- src/webui/webui.c | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/subscriptions.c b/src/subscriptions.c index ca5b1675..1bd429db 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -550,13 +550,16 @@ subscription_create_msg(th_subscription_t *s) htsmsg_add_str(m, "state", state); - if (s->ths_hostname && s->ths_username && s->ths_client) { + if(s->ths_hostname != NULL) htsmsg_add_str(m, "hostname", s->ths_hostname); + + if(s->ths_username != NULL) htsmsg_add_str(m, "username", s->ths_username); + + if(s->ths_client != NULL) htsmsg_add_str(m, "title", s->ths_client); - } else { + else if(s->ths_title != NULL) htsmsg_add_str(m, "title", s->ths_title); - } if(s->ths_channel != NULL) htsmsg_add_str(m, "channel", s->ths_channel->ch_name); diff --git a/src/webui/webui.c b/src/webui/webui.c index 85936146..26ac8c63 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -640,7 +641,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) pthread_mutex_lock(&global_lock); s = subscription_create_from_channel(ch, priority, "HTTP", st, flags, - NULL, NULL, NULL); + inet_ntoa(hc->hc_peer->sin_addr), + hc->hc_username, + http_arg_get(&hc->hc_args, "User-Agent")); pthread_mutex_unlock(&global_lock); if(s) { From bc323d1932dafed02ff00f387e6b3553f711f5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 25 Oct 2012 12:40:28 +0200 Subject: [PATCH 040/503] some cosmetics to the "active subscription" list when DVR is being used. --- src/dvr/dvr_rec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index ba5c8cb9..e0ba9b35 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -87,9 +87,7 @@ dvr_rec_subscribe(dvr_entry_t *de) de->de_s = subscription_create_from_channel(de->de_channel, weight, buf, st, flags, - NULL, "DVR", - lang_str_get(de->de_title, - NULL)); + NULL, NULL, NULL); pthread_create(&de->de_thread, NULL, dvr_thread, de); } From d49422a89912263f844a21197341a5dca5d04259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 09:54:50 +0200 Subject: [PATCH 041/503] tsfix: Mask timestamps with PTS_MASK When debugging the tsfix code a developer can now narrow PTS_MASK to force wrap to happen much more frequently --- src/plumbing/tsfix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index d04e610d..dfab483c 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -153,6 +153,9 @@ normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) return; } + pkt->pkt_dts &= PTS_MASK; + pkt->pkt_pts &= PTS_MASK; + /* Subtract the transport wide start offset */ dts = pkt->pkt_dts - tf->tf_tsref; @@ -315,7 +318,7 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) if(tf->tf_tsref == PTS_UNSET && (!tf->tf_hasvideo || (SCT_ISVIDEO(tfs->tfs_type) && pkt->pkt_frametype == PKT_I_FRAME))) { - tf->tf_tsref = pkt->pkt_dts; + tf->tf_tsref = pkt->pkt_dts & PTS_MASK; tsfixprintf("reference clock set to %"PRId64"\n", tf->tf_tsref); } From 8a86b6fa8a260822c000b4afd1c04ac979fdd133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 10:00:09 +0200 Subject: [PATCH 042/503] Rename dvb_transport_ -> dvb_service_ for better consistency --- src/dvb/dvb.h | 14 +++++----- src/dvb/dvb_multiplex.c | 8 +++--- src/dvb/dvb_tables.c | 8 +++--- src/dvb/dvb_transport.c | 56 ++++++++++++++++++------------------- src/epggrab/module/eit.c | 2 +- src/epggrab/module/opentv.c | 2 +- src/webui/extjs_dvb.c | 2 +- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 571431b5..c5d93d0c 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -406,26 +406,26 @@ th_dvb_mux_instance_t *dvb_mux_find /** * DVB Transport (aka DVB service) */ -void dvb_transport_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier); +void dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier); -struct service *dvb_transport_find(th_dvb_mux_instance_t *tdmi, +struct service *dvb_service_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier); -struct service *dvb_transport_find2(th_dvb_mux_instance_t *tdmi, +struct service *dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier, int *save); -struct service *dvb_transport_find3 +struct service *dvb_service_find3 (th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid, int enabled, int epgprimary); -void dvb_transport_notify(struct service *t); +void dvb_service_notify(struct service *t); -void dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda); +void dvb_service_notify_by_adapter(th_dvb_adapter_t *tda); -htsmsg_t *dvb_transport_build_msg(struct service *t); +htsmsg_t *dvb_service_build_msg(struct service *t); /** * DVB Frontend diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index ac7e6c5d..46ea57ef 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -208,7 +208,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, /* HACK - load old transports and remove old mux config */ if(identifier) { save = 1; - dvb_transport_load(tdmi, identifier); + dvb_service_load(tdmi, identifier); hts_settings_remove("dvbmuxes/%s/%s", tda->tda_identifier, identifier); } @@ -291,7 +291,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, dvb_adapter_notify(tda); } - dvb_transport_load(tdmi, identifier); + dvb_service_load(tdmi, identifier); dvb_mux_notify(tdmi); if(enabled) { @@ -328,7 +328,7 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) service_destroy(t); } - dvb_transport_notify_by_adapter(tda); + dvb_service_notify_by_adapter(tda); if(tda->tda_mux_current == tdmi) dvb_fe_stop(tda->tda_mux_current, 0); @@ -1201,7 +1201,7 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, return -1; // Already exist LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, s_group_link) { - t_dst = dvb_transport_find(tdmi_dst, + t_dst = dvb_service_find(tdmi_dst, t_src->s_dvb_service_id, t_src->s_pmt_pid, NULL); diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 7f4c2ebb..f4aa6899 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -504,7 +504,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(dllen > len) break; - if (!(t = dvb_transport_find(tdmi, service_id, 0, NULL))) { + if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) { len -= dllen; ptr += dllen; continue; @@ -647,7 +647,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(service != 0 && pmt != 0) { int save = 0; - dvb_transport_find2(tdmi, service, pmt, NULL, &save); + dvb_service_find2(tdmi, service, pmt, NULL, &save); if (save || !tda->tda_disable_pmt_monitor) dvb_table_add_pmt(tdmi, pmt); } @@ -898,7 +898,7 @@ dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, chan = ((ptr[2] & 3) << 8) | ptr[3]; if(chan != 0) { - t = dvb_transport_find(tdmi, sid, 0, NULL); + t = dvb_service_find(tdmi, sid, 0, NULL); if(t != NULL) { if(t->s_channel_number != chan) { @@ -1074,7 +1074,7 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, continue; service_id = (ptr[24] << 8) | ptr[25]; - if((t = dvb_transport_find(tdmi, service_id, 0, NULL)) == NULL) + if((t = dvb_service_find(tdmi, service_id, 0, NULL)) == NULL) continue; atsc_stype = ptr[27] & 0x3f; diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c index 2b840c85..9ea91b0f 100644 --- a/src/dvb/dvb_transport.c +++ b/src/dvb/dvb_transport.c @@ -48,7 +48,7 @@ * */ static void -dvb_transport_open_demuxers(th_dvb_adapter_t *tda, service_t *t) +dvb_service_open_demuxers(th_dvb_adapter_t *tda, service_t *t) { struct dmx_pes_filter_params dmx_param; int fd; @@ -103,7 +103,7 @@ dvb_transport_open_demuxers(th_dvb_adapter_t *tda, service_t *t) * transports that is subscribing to the adapter */ static int -dvb_transport_start(service_t *t, unsigned int weight, int force_start) +dvb_service_start(service_t *t, unsigned int weight, int force_start) { int w, r; th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; @@ -140,7 +140,7 @@ dvb_transport_start(service_t *t, unsigned int weight, int force_start) pthread_mutex_unlock(&tda->tda_delivery_mutex); if(!r) - dvb_transport_open_demuxers(tda, t); + dvb_service_open_demuxers(tda, t); dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); @@ -152,7 +152,7 @@ dvb_transport_start(service_t *t, unsigned int weight, int force_start) * */ static void -dvb_transport_stop(service_t *t) +dvb_service_stop(service_t *t) { th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; elementary_stream_t *st; @@ -177,12 +177,12 @@ dvb_transport_stop(service_t *t) * */ static void -dvb_transport_refresh(service_t *t) +dvb_service_refresh(service_t *t) { th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; lock_assert(&global_lock); - dvb_transport_open_demuxers(tda, t); + dvb_service_open_demuxers(tda, t); } @@ -190,7 +190,7 @@ dvb_transport_refresh(service_t *t) * */ static void -dvb_transport_save(service_t *t) +dvb_service_save(service_t *t) { htsmsg_t *m = htsmsg_create_map(); @@ -230,7 +230,7 @@ dvb_transport_save(service_t *t) t->s_identifier); htsmsg_destroy(m); - dvb_transport_notify(t); + dvb_service_notify(t); } @@ -238,7 +238,7 @@ dvb_transport_save(service_t *t) * Load config for the given mux */ void -dvb_transport_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) +dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) { htsmsg_t *l, *c; htsmsg_field_t *f; @@ -269,7 +269,7 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) if(htsmsg_get_u32(c, "pmt", &pmt)) continue; - t = dvb_transport_find(tdmi, sid, pmt, f->hmf_name); + t = dvb_service_find(tdmi, sid, pmt, f->hmf_name); htsmsg_get_u32(c, "stype", &t->s_servicetype); if(htsmsg_get_u32(c, "scrambled", &u32)) @@ -311,7 +311,7 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) /* HACK - force save for old config */ if(old) - dvb_transport_save(t); + dvb_service_save(t); } /* HACK - remove old settings */ @@ -332,7 +332,7 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) * return that value */ static int -dvb_transport_quality(service_t *t) +dvb_service_quality(service_t *t) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; @@ -346,7 +346,7 @@ dvb_transport_quality(service_t *t) * Generate a descriptive name for the source */ static void -dvb_transport_setsourceinfo(service_t *t, struct source_info *si) +dvb_service_setsourceinfo(service_t *t, struct source_info *si) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; char buf[100]; @@ -389,7 +389,7 @@ dvb_grace_period(service_t *t) * Find transport based on the DVB identification */ service_t * -dvb_transport_find3 +dvb_service_find3 (th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid, int enabled, int epgprimary) @@ -407,13 +407,13 @@ dvb_transport_find3 if (onid && onid != tdmi->tdmi_network_id) continue; if (tsid && tsid != tdmi->tdmi_transport_stream_id) continue; if (netname && strcmp(netname, tdmi->tdmi_network ?: "")) continue; - if ((svc = dvb_transport_find3(tda, tdmi, NULL, 0, 0, sid, + if ((svc = dvb_service_find3(tda, tdmi, NULL, 0, 0, sid, enabled, epgprimary))) return svc; } } else { TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) - if ((svc = dvb_transport_find3(tda, NULL, netname, onid, tsid, + if ((svc = dvb_service_find3(tda, NULL, netname, onid, tsid, sid, enabled, epgprimary))) return svc; } @@ -427,14 +427,14 @@ dvb_transport_find3 * If it cannot be found we create it if 'pmt_pid' is also set */ service_t * -dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, +dvb_service_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier) { - return dvb_transport_find2(tdmi, sid, pmt_pid, identifier, NULL); + return dvb_service_find2(tdmi, sid, pmt_pid, identifier, NULL); } service_t * -dvb_transport_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, +dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier, int *save) { service_t *t; @@ -465,12 +465,12 @@ dvb_transport_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, t->s_dvb_service_id = sid; t->s_pmt_pid = pmt_pid; - t->s_start_feed = dvb_transport_start; - t->s_refresh_feed = dvb_transport_refresh; - t->s_stop_feed = dvb_transport_stop; - t->s_config_save = dvb_transport_save; - t->s_setsourceinfo = dvb_transport_setsourceinfo; - t->s_quality_index = dvb_transport_quality; + t->s_start_feed = dvb_service_start; + t->s_refresh_feed = dvb_service_refresh; + t->s_stop_feed = dvb_service_stop; + t->s_config_save = dvb_service_save; + t->s_setsourceinfo = dvb_service_setsourceinfo; + t->s_quality_index = dvb_service_quality; t->s_grace_period = dvb_grace_period; t->s_dvb_mux_instance = tdmi; @@ -489,7 +489,7 @@ dvb_transport_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, * */ htsmsg_t * -dvb_transport_build_msg(service_t *t) +dvb_service_build_msg(service_t *t) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; htsmsg_t *m = htsmsg_create_map(); @@ -529,7 +529,7 @@ dvb_transport_build_msg(service_t *t) * */ void -dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda) +dvb_service_notify_by_adapter(th_dvb_adapter_t *tda) { htsmsg_t *m = htsmsg_create_map(); htsmsg_add_str(m, "adapterId", tda->tda_identifier); @@ -541,7 +541,7 @@ dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda) * */ void -dvb_transport_notify(service_t *t) +dvb_service_notify(service_t *t) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; htsmsg_t *m = htsmsg_create_map(); diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 3da8da57..9e0add8a 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -730,7 +730,7 @@ static int _eit_callback if(!tdmi) goto done; /* Get service */ - svc = dvb_transport_find3(NULL, tdmi, NULL, 0, 0, sid, 1, 1); + svc = dvb_service_find3(NULL, tdmi, NULL, 0, 0, sid, 1, 1); if (!svc || !svc->s_ch) goto done; /* Register as interesting */ diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index e3d8c1c2..e66dd8b1 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -425,7 +425,7 @@ static void _opentv_parse_channels cnum = ((int)buf[i+5] << 8) | buf[i+6]; /* Find the service */ - svc = dvb_transport_find3(NULL, NULL, NULL, onid, tsid, sid, 1, 1); + svc = dvb_service_find3(NULL, NULL, NULL, onid, tsid, sid, 1, 1); if (svc && svc->s_ch) { ec =_opentv_find_epggrab_channel(mod, cid, 1, &save); ecl = LIST_FIRST(&ec->channels); diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 59760abf..6d4d2c04 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -436,7 +436,7 @@ extjs_dvbservices(http_connection_t *hc, const char *remain, void *opaque) qsort(tvec, count, sizeof(service_t *), transportcmp); for(i = 0; i < count; i++) - htsmsg_add_msg(array, NULL, dvb_transport_build_msg(tvec[i])); + htsmsg_add_msg(array, NULL, dvb_service_build_msg(tvec[i])); htsmsg_add_msg(out, "entries", array); From 847b8463b5655e543f6586c1e9f7b4e2285af8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 10:00:57 +0200 Subject: [PATCH 043/503] Rename dvb_transport.c -> dvb_service.c --- Makefile | 2 +- src/dvb/{dvb_transport.c => dvb_service.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/dvb/{dvb_transport.c => dvb_service.c} (100%) diff --git a/Makefile b/Makefile index f8a2a535..59b25065 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/dvb/diseqc.c \ src/dvb/dvb_adapter.c \ src/dvb/dvb_multiplex.c \ - src/dvb/dvb_transport.c \ + src/dvb/dvb_service.c \ src/dvb/dvb_preconf.c \ src/dvb/dvb_satconf.c \ src/webui/extjs_dvb.c \ diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_service.c similarity index 100% rename from src/dvb/dvb_transport.c rename to src/dvb/dvb_service.c From cd52d7c2169f85b7bbfface42bff8f451d8f9929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 10:50:22 +0200 Subject: [PATCH 044/503] Don't have code with side effects in assert() Including code with side effects in assert() is bad because it won't be executed if compiled with NDEBUG --- src/dvb/dvb_adapter.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index f58eb1b5..e4973be3 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -498,7 +498,9 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) /* Start DVR thread */ if (tda->tda_dvr_pipe[0] == -1) { - assert(pipe(tda->tda_dvr_pipe) != -1); + int err = pipe(tda->tda_dvr_pipe); + assert(err != -1); + fcntl(tda->tda_dvr_pipe[0], F_SETFD, fcntl(tda->tda_dvr_pipe[0], F_GETFD) | FD_CLOEXEC); fcntl(tda->tda_dvr_pipe[0], F_SETFL, fcntl(tda->tda_dvr_pipe[0], F_GETFL) | O_NONBLOCK); fcntl(tda->tda_dvr_pipe[1], F_SETFD, fcntl(tda->tda_dvr_pipe[1], F_GETFD) | FD_CLOEXEC); @@ -526,7 +528,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) /* Stop DVR thread */ if (tda->tda_dvr_pipe[0] != -1) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - assert(write(tda->tda_dvr_pipe[1], "", 1) == 1); + int err = write(tda->tda_dvr_pipe[1], "", 1); + assert(err != -1); pthread_join(tda->tda_dvr_thread, NULL); close(tda->tda_dvr_pipe[0]); close(tda->tda_dvr_pipe[1]); From 65a8fa5f282cca73489e43b516d30e0c178a55c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 10:53:22 +0200 Subject: [PATCH 045/503] dvb: Move out PID specific stuff from dvb_service.c --- Makefile | 1 + src/dvb/dvb.h | 8 +++ src/dvb/dvb_adapter.c | 2 + src/dvb/dvb_input_filtered.c | 115 +++++++++++++++++++++++++++++++++++ src/dvb/dvb_service.c | 61 ++----------------- 5 files changed, 130 insertions(+), 57 deletions(-) create mode 100644 src/dvb/dvb_input_filtered.c diff --git a/Makefile b/Makefile index 59b25065..b7953339 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/dvb/dvb_service.c \ src/dvb/dvb_preconf.c \ src/dvb/dvb_satconf.c \ + src/dvb/dvb_input_filtered.c \ src/webui/extjs_dvb.c \ # V4L diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index c5d93d0c..7a71bf9c 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -24,6 +24,8 @@ #include #include "htsmsg.h" +struct service; + #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) #define DVB_VER_ATLEAST(maj, min) \ @@ -231,6 +233,9 @@ typedef struct th_dvb_adapter { uint32_t tda_extrapriority; // extra priority for choosing the best adapter/service + void (*tda_open_service)(struct th_dvb_adapter *tda, struct service *s); + void (*tda_close_service)(struct th_dvb_adapter *tda, struct service *s); + } th_dvb_adapter_t; /** @@ -336,6 +341,9 @@ void dvb_adapter_set_extrapriority(th_dvb_adapter_t *tda, int extrapriority); void dvb_adapter_poweroff(th_dvb_adapter_t *tda); +void dvb_input_filtered_setup(th_dvb_adapter_t *tda); + + /** * DVB Multiplex */ diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index e4973be3..83e2d4d0 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -68,6 +68,8 @@ tda_alloc(void) tda->tda_allpids_dmx_fd = -1; tda->tda_dump_fd = -1; + dvb_input_filtered_setup(tda); + return tda; } diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c new file mode 100644 index 00000000..59d934f8 --- /dev/null +++ b/src/dvb/dvb_input_filtered.c @@ -0,0 +1,115 @@ +/* + * TV Input - Linux DVB interface + * Copyright (C) 2012 Andreas Öman + * + * 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 . + */ + +/** + * DVB input using hardware filters + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "dvb.h" +#include "service.h" + +/** + * Install filters for a service + * + * global_lock must be held + */ +static void +open_service(th_dvb_adapter_t *tda, service_t *s) +{ + struct dmx_pes_filter_params dmx_param; + int fd; + elementary_stream_t *st; + + TAILQ_FOREACH(st, &s->s_components, es_link) { + if(st->es_pid >= 0x2000) + continue; + + if(st->es_demuxer_fd != -1) + continue; + + fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); + st->es_cc_valid = 0; + + if(fd == -1) { + st->es_demuxer_fd = -1; + tvhlog(LOG_ERR, "dvb", + "\"%s\" unable to open demuxer \"%s\" for pid %d -- %s", + s->s_identifier, tda->tda_demux_path, + st->es_pid, strerror(errno)); + continue; + } + + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = st->es_pid; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { + tvhlog(LOG_ERR, "dvb", + "\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s", + s->s_identifier, tda->tda_demux_path, + st->es_pid, strerror(errno)); + close(fd); + fd = -1; + } + + st->es_demuxer_fd = fd; + } +} + + +/** + * Remove filters for a service + * + * global_lock must be held + */ +static void +close_service(th_dvb_adapter_t *tda, service_t *s) +{ + elementary_stream_t *es; + + TAILQ_FOREACH(es, &s->s_components, es_link) { + if(es->es_demuxer_fd != -1) { + close(es->es_demuxer_fd); + es->es_demuxer_fd = -1; + } + } +} + + + + + +void +dvb_input_filtered_setup(th_dvb_adapter_t *tda) +{ + tda->tda_open_service = open_service; + tda->tda_close_service = close_service; +} + diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 9ea91b0f..32877f8e 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -44,54 +44,6 @@ #include "dvb_support.h" #include "notify.h" -/** - * - */ -static void -dvb_service_open_demuxers(th_dvb_adapter_t *tda, service_t *t) -{ - struct dmx_pes_filter_params dmx_param; - int fd; - elementary_stream_t *st; - - TAILQ_FOREACH(st, &t->s_components, es_link) { - if(st->es_pid >= 0x2000) - continue; - - if(st->es_demuxer_fd != -1) - continue; - - fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - st->es_cc_valid = 0; - - if(fd == -1) { - st->es_demuxer_fd = -1; - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to open demuxer \"%s\" for pid %d -- %s", - t->s_identifier, tda->tda_demux_path, - st->es_pid, strerror(errno)); - continue; - } - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = st->es_pid; - dmx_param.input = DMX_IN_FRONTEND; - dmx_param.output = DMX_OUT_TS_TAP; - dmx_param.pes_type = DMX_PES_OTHER; - dmx_param.flags = DMX_IMMEDIATE_START; - - if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s", - t->s_identifier, tda->tda_demux_path, - st->es_pid, strerror(errno)); - close(fd); - fd = -1; - } - - st->es_demuxer_fd = fd; - } -} @@ -140,7 +92,7 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) pthread_mutex_unlock(&tda->tda_delivery_mutex); if(!r) - dvb_service_open_demuxers(tda, t); + tda->tda_open_service(tda, t); dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); @@ -155,7 +107,6 @@ static void dvb_service_stop(service_t *t) { th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - elementary_stream_t *st; lock_assert(&global_lock); @@ -163,12 +114,8 @@ dvb_service_stop(service_t *t) LIST_REMOVE(t, s_active_link); pthread_mutex_unlock(&tda->tda_delivery_mutex); - TAILQ_FOREACH(st, &t->s_components, es_link) { - if(st->es_demuxer_fd != -1) { - close(st->es_demuxer_fd); - st->es_demuxer_fd = -1; - } - } + tda->tda_close_service(tda, t); + t->s_status = SERVICE_IDLE; } @@ -182,7 +129,7 @@ dvb_service_refresh(service_t *t) th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; lock_assert(&global_lock); - dvb_service_open_demuxers(tda, t); + tda->tda_open_service(tda, t); } From 7416dabc5fe3d5d364389082c585d28dfc67d5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 12:08:23 +0200 Subject: [PATCH 046/503] Remove dmx_sct_filter_params from all over the place --- src/dvb/dvb.h | 17 ++++---- src/dvb/dvb_tables.c | 78 ++++++++++++------------------------- src/epggrab/module/eit.c | 12 +++--- src/epggrab/module/opentv.c | 16 ++------ 4 files changed, 42 insertions(+), 81 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 7a71bf9c..63cbae67 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -273,10 +273,12 @@ typedef struct th_dvb_table { int tdt_count; int tdt_pid; - struct dmx_sct_filter_params *tdt_fparams; - int tdt_id; + int tdt_table; + int tdt_mask; + + } th_dvb_table_t; @@ -456,13 +458,10 @@ void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); -struct dmx_sct_filter_params *dvb_fparams_alloc(void); - -void -tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, - int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, - uint8_t tableid, void *opaque), void *opaque, - const char *name, int flags, int pid, th_dvb_table_t *tdt); +void tdt_add(th_dvb_mux_instance_t *tdmi, int table, int mask, + int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, + uint8_t tableid, void *opaque), void *opaque, + const char *name, int flags, int pid, th_dvb_table_t *tdt); int dvb_pidx11_callback (th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index f4aa6899..5e8da338 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -44,15 +44,6 @@ static int tdt_id_tally; -/** - * Helper for preparing a section filter parameter struct - */ -struct dmx_sct_filter_params * -dvb_fparams_alloc(void) -{ - return calloc(1, sizeof(struct dmx_sct_filter_params)); -} - /** * */ @@ -107,7 +98,18 @@ tdt_open_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) close(tdt->tdt_fd); tdt->tdt_fd = -1; } else { - if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, tdt->tdt_fparams)) { + + struct dmx_sct_filter_params fp = {0}; + + fp.filter.filter[0] = tdt->tdt_table; + fp.filter.mask[0] = tdt->tdt_mask; + + if(tdt->tdt_flags & TDT_CRC) + fp.flags |= DMX_CHECK_CRC; + fp.flags |= DMX_IMMEDIATE_START; + fp.pid = tdt->tdt_pid; + + if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, &fp)) { close(tdt->tdt_fd); tdt->tdt_fd = -1; } @@ -265,7 +267,6 @@ dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, } free(tdt->tdt_name); - free(tdt->tdt_fparams); free(tdt); } @@ -276,7 +277,7 @@ dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, * Add a new DVB table */ void -tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, +tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque), void *opaque, const char *name, int flags, int pid, th_dvb_table_t *tdt) @@ -291,18 +292,10 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, if(pid == t->tdt_pid && t->tdt_callback == callback && t->tdt_opaque == opaque) { if (tdt) free(tdt); - if (fparams) free(fparams); return; } } - if(fparams == NULL) - fparams = dvb_fparams_alloc(); - - if(flags & TDT_CRC) fparams->flags |= DMX_CHECK_CRC; - fparams->flags |= DMX_IMMEDIATE_START; - fparams->pid = pid; - if(tdt == NULL) tdt = calloc(1, sizeof(th_dvb_table_t)); @@ -312,7 +305,8 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, tdt->tdt_opaque = opaque; tdt->tdt_pid = pid; tdt->tdt_flags = flags; - tdt->tdt_fparams = fparams; + tdt->tdt_table = tableid; + tdt->tdt_mask = mask; LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link); tdt->tdt_fd = -1; TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); @@ -700,7 +694,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(pid == 0) break; - tdt_add(tdmi, NULL, dvb_ca_callback, (void *)caid, "CA", + tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA", TDT_CA, pid, NULL); break; @@ -1140,24 +1134,21 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, static void dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) { - struct dmx_sct_filter_params *fp; - /* Network Information Table */ - fp = dvb_fparams_alloc(); + int table; if(tdmi->tdmi_adapter->tda_nitoid) { - fp->filter.filter[0] = 0x41; + table = 0x41; } else { - fp->filter.filter[0] = 0x40; + table = 0x40; } - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit", + tdt_add(tdmi, table, 0xff, dvb_nit_callback, NULL, "nit", TDT_QUICKREQ | TDT_CRC, 0x10, NULL); /* Service Descriptor Table and Bouqeut Allocation Table */ - tdt_add(tdmi, NULL, dvb_pidx11_callback, NULL, "pidx11", + tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11", TDT_QUICKREQ | TDT_CRC, 0x11, NULL); } @@ -1168,7 +1159,6 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) static void dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) { - struct dmx_sct_filter_params *fp; int tableid; if(tdmi->tdmi_conf.dmc_fe_params.u.vsb.modulation == VSB_8) { @@ -1177,11 +1167,7 @@ dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) tableid = 0xc9; // Cable } - /* Virtual Channel Table */ - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = tableid; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, atsc_vct_callback, NULL, "vct", + tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct", TDT_QUICKREQ | TDT_CRC, 0x1ffb, NULL); } @@ -1194,22 +1180,12 @@ dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) void dvb_table_add_default(th_dvb_mux_instance_t *tdmi) { - struct dmx_sct_filter_params *fp; - /* Program Allocation Table */ - - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x00; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat", + tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat", TDT_QUICKREQ | TDT_CRC, 0, NULL); /* Conditional Access Table */ - - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x1; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat", + tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat", TDT_CRC, 1, NULL); @@ -1233,14 +1209,10 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid) { - struct dmx_sct_filter_params *fp; char pmtname[100]; snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid); - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x02; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_pmt_callback, NULL, pmtname, + tdt_add(tdmi, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname, TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid, NULL); } diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 9e0add8a..abad1ec2 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -839,19 +839,19 @@ static void _eit_start /* Freesat (3002/3003) */ if (!strcmp("uk_freesat", m->id)) { #ifdef IGNORE_TOO_SLOW - tdt_add(tdmi, NULL, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL); - tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 3841, NULL); + tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3841, NULL); #endif - tdt_add(tdmi, NULL, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002, NULL); - tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 3003, NULL); + tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003, NULL); /* Viasat Baltic (0x39) */ } else if (!strcmp("viasat_baltic", m->id)) { - tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 0x39, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39, NULL); /* Standard (0x12) */ } else { - tdt_add(tdmi, NULL, _eit_callback, m, m->id, TDT_CRC, 0x12, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12, NULL); } tvhlog(LOG_DEBUG, m->id, "install table handlers"); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index e66dd8b1..8b6010aa 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -641,7 +641,6 @@ static void _opentv_start ( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi ) { int *t; - struct dmx_sct_filter_params *fp; epggrab_ota_mux_t *ota; opentv_module_t *mod = (opentv_module_t*)m; opentv_status_t *sta; @@ -668,33 +667,24 @@ static void _opentv_start /* Channels */ t = mod->channel; while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x4a; - fp->filter.mask[0] = 0xff; // TODO: what about 0x46 (service description) - tdt_add(tdmi, fp, _opentv_channel_callback, m, + tdt_add(tdmi, 0x4a, 0xff, _opentv_channel_callback, m, m->id, TDT_CRC, *t++, NULL); } /* Titles */ t = mod->title; while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0xa0; - fp->filter.mask[0] = 0xfc; _opentv_status_get_pid(sta, *t); - tdt_add(tdmi, fp, _opentv_title_callback, m, + tdt_add(tdmi, 0xa0, 0xfc, _opentv_title_callback, m, m->id, TDT_CRC | TDT_TDT, *t++, NULL); } /* Summaries */ t = mod->summary; while (*t) { - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0xa8; - fp->filter.mask[0] = 0xfc; _opentv_status_get_pid(sta, *t); - tdt_add(tdmi, fp, _opentv_summary_callback, m, + tdt_add(tdmi, 0xa8, 0xfc, _opentv_summary_callback, m, m->id, TDT_CRC | TDT_TDT, *t++, NULL); } } From c26216ccba6d5594a2809f7e1c4aa20398452ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 13:00:17 +0200 Subject: [PATCH 047/503] dvb: Move low level DVB table reception to dvb_input_filtered.c --- src/dvb/dvb.h | 6 ++ src/dvb/dvb_adapter.c | 5 +- src/dvb/dvb_input_filtered.c | 189 +++++++++++++++++++++++++++++++++- src/dvb/dvb_tables.c | 191 +---------------------------------- 4 files changed, 198 insertions(+), 193 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 63cbae67..8afe3ae5 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -25,6 +25,8 @@ #include "htsmsg.h" struct service; +struct th_dvb_table; +struct th_dvb_mux_instance; #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) @@ -235,6 +237,8 @@ typedef struct th_dvb_adapter { void (*tda_open_service)(struct th_dvb_adapter *tda, struct service *s); void (*tda_close_service)(struct th_dvb_adapter *tda, struct service *s); + void (*tda_open_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s); + void (*tda_close_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s); } th_dvb_adapter_t; @@ -458,6 +462,8 @@ void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); +void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi); + void tdt_add(th_dvb_mux_instance_t *tdmi, int table, int mask, int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque), void *opaque, diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 83e2d4d0..8f136540 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -68,8 +68,6 @@ tda_alloc(void) tda->tda_allpids_dmx_fd = -1; tda->tda_dump_fd = -1; - dvb_input_filtered_setup(tda); - return tda; } @@ -479,8 +477,7 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - - dvb_table_init(tda); + dvb_input_filtered_setup(tda); if(tda->tda_sat) dvb_satconf_init(tda); diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c index 59d934f8..468b524f 100644 --- a/src/dvb/dvb_input_filtered.c +++ b/src/dvb/dvb_input_filtered.c @@ -19,7 +19,7 @@ /** * DVB input using hardware filters */ - +#include #include #include #include @@ -27,6 +27,7 @@ #include #include #include +#include #include "tvheadend.h" #include "dvb.h" @@ -105,11 +106,197 @@ close_service(th_dvb_adapter_t *tda, service_t *s) +/** + * + */ +static void +open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + struct epoll_event e; + static int tdt_id_tally; + assert(tdt->tdt_fd == -1); + TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); + + tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); + + if(tdt->tdt_fd != -1) { + + tdt->tdt_id = ++tdt_id_tally; + + e.events = EPOLLIN; + e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id; + + if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) { + close(tdt->tdt_fd); + tdt->tdt_fd = -1; + } else { + + struct dmx_sct_filter_params fp = {0}; + + fp.filter.filter[0] = tdt->tdt_table; + fp.filter.mask[0] = tdt->tdt_mask; + + if(tdt->tdt_flags & TDT_CRC) + fp.flags |= DMX_CHECK_CRC; + fp.flags |= DMX_IMMEDIATE_START; + fp.pid = tdt->tdt_pid; + + if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, &fp)) { + close(tdt->tdt_fd); + tdt->tdt_fd = -1; + } + } + } + + if(tdt->tdt_fd == -1) + TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); +} + + +/** + * Close FD for the given table and put table on the pending list + */ +static void +tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + assert(tdt->tdt_fd != -1); + + epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); + close(tdt->tdt_fd); + + tdt->tdt_fd = -1; + TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); +} + + +/** + * + */ +static void +dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, + int r) +{ + int chkcrc = tdt->tdt_flags & TDT_CRC; + int tableid, len; + uint8_t *ptr; + int ret; + + /* It seems some hardware (or is it the dvb API?) does not + honour the DMX_CHECK_CRC flag, so we check it again */ + if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) + return; + + r -= 3; + tableid = sec[0]; + len = ((sec[1] & 0x0f) << 8) | sec[2]; + + if(len < r) + return; + + ptr = &sec[3]; + if(chkcrc) len -= 4; /* Strip trailing CRC */ + + if(tdt->tdt_flags & TDT_CA) + ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt, + sec, len + 3, tableid, tdt->tdt_opaque); + else if(tdt->tdt_flags & TDT_TDT) + ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt); + else + ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); + + if(ret == 0) + tdt->tdt_count++; + + if(tdt->tdt_flags & TDT_QUICKREQ) + dvb_table_fastswitch(tdmi); +} + +/** + * + */ +static void * +dvb_table_input(void *aux) +{ + th_dvb_adapter_t *tda = aux; + int r, i, tid, fd, x; + struct epoll_event ev[1]; + uint8_t sec[4096]; + th_dvb_mux_instance_t *tdmi; + th_dvb_table_t *tdt; + int64_t cycle_barrier = 0; + + while(1) { + x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1); + + for(i = 0; i < x; i++) { + + tid = ev[i].data.u64 & 0xffffffff; + fd = ev[i].data.u64 >> 32; + + if(!(ev[i].events & EPOLLIN)) + continue; + + if((r = read(fd, sec, sizeof(sec))) < 3) + continue; + + pthread_mutex_lock(&global_lock); + if((tdmi = tda->tda_mux_current) != NULL) { + LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) + if(tdt->tdt_id == tid) + break; + + if(tdt != NULL) { + dvb_proc_table(tdmi, tdt, sec, r); + + /* Any tables pending (that wants a filter/fd), close this one */ + if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL && + cycle_barrier < getmonoclock()) { + tdt_close_fd(tdmi, tdt); + cycle_barrier = getmonoclock() + 100000; + tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); + assert(tdt != NULL); + + open_table(tdmi, tdt); + } + } + } + pthread_mutex_unlock(&global_lock); + } + } + return NULL; +} + + +static void +close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + if(tdt->tdt_fd == -1) { + TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); + } else { + epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); + close(tdt->tdt_fd); + } +} + +/** + * + */ void dvb_input_filtered_setup(th_dvb_adapter_t *tda) { tda->tda_open_service = open_service; tda->tda_close_service = close_service; + tda->tda_open_table = open_table; + tda->tda_close_table = close_table; + + pthread_t ptid; + tda->tda_table_epollfd = epoll_create(50); + pthread_create(&ptid, NULL, dvb_table_input, tda); } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 5e8da338..f292b4e5 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -42,12 +42,11 @@ #include "notify.h" #include "cwc.h" -static int tdt_id_tally; /** * */ -static void +void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) { th_dvb_table_t *tdt; @@ -73,183 +72,6 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) } -/** - * - */ -static void -tdt_open_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct epoll_event e; - - assert(tdt->tdt_fd == -1); - TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - - tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - - if(tdt->tdt_fd != -1) { - - tdt->tdt_id = ++tdt_id_tally; - - e.events = EPOLLIN; - e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id; - - if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) { - close(tdt->tdt_fd); - tdt->tdt_fd = -1; - } else { - - struct dmx_sct_filter_params fp = {0}; - - fp.filter.filter[0] = tdt->tdt_table; - fp.filter.mask[0] = tdt->tdt_mask; - - if(tdt->tdt_flags & TDT_CRC) - fp.flags |= DMX_CHECK_CRC; - fp.flags |= DMX_IMMEDIATE_START; - fp.pid = tdt->tdt_pid; - - if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, &fp)) { - close(tdt->tdt_fd); - tdt->tdt_fd = -1; - } - } - } - - if(tdt->tdt_fd == -1) - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); -} - - -/** - * Close FD for the given table and put table on the pending list - */ -static void -tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - - assert(tdt->tdt_fd != -1); - - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); - close(tdt->tdt_fd); - - tdt->tdt_fd = -1; - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); -} - - -/** - * - */ -static void -dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, - int r) -{ - int chkcrc = tdt->tdt_flags & TDT_CRC; - int tableid, len; - uint8_t *ptr; - int ret; - - /* It seems some hardware (or is it the dvb API?) does not - honour the DMX_CHECK_CRC flag, so we check it again */ - if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) - return; - - r -= 3; - tableid = sec[0]; - len = ((sec[1] & 0x0f) << 8) | sec[2]; - - if(len < r) - return; - - ptr = &sec[3]; - if(chkcrc) len -= 4; /* Strip trailing CRC */ - - if(tdt->tdt_flags & TDT_CA) - ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt, - sec, len + 3, tableid, tdt->tdt_opaque); - else if(tdt->tdt_flags & TDT_TDT) - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt); - else - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); - - if(ret == 0) - tdt->tdt_count++; - - if(tdt->tdt_flags & TDT_QUICKREQ) - dvb_table_fastswitch(tdmi); -} - -/** - * - */ -static void * -dvb_table_input(void *aux) -{ - th_dvb_adapter_t *tda = aux; - int r, i, tid, fd, x; - struct epoll_event ev[1]; - uint8_t sec[4096]; - th_dvb_mux_instance_t *tdmi; - th_dvb_table_t *tdt; - int64_t cycle_barrier = 0; - - while(1) { - x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1); - - for(i = 0; i < x; i++) { - - tid = ev[i].data.u64 & 0xffffffff; - fd = ev[i].data.u64 >> 32; - - if(!(ev[i].events & EPOLLIN)) - continue; - - if((r = read(fd, sec, sizeof(sec))) < 3) - continue; - - pthread_mutex_lock(&global_lock); - if((tdmi = tda->tda_mux_current) != NULL) { - LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) - if(tdt->tdt_id == tid) - break; - - if(tdt != NULL) { - dvb_proc_table(tdmi, tdt, sec, r); - - /* Any tables pending (that wants a filter/fd), close this one */ - if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL && - cycle_barrier < getmonoclock()) { - tdt_close_fd(tdmi, tdt); - cycle_barrier = getmonoclock() + 100000; - tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); - assert(tdt != NULL); - - tdt_open_fd(tdmi, tdt); - } - } - } - pthread_mutex_unlock(&global_lock); - } - } - return NULL; -} - - - -/** - * - */ -void -dvb_table_init(th_dvb_adapter_t *tda) -{ - pthread_t ptid; - tda->tda_table_epollfd = epoll_create(50); - pthread_create(&ptid, NULL, dvb_table_input, tda); -} - - /** * */ @@ -258,14 +80,7 @@ dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) { LIST_REMOVE(tdt, tdt_link); - - if(tdt->tdt_fd == -1) { - TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - } else { - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); - close(tdt->tdt_fd); - } - + tda->tda_close_table(tdmi, tdt); free(tdt->tdt_name); free(tdt); } @@ -311,7 +126,7 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, tdt->tdt_fd = -1; TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - tdt_open_fd(tdmi, tdt); + tdmi->tdmi_adapter->tda_open_table(tdmi, tdt); } /** From 43509ecf0204f3592b03724c52968295b7b69900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Fri, 19 Oct 2012 14:42:36 +0200 Subject: [PATCH 048/503] Add new dvb input method: raw Receives all content from a mux and filter it in tvheadend Remove dumpmux feature --- Makefile | 1 + src/dvb/dvb.h | 13 +++--- src/dvb/dvb_adapter.c | 81 ++++++++++++++++------------------ src/dvb/dvb_fe.c | 86 ------------------------------------ src/dvb/dvb_input_raw.c | 88 +++++++++++++++++++++++++++++++++++++ src/webui/extjs_dvb.c | 4 -- src/webui/static/app/dvb.js | 12 +---- 7 files changed, 134 insertions(+), 151 deletions(-) create mode 100644 src/dvb/dvb_input_raw.c diff --git a/Makefile b/Makefile index b7953339..e8d3e37e 100644 --- a/Makefile +++ b/Makefile @@ -156,6 +156,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/dvb/dvb_preconf.c \ src/dvb/dvb_satconf.c \ src/dvb/dvb_input_filtered.c \ + src/dvb/dvb_input_raw.c \ src/webui/extjs_dvb.c \ # V4L diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 8afe3ae5..4cdc7e4d 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -202,7 +202,6 @@ typedef struct th_dvb_adapter { char *tda_demux_path; - char *tda_dvr_path; pthread_t tda_dvr_thread; int tda_dvr_pipe[2]; @@ -223,11 +222,6 @@ typedef struct th_dvb_adapter { struct th_dvb_mux_instance_list tda_mux_list; - uint32_t tda_dump_muxes; - - int tda_allpids_dmx_fd; - int tda_dump_fd; - uint32_t tda_last_fec; int tda_unc_is_delta; /* 1 if we believe FE_READ_UNCORRECTED_BLOCKS @@ -240,6 +234,8 @@ typedef struct th_dvb_adapter { void (*tda_open_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s); void (*tda_close_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s); + int tda_rawmode; + } th_dvb_adapter_t; /** @@ -314,8 +310,6 @@ void dvb_adapter_set_skip_checksubscr(th_dvb_adapter_t *tda, int on); void dvb_adapter_set_qmon(th_dvb_adapter_t *tda, int on); -void dvb_adapter_set_dump_muxes(th_dvb_adapter_t *tda, int on); - void dvb_adapter_set_idleclose(th_dvb_adapter_t *tda, int on); void dvb_adapter_set_poweroff(th_dvb_adapter_t *tda, int on); @@ -349,6 +343,9 @@ void dvb_adapter_poweroff(th_dvb_adapter_t *tda); void dvb_input_filtered_setup(th_dvb_adapter_t *tda); +void dvb_input_raw_setup(th_dvb_adapter_t *tda); + + /** * DVB Multiplex diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8f136540..73c48b6b 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -64,10 +64,6 @@ tda_alloc(void) TAILQ_INIT(&tda->tda_scan_queues[i]); TAILQ_INIT(&tda->tda_initial_scan_queue); TAILQ_INIT(&tda->tda_satconfs); - - tda->tda_allpids_dmx_fd = -1; - tda->tda_dump_fd = -1; - return tda; } @@ -90,7 +86,6 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "skip_checksubscr", tda->tda_skip_checksubscr); htsmsg_add_u32(m, "sidtochan", tda->tda_sidtochan); htsmsg_add_u32(m, "qmon", tda->tda_qmon); - htsmsg_add_u32(m, "dump_muxes", tda->tda_dump_muxes); htsmsg_add_u32(m, "poweroff", tda->tda_poweroff); htsmsg_add_u32(m, "nitoid", tda->tda_nitoid); htsmsg_add_u32(m, "diseqc_version", tda->tda_diseqc_version); @@ -274,25 +269,6 @@ dvb_adapter_set_poweroff(th_dvb_adapter_t *tda, int on) } -/** - * - */ -void -dvb_adapter_set_dump_muxes(th_dvb_adapter_t *tda, int on) -{ - if(tda->tda_dump_muxes == on) - return; - - lock_assert(&global_lock); - - tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" dump of DVB mux input set to: %s", - tda->tda_displayname, on ? "On" : "Off"); - - tda->tda_dump_muxes = on; - tda_save(tda); -} - - /** * */ @@ -428,8 +404,6 @@ tda_add(int adapter_num) tda->tda_rootpath = strdup(path); tda->tda_demux_path = malloc(256); snprintf(tda->tda_demux_path, 256, "%s/demux0", path); - tda->tda_dvr_path = malloc(256); - snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path); tda->tda_fe_path = strdup(fname); tda->tda_fe_fd = -1; tda->tda_dvr_pipe[0] = -1; @@ -588,7 +562,6 @@ dvb_adapter_init(uint32_t adapter_mask) htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr); htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan); htsmsg_get_u32(c, "qmon", &tda->tda_qmon); - htsmsg_get_u32(c, "dump_muxes", &tda->tda_dump_muxes); htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff); htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid); htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version); @@ -750,17 +723,51 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r, c, efd, nfds; + int fd, i, r, c, efd, nfds, dmx = -1; uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; + char path[256]; - fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); + snprintf(path, sizeof(path), "%s/dvr0", tda->tda_rootpath); + + fd = tvh_open(path, O_RDONLY | O_NONBLOCK, 0); if(fd == -1) { - tvhlog(LOG_ALERT, "dvb", "%s: unable to open dvr", tda->tda_dvr_path); + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", path, strerror(errno)); return NULL; } + if(tda->tda_rawmode) { + + // Receive unfiltered raw transport stream + + dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(dmx == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_demux_path, strerror(errno)); + close(fd); + return NULL; + } + + struct dmx_pes_filter_params dmx_param; + + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = 0x2000; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param)) { + tvhlog(LOG_ERR, "dvb", + "Unable to configure demuxer \"%s\" for all PIDs -- %s", + tda->tda_demux_path, strerror(errno)); + close(dmx); + close(fd); + return NULL; + } + } + /* Create poll */ efd = epoll_create(2); ev.events = EPOLLIN; @@ -790,18 +797,6 @@ dvb_adapter_input_dvr(void *aux) if (r < 188) continue; pthread_mutex_lock(&tda->tda_delivery_mutex); - - /* debug */ - if(tda->tda_dump_fd != -1) { - if(write(tda->tda_dump_fd, tsb, r) != r) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to write to mux dump file -- %s", - tda->tda_identifier, strerror(errno)); - close(tda->tda_dump_fd); - tda->tda_dump_fd = -1; - } - } - /* Process */ while (r >= 188) { @@ -828,6 +823,8 @@ dvb_adapter_input_dvr(void *aux) i = 0; } + if(dmx != -1) + close(dmx); close(efd); close(fd); return NULL; diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 4830e2fc..321ae860 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -229,16 +229,6 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) assert(tdmi == tda->tda_mux_current); tda->tda_mux_current = NULL; - if(tda->tda_allpids_dmx_fd != -1) { - close(tda->tda_allpids_dmx_fd); - tda->tda_allpids_dmx_fd = -1; - } - - if(tda->tda_dump_fd != -1) { - close(tda->tda_dump_fd); - tda->tda_dump_fd = -1; - } - if(tdmi->tdmi_table_initial) { tdmi->tdmi_table_initial = 0; tda->tda_initial_num_mux--; @@ -263,79 +253,6 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) } } - -/** - * Open a dump file which we write the entire mux output to - */ -static void -dvb_adapter_open_dump_file(th_dvb_adapter_t *tda) -{ - struct dmx_pes_filter_params dmx_param; - char fullname[1000]; - char path[500]; - const char *fname = tda->tda_mux_current->tdmi_identifier; - - int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - if(fd == -1) - return; - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = 0x2000; - dmx_param.input = DMX_IN_FRONTEND; - dmx_param.output = DMX_OUT_TS_TAP; - dmx_param.pes_type = DMX_PES_OTHER; - dmx_param.flags = DMX_IMMEDIATE_START; - - if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to configure demuxer \"%s\" for all PIDs -- %s", - fname, tda->tda_demux_path, - strerror(errno)); - close(fd); - return; - } - - snprintf(path, sizeof(path), "%s/muxdumps", - dvr_config_find_by_name_default("")->dvr_storage); - - if(mkdir(path, 0777) && errno != EEXIST) { - tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump dir %s -- %s", - fname, path, strerror(errno)); - close(fd); - return; - } - - int attempt = 1; - - while(1) { - struct stat st; - snprintf(fullname, sizeof(fullname), "%s/%s.dump%d.ts", - path, fname, attempt); - - if(stat(fullname, &st) == -1) - break; - - attempt++; - } - - int f = open(fullname, O_CREAT | O_TRUNC | O_WRONLY, 0777); - - if(f == -1) { - tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump file %s -- %s", - fname, fullname, strerror(errno)); - close(fd); - return; - } - - tvhlog(LOG_WARNING, "dvb", "\"%s\" writing to mux dump file %s", - fname, fullname); - - tda->tda_allpids_dmx_fd = fd; - tda->tda_dump_fd = f; -} - - - #if DVB_API_VERSION >= 5 static int check_frontend (int fe_fd, int dvr, int human_readable) { @@ -553,9 +470,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) tda->tda_mux_current = tdmi; - if(tda->tda_dump_muxes) - dvb_adapter_open_dump_file(tda); - gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); diff --git a/src/dvb/dvb_input_raw.c b/src/dvb/dvb_input_raw.c new file mode 100644 index 00000000..b1677805 --- /dev/null +++ b/src/dvb/dvb_input_raw.c @@ -0,0 +1,88 @@ +/* + * TV Input - Linux DVB interface + * Copyright (C) 2012 Andreas Öman + * + * 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 . + */ + +/** + * DVB input from a raw transport stream + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "dvb.h" +#include "service.h" + +/** + * Install filters for a service + * + * global_lock must be held + */ +static void +open_service(th_dvb_adapter_t *tda, service_t *s) +{ +} + + +/** + * Remove filters for a service + * + * global_lock must be held + */ +static void +close_service(th_dvb_adapter_t *tda, service_t *s) +{ +} + + +/** + * + */ +static void +open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ +} + + +/** + * + */ +static void +close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ +} + +/** + * + */ +void +dvb_input_raw_setup(th_dvb_adapter_t *tda) +{ + tda->tda_rawmode = 1; + tda->tda_open_service = open_service; + tda->tda_close_service = close_service; + tda->tda_open_table = open_table; + tda->tda_close_table = close_table; +} + diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 6d4d2c04..4ae0d35d 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -153,7 +153,6 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "idleclose", tda->tda_idleclose); htsmsg_add_u32(r, "skip_checksubscr", tda->tda_skip_checksubscr); htsmsg_add_u32(r, "qmon", tda->tda_qmon); - htsmsg_add_u32(r, "dumpmux", tda->tda_dump_muxes); htsmsg_add_u32(r, "poweroff", tda->tda_poweroff); htsmsg_add_u32(r, "sidtochan", tda->tda_sidtochan); htsmsg_add_u32(r, "nitoid", tda->tda_nitoid); @@ -197,9 +196,6 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) s = http_arg_get(&hc->hc_req_args, "sidtochan"); dvb_adapter_set_sidtochan(tda, !!s); - s = http_arg_get(&hc->hc_req_args, "dumpmux"); - dvb_adapter_set_dump_muxes(tda, !!s); - s = http_arg_get(&hc->hc_req_args, "disable_pmt_monitor"); dvb_adapter_set_disable_pmt_monitor(tda, !!s); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 5667e6f1..48365610 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1087,7 +1087,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { var confreader = new Ext.data.JsonReader({ root : 'dvbadapters' }, [ 'name', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', - 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'dumpmux', + 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', ,'disable_pmt_monitor', 'idleclose' ]); @@ -1138,16 +1138,6 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { new Ext.form.Checkbox({ fieldLabel : 'Disable PMT monitoring', name : 'disable_pmt_monitor' - }), - new Ext.form.Checkbox({ - fieldLabel : 'Write full DVB MUX to disk', - name : 'dumpmux', - handler : function(s, v) { - if (v) Ext.MessageBox.alert('DVB Mux dump', - 'Please note that keeping this ' - + 'option enabled can consume a lot ' - + 'of diskspace. You have been warned'); - } }), { fieldLabel : 'Original Network ID', name : 'nitoid', From f4fc4380314c1e03cb3a3dbd6bf13fa18371e8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 22 Oct 2012 11:26:24 +0200 Subject: [PATCH 049/503] dvb: Add table parsing when in raw mode --- src/dvb/dvb.h | 40 +++++++++++++-- src/dvb/dvb_adapter.c | 18 ++++++- src/dvb/dvb_fe.c | 2 + src/dvb/dvb_input_filtered.c | 47 +----------------- src/dvb/dvb_input_raw.c | 88 +++++++++++++++++++++++++++++++++ src/dvb/dvb_tables.c | 96 +++++++++++++++++++++++++++++------- src/epggrab/module/eit.c | 8 +-- src/epggrab/module/opentv.c | 6 +-- src/psi.c | 2 +- 9 files changed, 232 insertions(+), 75 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 4cdc7e4d..8d7929a9 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -23,6 +23,7 @@ #include #include #include "htsmsg.h" +#include "psi.h" struct service; struct th_dvb_table; @@ -107,6 +108,8 @@ typedef struct th_dvb_mux_instance { LIST_HEAD(, th_dvb_table) tdmi_tables; + int tdmi_num_tables; + TAILQ_HEAD(, th_dvb_table) tdmi_table_queue; int tdmi_table_initial; @@ -150,6 +153,22 @@ typedef struct th_dvb_mux_instance { } th_dvb_mux_instance_t; + + +/** + * When in raw mode we need to enqueue raw TS packet + * to a different thread because we need to hold + * global_lock when doing delivery of the tables + */ +TAILQ_HEAD(dvb_table_feed_queue, dvb_table_feed); + +typedef struct dvb_table_feed { + TAILQ_ENTRY(dvb_table_feed) dtf_link; + uint8_t dtf_tsb[188]; +} dvb_table_feed_t; + + + /** * DVB Adapter (one of these per physical adapter) */ @@ -236,6 +255,14 @@ typedef struct th_dvb_adapter { int tda_rawmode; + + struct dvb_table_feed_queue tda_table_feed; + pthread_cond_t tda_table_feed_cond; // Bound to tda_delivery_mutex + + // PIDs that needs to be requeued and processed as tables + uint8_t tda_table_filter[8192]; + + } th_dvb_adapter_t; /** @@ -262,6 +289,7 @@ typedef struct th_dvb_table { int tdt_fd; LIST_ENTRY(th_dvb_table) tdt_link; + th_dvb_mux_instance_t *tdt_tdmi; char *tdt_name; @@ -278,6 +306,10 @@ typedef struct th_dvb_table { int tdt_table; int tdt_mask; + int tdt_destroyed; + int tdt_refcount; + + psi_section_t tdt_sect; // Manual reassembly } th_dvb_table_t; @@ -459,12 +491,10 @@ void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); -void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi); - void tdt_add(th_dvb_mux_instance_t *tdmi, int table, int mask, int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque), void *opaque, - const char *name, int flags, int pid, th_dvb_table_t *tdt); + const char *name, int flags, int pid); int dvb_pidx11_callback (th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, @@ -475,6 +505,10 @@ int dvb_pidx11_callback #define TDT_CA 0x4 #define TDT_TDT 0x8 +void dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt); + +void dvb_table_release(th_dvb_table_t *tdt); + /** * Satellite configuration */ diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 73c48b6b..c3ad82b7 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -451,7 +451,7 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - dvb_input_filtered_setup(tda); + dvb_input_raw_setup(tda); if(tda->tda_sat) dvb_satconf_init(tda); @@ -796,12 +796,25 @@ dvb_adapter_input_dvr(void *aux) /* not enough data */ if (r < 188) continue; + int wakeup_table_feed = 0; // Just wanna wakeup once + pthread_mutex_lock(&tda->tda_delivery_mutex); /* Process */ while (r >= 188) { /* sync */ if (tsb[i] == 0x47) { + + if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error + int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; + if(tda->tda_table_filter[pid]) { + dvb_table_feed_t *dtf = malloc(sizeof(dvb_table_feed_t)); + memcpy(dtf->dtf_tsb, tsb + i, 188); + TAILQ_INSERT_TAIL(&tda->tda_table_feed, dtf, dtf_link); + wakeup_table_feed = 1; + } + } + LIST_FOREACH(t, &tda->tda_transports, s_active_link) if(t->s_dvb_mux_instance == tda->tda_mux_current) ts_recv_packet1(t, tsb + i, NULL); @@ -816,6 +829,9 @@ dvb_adapter_input_dvr(void *aux) } } + if(wakeup_table_feed) + pthread_cond_signal(&tda->tda_table_feed_cond); + pthread_mutex_unlock(&tda->tda_delivery_mutex); /* reset buffer */ diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 321ae860..1ebfda3a 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -226,6 +226,8 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + lock_assert(&global_lock); + assert(tdmi == tda->tda_mux_current); tda->tda_mux_current = NULL; diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c index 468b524f..a4dddf26 100644 --- a/src/dvb/dvb_input_filtered.c +++ b/src/dvb/dvb_input_filtered.c @@ -116,9 +116,6 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) struct epoll_event e; static int tdt_id_tally; - assert(tdt->tdt_fd == -1); - TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); if(tdt->tdt_fd != -1) { @@ -173,47 +170,6 @@ tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) } -/** - * - */ -static void -dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, - int r) -{ - int chkcrc = tdt->tdt_flags & TDT_CRC; - int tableid, len; - uint8_t *ptr; - int ret; - - /* It seems some hardware (or is it the dvb API?) does not - honour the DMX_CHECK_CRC flag, so we check it again */ - if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) - return; - - r -= 3; - tableid = sec[0]; - len = ((sec[1] & 0x0f) << 8) | sec[2]; - - if(len < r) - return; - - ptr = &sec[3]; - if(chkcrc) len -= 4; /* Strip trailing CRC */ - - if(tdt->tdt_flags & TDT_CA) - ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt, - sec, len + 3, tableid, tdt->tdt_opaque); - else if(tdt->tdt_flags & TDT_TDT) - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt); - else - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); - - if(ret == 0) - tdt->tdt_count++; - - if(tdt->tdt_flags & TDT_QUICKREQ) - dvb_table_fastswitch(tdmi); -} /** * @@ -250,7 +206,7 @@ dvb_table_input(void *aux) break; if(tdt != NULL) { - dvb_proc_table(tdmi, tdt, sec, r); + dvb_table_dispatch(sec, r, tdt); /* Any tables pending (that wants a filter/fd), close this one */ if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL && @@ -259,6 +215,7 @@ dvb_table_input(void *aux) cycle_barrier = getmonoclock() + 100000; tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); assert(tdt != NULL); + TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); open_table(tdmi, tdt); } diff --git a/src/dvb/dvb_input_raw.c b/src/dvb/dvb_input_raw.c index b1677805..0c3f2a56 100644 --- a/src/dvb/dvb_input_raw.c +++ b/src/dvb/dvb_input_raw.c @@ -42,6 +42,7 @@ static void open_service(th_dvb_adapter_t *tda, service_t *s) { + // NOP -- We receive all PIDs anyway } @@ -53,6 +54,7 @@ open_service(th_dvb_adapter_t *tda, service_t *s) static void close_service(th_dvb_adapter_t *tda, service_t *s) { + // NOP -- open_service() is a NOP } @@ -62,6 +64,9 @@ close_service(th_dvb_adapter_t *tda, service_t *s) static void open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) { + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + assert(tdt->tdt_pid < 0x2000); + tda->tda_table_filter[tdt->tdt_pid] = 1; } @@ -71,8 +76,84 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) static void close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) { + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + assert(tdt->tdt_pid < 0x2000); + tda->tda_table_filter[tdt->tdt_pid] = 0; } + +/** + * + */ +static void +got_section(const uint8_t *data, size_t len, void *opaque) +{ + th_dvb_table_t *tdt = opaque; + dvb_table_dispatch((uint8_t *)data, len, tdt); +} + + + +/** + * All the tables can be destroyed from any of the callbacks + * so we need to be a bit careful here + */ +static void +dvb_table_raw_dispatch(th_dvb_mux_instance_t *tdmi, + const dvb_table_feed_t *dtf) +{ + int pid = (dtf->dtf_tsb[1] & 0x1f) << 8 | dtf->dtf_tsb[2]; + th_dvb_table_t *vec[tdmi->tdmi_num_tables], *tdt; + int i = 0; + LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) { + vec[i++] = tdt; + tdt->tdt_refcount++; + } + assert(i == tdmi->tdmi_num_tables); + int len = tdmi->tdmi_num_tables; // can change during callbacks + for(i = 0; i < len; i++) { + tdt = vec[i]; + if(!tdt->tdt_destroyed) { + if(tdt->tdt_pid == pid) + psi_section_reassemble(&tdt->tdt_sect, dtf->dtf_tsb, + 0, got_section, tdt); + } + dvb_table_release(tdt); + } +} + +/** + * + */ +static void * +dvb_table_input(void *aux) +{ + th_dvb_adapter_t *tda = aux; + th_dvb_mux_instance_t *tdmi; + dvb_table_feed_t *dtf; + + while(1) { + + pthread_mutex_lock(&tda->tda_delivery_mutex); + + while((dtf = TAILQ_FIRST(&tda->tda_table_feed)) == NULL) + pthread_cond_wait(&tda->tda_table_feed_cond, &tda->tda_delivery_mutex); + TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link); + + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + pthread_mutex_lock(&global_lock); + + if((tdmi = tda->tda_mux_current) != NULL) + dvb_table_raw_dispatch(tdmi, dtf); + + pthread_mutex_unlock(&global_lock); + free(dtf); + } + return NULL; +} + + /** * */ @@ -84,5 +165,12 @@ dvb_input_raw_setup(th_dvb_adapter_t *tda) tda->tda_close_service = close_service; tda->tda_open_table = open_table; tda->tda_close_table = close_table; + + TAILQ_INIT(&tda->tda_table_feed); + pthread_cond_init(&tda->tda_table_feed_cond, NULL); + + pthread_t ptid; + pthread_create(&ptid, NULL, dvb_table_input, tda); + } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index f292b4e5..529f5c50 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -46,7 +46,7 @@ /** * */ -void +static void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) { th_dvb_table_t *tdt; @@ -72,6 +72,66 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) } +/** + * + */ +void +dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt) +{ + if(tdt->tdt_destroyed) + return; + + int chkcrc = tdt->tdt_flags & TDT_CRC; + int tableid, len; + uint8_t *ptr; + int ret; + th_dvb_mux_instance_t *tdmi = tdt->tdt_tdmi; + + /* It seems some hardware (or is it the dvb API?) does not + honour the DMX_CHECK_CRC flag, so we check it again */ + if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) + return; + + r -= 3; + tableid = sec[0]; + len = ((sec[1] & 0x0f) << 8) | sec[2]; + + if(len < r) + return; + + if((tableid & tdt->tdt_mask) != tdt->tdt_table) + return; + + ptr = &sec[3]; + if(chkcrc) len -= 4; /* Strip trailing CRC */ + + if(tdt->tdt_flags & TDT_CA) + ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt, + sec, len + 3, tableid, tdt->tdt_opaque); + else if(tdt->tdt_flags & TDT_TDT) + ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt); + else + ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque); + + if(ret == 0) + tdt->tdt_count++; + + if(tdt->tdt_flags & TDT_QUICKREQ) + dvb_table_fastswitch(tdmi); +} + + +/** + * + */ +void +dvb_table_release(th_dvb_table_t *tdt) +{ + if(--tdt->tdt_refcount == 0) + free(tdt); +} + + /** * */ @@ -79,15 +139,17 @@ static void dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) { + lock_assert(&global_lock); + assert(tdt->tdt_tdmi == tdmi); LIST_REMOVE(tdt, tdt_link); + tdmi->tdmi_num_tables--; tda->tda_close_table(tdmi, tdt); free(tdt->tdt_name); - free(tdt); + tdt->tdt_destroyed = 1; + dvb_table_release(tdt); } - - /** * Add a new DVB table */ @@ -95,7 +157,7 @@ void tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque), void *opaque, - const char *name, int flags, int pid, th_dvb_table_t *tdt) + const char *name, int flags, int pid) { th_dvb_table_t *t; @@ -106,15 +168,12 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) { if(pid == t->tdt_pid && t->tdt_callback == callback && t->tdt_opaque == opaque) { - if (tdt) free(tdt); return; } } - - if(tdt == NULL) - tdt = calloc(1, sizeof(th_dvb_table_t)); - + th_dvb_table_t *tdt = calloc(1, sizeof(th_dvb_table_t)); + tdt->tdt_refcount = 1; tdt->tdt_name = strdup(name); tdt->tdt_callback = callback; tdt->tdt_opaque = opaque; @@ -122,9 +181,10 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, tdt->tdt_flags = flags; tdt->tdt_table = tableid; tdt->tdt_mask = mask; + tdt->tdt_tdmi = tdmi; LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link); + tdmi->tdmi_num_tables++; tdt->tdt_fd = -1; - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); tdmi->tdmi_adapter->tda_open_table(tdmi, tdt); } @@ -510,7 +570,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, break; tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA", - TDT_CA, pid, NULL); + TDT_CA, pid); break; default: @@ -959,12 +1019,12 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) table = 0x40; } tdt_add(tdmi, table, 0xff, dvb_nit_callback, NULL, "nit", - TDT_QUICKREQ | TDT_CRC, 0x10, NULL); + TDT_QUICKREQ | TDT_CRC, 0x10); /* Service Descriptor Table and Bouqeut Allocation Table */ tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11", - TDT_QUICKREQ | TDT_CRC, 0x11, NULL); + TDT_QUICKREQ | TDT_CRC, 0x11); } @@ -983,7 +1043,7 @@ dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) } tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct", - TDT_QUICKREQ | TDT_CRC, 0x1ffb, NULL); + TDT_QUICKREQ | TDT_CRC, 0x1ffb); } @@ -997,11 +1057,11 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi) { /* Program Allocation Table */ tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat", - TDT_QUICKREQ | TDT_CRC, 0, NULL); + TDT_QUICKREQ | TDT_CRC, 0); /* Conditional Access Table */ tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat", - TDT_CRC, 1, NULL); + TDT_CRC, 1); switch(tdmi->tdmi_adapter->tda_type) { @@ -1028,7 +1088,7 @@ dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid) snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid); tdt_add(tdmi, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname, - TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid, NULL); + TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid); } void diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index abad1ec2..cb5bffd3 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -842,16 +842,16 @@ static void _eit_start tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL); tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3841, NULL); #endif - tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002, NULL); - tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003, NULL); + tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003); /* Viasat Baltic (0x39) */ } else if (!strcmp("viasat_baltic", m->id)) { - tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39); /* Standard (0x12) */ } else { - tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12, NULL); + tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12); } tvhlog(LOG_DEBUG, m->id, "install table handlers"); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 8b6010aa..0778ca82 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -669,7 +669,7 @@ static void _opentv_start while (*t) { // TODO: what about 0x46 (service description) tdt_add(tdmi, 0x4a, 0xff, _opentv_channel_callback, m, - m->id, TDT_CRC, *t++, NULL); + m->id, TDT_CRC, *t++); } /* Titles */ @@ -677,7 +677,7 @@ static void _opentv_start while (*t) { _opentv_status_get_pid(sta, *t); tdt_add(tdmi, 0xa0, 0xfc, _opentv_title_callback, m, - m->id, TDT_CRC | TDT_TDT, *t++, NULL); + m->id, TDT_CRC | TDT_TDT, *t++); } /* Summaries */ @@ -685,7 +685,7 @@ static void _opentv_start while (*t) { _opentv_status_get_pid(sta, *t); tdt_add(tdmi, 0xa8, 0xfc, _opentv_summary_callback, m, - m->id, TDT_CRC | TDT_TDT, *t++, NULL); + m->id, TDT_CRC | TDT_TDT, *t++); } } diff --git a/src/psi.c b/src/psi.c index 7960ae2b..dcfec72a 100644 --- a/src/psi.c +++ b/src/psi.c @@ -63,8 +63,8 @@ psi_section_reassemble0(psi_section_t *ps, const uint8_t *data, if(crc && tvh_crc32(ps->ps_data, tsize, 0xffffffff)) return -1; - cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque); ps->ps_offset = 0; + cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque); return len - excess; } From 9f0de04a7bb35d61ec6b437addfb6e85b0c6ce59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 22 Oct 2012 16:19:26 +0200 Subject: [PATCH 050/503] dvb: Add support for grabbing entire mux directly via HTTP --- src/dvb/dvb.c | 4 +- src/dvb/dvb.h | 20 +++++++- src/dvb/dvb_adapter.c | 71 +++++++++++++++++++++++++- src/dvb/dvb_multiplex.c | 29 +++++++++++ src/dvb/dvb_service.c | 5 +- src/main.c | 8 ++- src/subscriptions.c | 33 +++++++++---- src/subscriptions.h | 13 ++++- src/webui/webui.c | 107 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 269 insertions(+), 21 deletions(-) diff --git a/src/dvb/dvb.c b/src/dvb/dvb.c index 4c764eba..9e3775b3 100644 --- a/src/dvb/dvb.c +++ b/src/dvb/dvb.c @@ -23,8 +23,8 @@ #include "dvb_charset.h" void -dvb_init(uint32_t adapter_mask) +dvb_init(uint32_t adapter_mask, const char *rawfile) { dvb_charset_init(); - dvb_adapter_init(adapter_mask); + dvb_adapter_init(adapter_mask, rawfile); } diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 8d7929a9..2b0460d5 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -150,6 +150,8 @@ typedef struct th_dvb_mux_instance { TAILQ_HEAD(, epggrab_ota_mux) tdmi_epg_grab; + struct th_subscription_list tdmi_subscriptions; + } th_dvb_mux_instance_t; @@ -255,6 +257,10 @@ typedef struct th_dvb_adapter { int tda_rawmode; + // Full mux streaming, protected via the delivery mutex + + streaming_pad_t tda_streaming_pad; + struct dvb_table_feed_queue tda_table_feed; pthread_cond_t tda_table_feed_cond; // Bound to tda_delivery_mutex @@ -317,12 +323,12 @@ typedef struct th_dvb_table { extern struct th_dvb_adapter_queue dvb_adapters; extern struct th_dvb_mux_instance_tree dvb_muxes; -void dvb_init(uint32_t adapter_mask); +void dvb_init(uint32_t adapter_mask, const char *rawfile); /** * DVB Adapter */ -void dvb_adapter_init(uint32_t adapter_mask); +void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile); void dvb_adapter_mux_scanner(void *aux); @@ -524,4 +530,14 @@ dvb_satconf_t *dvb_satconf_entry_find(th_dvb_adapter_t *tda, void dvb_lnb_get_frequencies(const char *id, int *f_low, int *f_hi, int *f_switch); + +/** + * Raw demux + */ +struct th_subscription; +struct th_subscription *dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, + const char *name, + streaming_target_t *st); + #endif /* DVB_H_ */ + diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index c3ad82b7..b52df831 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -64,6 +64,7 @@ tda_alloc(void) TAILQ_INIT(&tda->tda_scan_queues[i]); TAILQ_INIT(&tda->tda_initial_scan_queue); TAILQ_INIT(&tda->tda_satconfs); + streaming_pad_init(&tda->tda_streaming_pad); return tda; } @@ -459,6 +460,53 @@ tda_add(int adapter_num) gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); } + +/** + * + */ +static void +tda_add_from_file(const char *filename) +{ + int i, r; + th_dvb_adapter_t *tda; + char buf[400]; + + tda = tda_alloc(); + + tda->tda_adapter_num = -1; + tda->tda_fe_fd = -1; + tda->tda_dvr_pipe[0] = -1; + + tda->tda_type = -1; + + snprintf(buf, sizeof(buf), "%s", filename); + + r = strlen(buf); + for(i = 0; i < r; i++) + if(!isalnum((int)buf[i])) + buf[i] = '_'; + + tda->tda_identifier = strdup(buf); + + tda->tda_autodiscovery = 0; + tda->tda_idlescan = 0; + + tda->tda_sat = 0; + + /* Come up with an initial displayname, user can change it and it will + be overridden by any stored settings later on */ + + tda->tda_displayname = strdup(filename); + + TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); + + dvb_input_raw_setup(tda); +} + + +/** + * + */ void dvb_adapter_start ( th_dvb_adapter_t *tda ) { @@ -515,7 +563,7 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) * */ void -dvb_adapter_init(uint32_t adapter_mask) +dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) { htsmsg_t *l, *c; htsmsg_field_t *f; @@ -529,6 +577,10 @@ dvb_adapter_init(uint32_t adapter_mask) if ((1 << i) & adapter_mask) tda_add(i); + if(rawfile) + tda_add_from_file(rawfile); + + l = hts_settings_load("dvbadapters"); if(l != NULL) { HTSMSG_FOREACH(f, l) { @@ -605,6 +657,10 @@ dvb_adapter_mux_scanner(void *aux) if(service_compute_weight(&tda->tda_transports) > 0) return; + if(tda->tda_mux_current != NULL && + LIST_FIRST(&tda->tda_mux_current->tdmi_subscriptions) != NULL) + return; // Someone is doing full mux dump + /* Check if we have muxes pending for quickscan, if so, choose them */ if((tdmi = TAILQ_FIRST(&tda->tda_initial_scan_queue)) != NULL) { dvb_fe_tune(tdmi, "Initial autoscan"); @@ -804,7 +860,18 @@ dvb_adapter_input_dvr(void *aux) /* sync */ if (tsb[i] == 0x47) { - + + if(LIST_FIRST(&tda->tda_streaming_pad.sp_targets) != NULL) { + streaming_message_t sm; + pktbuf_t *pb = pktbuf_alloc(tsb, 188); + memset(&sm, 0, sizeof(sm)); + sm.sm_type = SMT_MPEGTS; + sm.sm_data = pb; + streaming_pad_deliver(&tda->tda_streaming_pad, &sm); + pktbuf_ref_dec(pb); + } + + if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; if(tda->tda_table_filter[pid]) { diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 46ea57ef..1ac5caf1 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1285,3 +1285,32 @@ th_dvb_mux_instance_t *dvb_mux_find } return NULL; } + + +/** + * + */ +th_subscription_t * +dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, + const char *name, + streaming_target_t *st) +{ + th_subscription_t *s; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + s = subscription_create(INT32_MAX, name, st, SUBSCRIPTION_RAW_MPEGTS, + NULL, NULL, NULL, NULL); + + + s->ths_tdmi = tdmi; + LIST_INSERT_HEAD(&tdmi->tdmi_subscriptions, s, ths_tdmi_link); + + dvb_fe_tune(tdmi, "Full mux subscription"); + + pthread_mutex_lock(&tda->tda_delivery_mutex); + streaming_target_connect(&tda->tda_streaming_pad, &s->ths_input); + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + notify_reload("subscriptions"); + return s; +} diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 32877f8e..82621227 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -79,7 +79,10 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) if(w && w >= weight && !force_start) /* We are outranked by weight, cant use it */ return SM_CODE_NOT_FREE; - + + if(LIST_FIRST(&tdmi->tdmi_subscriptions) != NULL) + return SM_CODE_NOT_FREE; + dvb_adapter_clean(tda); } diff --git a/src/main.c b/src/main.c index 9cca79fd..263bb6ce 100644 --- a/src/main.c +++ b/src/main.c @@ -262,6 +262,7 @@ main(int argc, char **argv) sigset_t set; const char *homedir; const char *rawts_input = NULL; + const char *dvb_rawts_input = NULL; const char *join_transport = NULL; const char *confpath = NULL; char *p, *endp; @@ -279,7 +280,7 @@ main(int argc, char **argv) // make sure the timezone is set tzset(); - while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:")) != -1) { + while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:R:")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; @@ -340,6 +341,9 @@ main(int argc, char **argv) case 'r': rawts_input = optarg; break; + case 'R': + dvb_rawts_input = optarg; + break; case 'j': join_transport = optarg; break; @@ -421,7 +425,7 @@ main(int argc, char **argv) tcp_server_init(); #if ENABLE_LINUXDVB - dvb_init(adapter_mask); + dvb_init(adapter_mask, dvb_rawts_input); #endif iptv_input_init(); #if ENABLE_V4L diff --git a/src/subscriptions.c b/src/subscriptions.c index 1bd429db..1cae0a4f 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -39,6 +39,7 @@ #include "htsmsg.h" #include "notify.h" #include "atomic.h" +#include "dvb/dvb.h" struct th_subscription_list subscriptions; static gtimer_t subscription_reschedule_timer; @@ -215,6 +216,14 @@ subscription_unsubscribe(th_subscription_t *s) if(t != NULL) service_remove_subscriber(t, s, SM_CODE_OK); + if(s->ths_tdmi != NULL) { + LIST_REMOVE(s, ths_tdmi_link); + th_dvb_adapter_t *tda = s->ths_tdmi->tdmi_adapter; + pthread_mutex_lock(&tda->tda_delivery_mutex); + streaming_target_disconnect(&tda->tda_streaming_pad, &s->ths_input); + pthread_mutex_unlock(&tda->tda_delivery_mutex); + } + if(s->ths_start_message != NULL) streaming_msg_free(s->ths_start_message); @@ -302,9 +311,9 @@ subscription_input_direct(void *opauqe, streaming_message_t *sm) /** * */ -static th_subscription_t * +th_subscription_t * subscription_create(int weight, const char *name, streaming_target_t *st, - int flags, int direct, const char *hostname, + int flags, st_callback_t *cb, const char *hostname, const char *username, const char *client) { th_subscription_t *s = calloc(1, sizeof(th_subscription_t)); @@ -316,9 +325,8 @@ subscription_create(int weight, const char *name, streaming_target_t *st, else reject |= SMT_TO_MASK(SMT_MPEGTS); // Reject raw mpegts - - streaming_target_init(&s->ths_input, direct ? subscription_input_direct : - subscription_input, s, reject); + streaming_target_init(&s->ths_input, + cb ?: subscription_input_direct, s, reject); s->ths_weight = weight; s->ths_title = strdup(name); @@ -348,8 +356,10 @@ subscription_create_from_channel(channel_t *ch, unsigned int weight, int flags, const char *hostname, const char *username, const char *client) { - th_subscription_t *s = subscription_create(weight, name, st, flags, 0, - hostname, username, client); + th_subscription_t *s; + + s = subscription_create(weight, name, st, flags, subscription_input, + hostname, username, client); s->ths_channel = ch; LIST_INSERT_HEAD(&ch->ch_subscriptions, s, ths_channel_link); @@ -391,13 +401,16 @@ subscription_create_from_channel(channel_t *ch, unsigned int weight, */ th_subscription_t * subscription_create_from_service(service_t *t, const char *name, - streaming_target_t *st, int flags) + streaming_target_t *st, int flags) { - th_subscription_t *s = subscription_create(INT32_MAX, name, st, flags, 1, - NULL, NULL, NULL); + th_subscription_t *s; source_info_t si; int r; + s = subscription_create(INT32_MAX, name, st, flags, + subscription_input_direct, + NULL, NULL, NULL); + if(t->s_status != SERVICE_RUNNING) { if((r = service_start(t, INT32_MAX, 1)) != 0) { subscription_unsubscribe(s); diff --git a/src/subscriptions.h b/src/subscriptions.h index ff5d1d1d..0a88e5f5 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -65,6 +65,10 @@ typedef struct th_subscription { char *ths_username; char *ths_client; + // Ugly ugly ugly to refer DVB code here + + LIST_ENTRY(th_subscription) ths_tdmi_link; + struct th_dvb_mux_instance *ths_tdmi; } th_subscription_t; @@ -95,9 +99,14 @@ th_subscription_t *subscription_create_from_service(struct service *t, streaming_target_t *st, int flags); -void subscription_change_weight(th_subscription_t *s, int weight); +th_subscription_t *subscription_create(int weight, const char *name, + streaming_target_t *st, + int flags, st_callback_t *cb, + const char *hostname, + const char *username, + const char *client); -void subscription_stop(th_subscription_t *s); +void subscription_change_weight(th_subscription_t *s, int weight); void subscription_unlink_service(th_subscription_t *s, int reason); diff --git a/src/webui/webui.c b/src/webui/webui.c index 26ac8c63..05e85df9 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -42,6 +42,8 @@ #include "plumbing/globalheaders.h" #include "epg.h" #include "muxer.h" +#include "dvb/dvb.h" +#include "dvb/dvb_support.h" /** * @@ -264,6 +266,76 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } + +/** + * HTTP stream loop + */ +static void +http_stream_run2(http_connection_t *hc, streaming_queue_t *sq) +{ + streaming_message_t *sm; + int run = 1; + int timeouts = 0; + struct timespec ts; + struct timeval tp; + int err = 0; + socklen_t errlen = sizeof(err); + + /* reduce timeout on write() for streaming */ + tp.tv_sec = 5; + tp.tv_usec = 0; + setsockopt(hc->hc_fd, SOL_SOCKET, SO_SNDTIMEO, &tp, sizeof(tp)); + http_output_content(hc, "application/octet-stream"); + + while(run) { + pthread_mutex_lock(&sq->sq_mutex); + sm = TAILQ_FIRST(&sq->sq_queue); + if(sm == NULL) { + gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec + 1; + ts.tv_nsec = tp.tv_usec * 1000; + + if(pthread_cond_timedwait(&sq->sq_cond, &sq->sq_mutex, &ts) == ETIMEDOUT) { + timeouts++; + + //Check socket status + getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); + if(err) { + tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); + run = 0; + }else if(timeouts >= 20) { + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, timeout waiting for packets", hc->hc_url_orig); + run = 0; + } + } + pthread_mutex_unlock(&sq->sq_mutex); + continue; + } + + timeouts = 0; //Reset timeout counter + TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); + pthread_mutex_unlock(&sq->sq_mutex); + + pktbuf_t *pb; + + switch(sm->sm_type) { + case SMT_MPEGTS: + pb = sm->sm_data; + if(write(hc->hc_fd, pb->pb_data, pb->pb_size) != pb->pb_size) { + tvhlog(LOG_DEBUG, "webui", "Write error %s, stopping", hc->hc_url_orig); + run = 0; + } + break; + default: + break; + } + + streaming_msg_free(sm); + } +} + + + /** * Output a playlist containing a single channel */ @@ -596,6 +668,34 @@ http_stream_service(http_connection_t *hc, service_t *service) } +/** + * Subscribes to a service and starts the streaming loop + */ +static int +http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) +{ + th_subscription_t *s; + streaming_queue_t sq; + + streaming_queue_init(&sq, SMT_PACKET); + + pthread_mutex_lock(&global_lock); + s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st); + pthread_mutex_unlock(&global_lock); + + http_stream_run2(hc, &sq); + + + pthread_mutex_lock(&global_lock); + subscription_unsubscribe(s); + pthread_mutex_unlock(&global_lock); + + streaming_queue_deinit(&sq); + + return 0; +} + + /** * Subscribes to a channel and starts the streaming loop */ @@ -668,6 +768,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) * Handle the http request. http://tvheadend/stream/channelid/ * http://tvheadend/stream/channel/ * http://tvheadend/stream/service/ + * http://tvheadend/stream/mux/ */ static int http_stream(http_connection_t *hc, const char *remain, void *opaque) @@ -675,6 +776,7 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) char *components[2]; channel_t *ch = NULL; service_t *service = NULL; + th_dvb_mux_instance_t *tdmi = NULL; hc->hc_keep_alive = 0; @@ -698,14 +800,19 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) ch = channel_find_by_name(components[1], 0, 0); } else if(!strcmp(components[0], "service")) { service = service_find_by_identifier(components[1]); + } else if(!strcmp(components[0], "mux")) { + tdmi = dvb_mux_find_by_identifier(components[1]); } + // bug here: We can't retain pointers to channels etc outside global_lock pthread_mutex_unlock(&global_lock); if(ch != NULL) { return http_stream_channel(hc, ch); } else if(service != NULL) { return http_stream_service(hc, service); + } else if(tdmi != NULL) { + return http_stream_tdmi(hc, tdmi); } else { http_error(hc, HTTP_STATUS_BAD_REQUEST); return HTTP_STATUS_BAD_REQUEST; From da06305864d854fe3b8bc8f173063e12bb222fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 23 Oct 2012 09:12:56 +0000 Subject: [PATCH 051/503] Fix incorrect bit arithmetic causing service restart to not correctly trig --- src/psi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psi.c b/src/psi.c index dcfec72a..dca98658 100644 --- a/src/psi.c +++ b/src/psi.c @@ -690,7 +690,7 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, service_request_save(t, 0); // Only restart if something that our clients worry about did change - if(update & !(PMT_UPDATE_NEW_CA_STREAM | + if(update & ~(PMT_UPDATE_NEW_CA_STREAM | PMT_UPDATE_NEW_CAID | PMT_UPDATE_CA_PROVIDER_CHANGE | PMT_UPDATE_CAID_DELETED)) { From d97f19f96375f4754969b1ba92e0b0a9eb11759e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 23 Oct 2012 09:13:59 +0000 Subject: [PATCH 052/503] rawtsinput: Handle stream delete --- src/psi.c | 4 ++++ src/rawtsinput.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/psi.c b/src/psi.c index dca98658..9763a50a 100644 --- a/src/psi.c +++ b/src/psi.c @@ -468,6 +468,10 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, /* Mark all streams for deletion */ if(delete) { TAILQ_FOREACH(st, &t->s_components, es_link) { + + if(st->es_type == SCT_PMT) + continue; + st->es_delete_me = 1; LIST_FOREACH(c, &st->es_caids, link) diff --git a/src/rawtsinput.c b/src/rawtsinput.c index e22ac146..a947de56 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -164,7 +164,7 @@ got_pmt(struct service *t, elementary_stream_t *st, return; pthread_mutex_lock(&global_lock); - psi_parse_pmt(t, table + 3, table_len - 3, 1, 0); + psi_parse_pmt(t, table + 3, table_len - 3, 1, 1); pthread_mutex_unlock(&global_lock); } From 0634ac207d94fcd0bbe6b1896323cccda0c84dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 24 Oct 2012 11:28:11 +0200 Subject: [PATCH 053/503] dvb: Stay with filtered hw for now --- src/dvb/dvb_adapter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index b52df831..7320db18 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -452,7 +452,7 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - dvb_input_raw_setup(tda); + dvb_input_filtered_setup(tda); if(tda->tda_sat) dvb_satconf_init(tda); From 4727974500a77336475def1adeac997dfa3953de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 24 Oct 2012 12:58:47 +0200 Subject: [PATCH 054/503] HTSP: Include video frame duration in streamingStart message --- src/htsp.c | 7 +++++-- src/parser_h264.c | 20 +++++++++----------- src/parser_h264.h | 2 +- src/parsers.c | 34 +++++++++++++++------------------- src/parsers.h | 3 ++- src/psi.c | 5 +++++ src/service.c | 1 + 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/htsp.c b/src/htsp.c index 4225b774..507f4973 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -1880,9 +1880,12 @@ htsp_subscription_start(htsp_subscription_t *hs, const streaming_start_t *ss) if(ssc->ssc_type == SCT_MPEG2VIDEO || ssc->ssc_type == SCT_H264) { if(ssc->ssc_width) - htsmsg_add_u32(c, "width", ssc->ssc_width); + htsmsg_add_u32(c, "width", ssc->ssc_width); if(ssc->ssc_height) - htsmsg_add_u32(c, "height", ssc->ssc_height); + htsmsg_add_u32(c, "height", ssc->ssc_height); + if(ssc->ssc_frameduration) + htsmsg_add_u32(c, "duration", hs->hs_90khz ? ssc->ssc_frameduration : + ts_rescale(ssc->ssc_frameduration, 1000000)); if (ssc->ssc_aspect_num) htsmsg_add_u32(c, "aspect_num", ssc->ssc_aspect_num); if (ssc->ssc_aspect_den) diff --git a/src/parser_h264.c b/src/parser_h264.c index 07bed03b..67a82304 100644 --- a/src/parser_h264.c +++ b/src/parser_h264.c @@ -352,7 +352,7 @@ h264_decode_pic_parameter_set(elementary_stream_t *st, bitstream_t *bs) int h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype, - int *duration, int *isfield) + int *isfield) { h264_private_t *p; int slice_type, pps_id, sps_id; @@ -402,22 +402,20 @@ h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype, *isfield = field; - if(p->sps[sps_id].time_scale != 0) { - int d = timebase * p->sps[sps_id].units_in_tick / p->sps[sps_id].time_scale; - *duration = d; - } else { - *duration = 0; - } + int d = 0; + if(p->sps[sps_id].time_scale != 0) + d = timebase * p->sps[sps_id].units_in_tick / p->sps[sps_id].time_scale; if(p->sps[sps_id].cbpsize != 0) st->es_vbv_size = p->sps[sps_id].cbpsize; st->es_vbv_delay = -1; - if(p->sps[sps_id].width && p->sps[sps_id].height && !st->es_buf.sb_err) - parser_set_stream_vsize(st, p->sps[sps_id].width, - p->sps[sps_id].height * - (2 - p->sps[sps_id].mbs_only_flag)); + if(p->sps[sps_id].width && p->sps[sps_id].height && d && !st->es_buf.sb_err) + parser_set_stream_vparam(st, p->sps[sps_id].width, + p->sps[sps_id].height * + (2 - p->sps[sps_id].mbs_only_flag), + d); if(p->sps[sps_id].aspect_num && p->sps[sps_id].aspect_den) { diff --git a/src/parser_h264.h b/src/parser_h264.h index af92a569..5e78b436 100644 --- a/src/parser_h264.h +++ b/src/parser_h264.h @@ -28,6 +28,6 @@ int h264_decode_seq_parameter_set(struct elementary_stream *st, bitstream_t *bs) int h264_decode_pic_parameter_set(struct elementary_stream *st, bitstream_t *bs); int h264_decode_slice_header(struct elementary_stream *st, bitstream_t *bs, - int *pkttype, int *duration, int *isfield); + int *pkttype, int *isfield); #endif /* PARSER_H264_H_ */ diff --git a/src/parsers.c b/src/parsers.c index 9b4768d9..9adc8b20 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -904,16 +904,18 @@ parse_mpeg2video_pic_start(service_t *t, elementary_stream_t *st, int *frametype * */ void -parser_set_stream_vsize(elementary_stream_t *st, int width, int height) +parser_set_stream_vparam(elementary_stream_t *st, int width, int height, + int duration) { int need_save = 0; - if(st->es_width == 0 && st->es_height == 0) { + if(st->es_width == 0 && st->es_height == 0 && st->es_frame_duration == 0) { need_save = 1; st->es_meta_change = 0; - } else if(st->es_width != width || st->es_height != height) { - + } else if(st->es_width != width || st->es_height != height || + st->es_frame_duration != duration) { + st->es_meta_change++; if(st->es_meta_change == 2) need_save = 1; @@ -925,6 +927,7 @@ parser_set_stream_vsize(elementary_stream_t *st, int width, int height) if(need_save) { st->es_width = width; st->es_height = height; + st->es_frame_duration = duration; service_request_save(st->es_service, 1); } } @@ -969,7 +972,7 @@ parse_mpeg2video_seq_start(service_t *t, elementary_stream_t *st, st->es_aspect_num = mpeg2_aspect[aspect][0]; st->es_aspect_den = mpeg2_aspect[aspect][1]; - st->es_frame_duration = mpeg2video_framedurations[read_bits(bs, 4)]; + int duration = mpeg2video_framedurations[read_bits(bs, 4)]; v = read_bits(bs, 18) * 400; skip_bits(bs, 1); @@ -977,7 +980,7 @@ parse_mpeg2video_seq_start(service_t *t, elementary_stream_t *st, v = read_bits(bs, 10) * 16 * 1024 / 8; st->es_vbv_size = v; - parser_set_stream_vsize(st, width, height); + parser_set_stream_vparam(st, width, height, duration); return 0; } @@ -1157,8 +1160,7 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len, { const uint8_t *buf = st->es_buf.sb_data + sc_offset; uint32_t sc = st->es_startcode; - int64_t d; - int l2, pkttype, duration, isfield; + int l2, pkttype, isfield; bitstream_t bs; int ret = 0; @@ -1169,12 +1171,6 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len, if(plen >= 0xffe9) st->es_incomplete =1; parse_pes_header(t, st, buf + 6, len - 6); } - if(st->es_prevdts != PTS_UNSET && st->es_curdts != PTS_UNSET) { - d = (st->es_curdts - st->es_prevdts) & 0x1ffffffffLL; - - if(d < 90000) - st->es_frame_duration = d; - } st->es_prevdts = st->es_curdts; return 1; } @@ -1211,23 +1207,23 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len, case 5: /* IDR+SLICE */ case 1: - if(st->es_curpkt != NULL || st->es_frame_duration == 0) - break; - l2 = len - 3 > 64 ? 64 : len - 3; void *f = h264_nal_deescape(&bs, buf + 3, l2); /* we just want the first stuff */ - if(h264_decode_slice_header(st, &bs, &pkttype, &duration, &isfield)) { + if(h264_decode_slice_header(st, &bs, &pkttype, &isfield)) { free(f); return 1; } free(f); + if(st->es_curpkt != NULL || st->es_frame_duration == 0) + break; + st->es_curpkt = pkt_alloc(NULL, 0, st->es_curpts, st->es_curdts); st->es_curpkt->pkt_frametype = pkttype; st->es_curpkt->pkt_field = isfield; - st->es_curpkt->pkt_duration = duration ?: st->es_frame_duration; + st->es_curpkt->pkt_duration = st->es_frame_duration; st->es_curpkt->pkt_commercial = t->s_tt_commercial_advice; break; diff --git a/src/parsers.h b/src/parsers.h index 947b6fd6..826daaed 100644 --- a/src/parsers.h +++ b/src/parsers.h @@ -31,7 +31,8 @@ void parse_mpeg_ps(struct service *t, struct elementary_stream *st, void parser_enqueue_packet(struct service *t, struct elementary_stream *st, th_pkt_t *pkt); -void parser_set_stream_vsize(struct elementary_stream *st, int width, int height); +void parser_set_stream_vparam(struct elementary_stream *st, int width, int height, + int duration); extern const unsigned int mpeg2video_framedurations[16]; diff --git a/src/psi.c b/src/psi.c index 9763a50a..ff298fad 100644 --- a/src/psi.c +++ b/src/psi.c @@ -998,6 +998,8 @@ psi_save_service_settings(htsmsg_t *m, service_t *t) htsmsg_add_u32(sub, "width", st->es_width); htsmsg_add_u32(sub, "height", st->es_height); } + if(st->es_frame_duration) + htsmsg_add_u32(sub, "duration", st->es_frame_duration); } htsmsg_add_msg(m, "stream", sub); @@ -1141,6 +1143,9 @@ psi_load_service_settings(htsmsg_t *m, service_t *t) if(!htsmsg_get_u32(c, "height", &u32)) st->es_height = u32; + + if(!htsmsg_get_u32(c, "duration", &u32)) + st->es_frame_duration = u32; } } diff --git a/src/service.c b/src/service.c index 1f8fe565..07805d3e 100644 --- a/src/service.c +++ b/src/service.c @@ -902,6 +902,7 @@ service_build_stream_start(service_t *t) ssc->ssc_pid = st->es_pid; ssc->ssc_width = st->es_width; ssc->ssc_height = st->es_height; + ssc->ssc_frameduration = st->es_frame_duration; } t->s_setsourceinfo(t, &ss->ss_si); From 9bf78ac40588595188a1c0e6cfdb45fda0902ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 24 Oct 2012 11:34:18 +0100 Subject: [PATCH 055/503] dvb: For full mux streaming, send more than one TS block in each packet --- src/dvb/dvb_adapter.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 7320db18..49db8b34 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -854,23 +854,27 @@ dvb_adapter_input_dvr(void *aux) int wakeup_table_feed = 0; // Just wanna wakeup once + pthread_mutex_lock(&tda->tda_delivery_mutex); + + if(LIST_FIRST(&tda->tda_streaming_pad.sp_targets) != NULL) { + streaming_message_t sm; + pktbuf_t *pb = pktbuf_alloc(tsb, r); + memset(&sm, 0, sizeof(sm)); + sm.sm_type = SMT_MPEGTS; + sm.sm_data = pb; + streaming_pad_deliver(&tda->tda_streaming_pad, &sm); + pktbuf_ref_dec(pb); + } + + + /* Process */ while (r >= 188) { /* sync */ if (tsb[i] == 0x47) { - if(LIST_FIRST(&tda->tda_streaming_pad.sp_targets) != NULL) { - streaming_message_t sm; - pktbuf_t *pb = pktbuf_alloc(tsb, 188); - memset(&sm, 0, sizeof(sm)); - sm.sm_type = SMT_MPEGTS; - sm.sm_data = pb; - streaming_pad_deliver(&tda->tda_streaming_pad, &sm); - pktbuf_ref_dec(pb); - } - if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; From e3f4768e5cccd67dcef84d4e8e9b71d13dd470d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 25 Oct 2012 08:52:35 +0200 Subject: [PATCH 056/503] dvb: Stub the raw_setup() --- src/dvb/dvb_adapter.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 49db8b34..8de17673 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -452,7 +452,12 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - dvb_input_filtered_setup(tda); + if(0) { + // TBD + dvb_input_raw_setup(tda); + } else { + dvb_input_filtered_setup(tda); + } if(tda->tda_sat) dvb_satconf_init(tda); From 47049eaa2124e1ff4a01d9c73fd402e61c744b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 25 Oct 2012 09:51:16 +0200 Subject: [PATCH 057/503] dvb: Run adapters in full mux mode if we can --- src/dvb/dvb_adapter.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8de17673..650321f3 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -374,7 +374,38 @@ dvb_adapter_checkspeed(th_dvb_adapter_t *tda) snprintf(dev, sizeof(dev), "dvb/dvb%d.dvr0", tda->tda_adapter_num); tda->tda_hostconnection = get_device_connection(dev); - } +} + + + + +/** + * Return 1 if an adapter is capable of receiving a full mux + */ +static int +check_full_stream(th_dvb_adapter_t *tda) +{ + struct dmx_pes_filter_params dmx_param; + int r; + + if(tda->tda_hostconnection == HOSTCONNECTION_USB12) + return 0; // Don't even bother, device <-> host interface is too slow + + int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(fd == -1) + return 0; + + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = 0x2000; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + r = ioctl(fd, DMX_SET_PES_FILTER, &dmx_param); + close(fd); + return !r; +} /** @@ -452,10 +483,11 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - if(0) { - // TBD + if(check_full_stream(tda)) { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", path); dvb_input_raw_setup(tda); } else { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", path); dvb_input_filtered_setup(tda); } From 0bde079fda0ea0d1178426c36fa21a06fd756a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 25 Oct 2012 09:53:12 +0200 Subject: [PATCH 058/503] http: Let full mux dump use the normal straeming code Fix some locking issues in the http streaming code --- src/muxer_pass.c | 25 +++++----- src/webui/webui.c | 115 +++++++--------------------------------------- 2 files changed, 29 insertions(+), 111 deletions(-) diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 73053438..20a986a7 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -107,7 +107,8 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) pass_muxer_t *pm = (pass_muxer_t*)m; const source_info_t *si = &ss->ss_si; - if(si->si_type == S_MPEG_TS) { + if(si->si_type == S_MPEG_TS && ss->ss_pmt_pid) { + pm->pm_pat = realloc(pm->pm_pat, 188); memset(pm->pm_pat, 0xff, 188); pm->pm_pat[0] = 0x47; pm->pm_pat[1] = 0x40; @@ -120,6 +121,7 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) return -1; } + pm->pm_pmt = realloc(pm->pm_pmt, 188); memset(pm->pm_pmt, 0xff, 188); pm->pm_pmt[0] = 0x47; pm->pm_pmt[1] = 0x40 | (ss->ss_pmt_pid >> 8); @@ -216,14 +218,16 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) pass_muxer_t *pm = (pass_muxer_t*)m; int rem; - // Inject pmt and pat into the stream - rem = pm->pm_pc % TS_INJECTION_RATE; - if(!rem) { - pm->pm_pat[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); - pm->pm_pmt[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); - pass_muxer_write(m, pm->pm_pmt, 188); - pass_muxer_write(m, pm->pm_pat, 188); - pm->pm_ic++; + if(pm->pm_pat != NULL) { + // Inject pmt and pat into the stream + rem = pm->pm_pc % TS_INJECTION_RATE; + if(!rem) { + pm->pm_pat[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); + pm->pm_pmt[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); + pass_muxer_write(m, pm->pm_pmt, 188); + pass_muxer_write(m, pm->pm_pat, 188); + pm->pm_ic++; + } } pass_muxer_write(m, pb->pb_data, pb->pb_size); @@ -331,9 +335,6 @@ pass_muxer_create(muxer_container_type_t mc) pm->m_close = pass_muxer_close; pm->m_destroy = pass_muxer_destroy; - pm->pm_pat = malloc(188); - pm->pm_pmt = malloc(188); - return (muxer_t *)pm; } diff --git a/src/webui/webui.c b/src/webui/webui.c index 05e85df9..7919f040 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -142,8 +142,8 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) * HTTP stream loop */ static void -http_stream_run(http_connection_t *hc, streaming_queue_t *sq, - th_subscription_t *s, muxer_container_type_t mc) +http_stream_run(http_connection_t *hc, streaming_queue_t *sq, + const char *name, muxer_container_type_t mc) { streaming_message_t *sm; int run = 1; @@ -154,17 +154,11 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, struct timeval tp; int err = 0; socklen_t errlen = sizeof(err); - const char *name; mux = muxer_create(mc); if(muxer_open_stream(mux, hc->hc_fd)) run = 0; - if(s->ths_channel) - name = s->ths_channel->ch_name; - else - name = "Live Stream"; - /* reduce timeout on write() for streaming */ tp.tv_sec = 5; tp.tv_usec = 0; @@ -266,76 +260,6 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } - -/** - * HTTP stream loop - */ -static void -http_stream_run2(http_connection_t *hc, streaming_queue_t *sq) -{ - streaming_message_t *sm; - int run = 1; - int timeouts = 0; - struct timespec ts; - struct timeval tp; - int err = 0; - socklen_t errlen = sizeof(err); - - /* reduce timeout on write() for streaming */ - tp.tv_sec = 5; - tp.tv_usec = 0; - setsockopt(hc->hc_fd, SOL_SOCKET, SO_SNDTIMEO, &tp, sizeof(tp)); - http_output_content(hc, "application/octet-stream"); - - while(run) { - pthread_mutex_lock(&sq->sq_mutex); - sm = TAILQ_FIRST(&sq->sq_queue); - if(sm == NULL) { - gettimeofday(&tp, NULL); - ts.tv_sec = tp.tv_sec + 1; - ts.tv_nsec = tp.tv_usec * 1000; - - if(pthread_cond_timedwait(&sq->sq_cond, &sq->sq_mutex, &ts) == ETIMEDOUT) { - timeouts++; - - //Check socket status - getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); - if(err) { - tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); - run = 0; - }else if(timeouts >= 20) { - tvhlog(LOG_WARNING, "webui", "Stop streaming %s, timeout waiting for packets", hc->hc_url_orig); - run = 0; - } - } - pthread_mutex_unlock(&sq->sq_mutex); - continue; - } - - timeouts = 0; //Reset timeout counter - TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); - pthread_mutex_unlock(&sq->sq_mutex); - - pktbuf_t *pb; - - switch(sm->sm_type) { - case SMT_MPEGTS: - pb = sm->sm_data; - if(write(hc->hc_fd, pb->pb_data, pb->pb_size) != pb->pb_size) { - tvhlog(LOG_DEBUG, "webui", "Write error %s, stopping", hc->hc_url_orig); - run = 0; - } - break; - default: - break; - } - - streaming_msg_free(sm); - } -} - - - /** * Output a playlist containing a single channel */ @@ -618,7 +542,8 @@ http_stream_service(http_connection_t *hc, service_t *service) muxer_container_type_t mc; int flags; const char *str; - size_t qsize ; + size_t qsize; + const char *name; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -645,15 +570,14 @@ http_stream_service(http_connection_t *hc, service_t *service) flags = 0; } - pthread_mutex_lock(&global_lock); s = subscription_create_from_service(service, "HTTP", st, flags); - pthread_mutex_unlock(&global_lock); - if(s) { - http_stream_run(hc, &sq, s, mc); + name = strdupa(service->s_ch ? + service->s_ch->ch_name : service->s_nicename); + pthread_mutex_unlock(&global_lock); + http_stream_run(hc, &sq, name, mc); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); } if(gh) @@ -676,19 +600,15 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) { th_subscription_t *s; streaming_queue_t sq; - + const char *name; streaming_queue_init(&sq, SMT_PACKET); - pthread_mutex_lock(&global_lock); s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st); + name = strdupa(tdmi->tdmi_identifier); pthread_mutex_unlock(&global_lock); - - http_stream_run2(hc, &sq); - - + http_stream_run(hc, &sq, name, MC_PASS); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); streaming_queue_deinit(&sq); @@ -713,6 +633,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) muxer_container_type_t mc; char *str; size_t qsize; + const char *name; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -739,18 +660,17 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) flags = 0; } - pthread_mutex_lock(&global_lock); s = subscription_create_from_channel(ch, priority, "HTTP", st, flags, inet_ntoa(hc->hc_peer->sin_addr), hc->hc_username, http_arg_get(&hc->hc_args, "User-Agent")); - pthread_mutex_unlock(&global_lock); if(s) { - http_stream_run(hc, &sq, s, mc); + name = strdupa(ch->ch_name); + pthread_mutex_unlock(&global_lock); + http_stream_run(hc, &sq, name, mc); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); } if(gh) @@ -792,7 +712,7 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) http_deescape(components[1]); - pthread_mutex_lock(&global_lock); + scopedgloballock(); if(!strcmp(components[0], "channelid")) { ch = channel_find_by_identifier(atoi(components[1])); @@ -804,9 +724,6 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) tdmi = dvb_mux_find_by_identifier(components[1]); } - // bug here: We can't retain pointers to channels etc outside global_lock - pthread_mutex_unlock(&global_lock); - if(ch != NULL) { return http_stream_channel(hc, ch); } else if(service != NULL) { From c8e56a2865b3fea681511910f7764a6b27e57749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 25 Oct 2012 11:51:03 +0200 Subject: [PATCH 059/503] Plug some memory leaks --- src/epg.c | 10 ++++++++-- src/epggrab/otamux.c | 2 ++ src/main.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/epg.c b/src/epg.c index 58009eb8..3a658b27 100644 --- a/src/epg.c +++ b/src/epg.c @@ -990,6 +990,7 @@ int epg_episode_set_genre g2 = LIST_NEXT(g1, link); if (!epg_genre_list_contains(genre, g1, 0)) { LIST_REMOVE(g1, link); + free(g1); save = 1; } g1 = g2; @@ -1839,10 +1840,15 @@ epg_broadcast_t *epg_broadcast_deserialize if (!htsmsg_get_u32(m, "is_repeat", &u32)) *save |= epg_broadcast_set_is_repeat(ebc, u32, NULL); - if ((ls = lang_str_deserialize(m, "summary"))) + if ((ls = lang_str_deserialize(m, "summary"))) { *save |= epg_broadcast_set_summary2(ebc, ls, NULL); - if ((ls = lang_str_deserialize(m, "description"))) + lang_str_destroy(ls); + } + + if ((ls = lang_str_deserialize(m, "description"))) { *save |= epg_broadcast_set_description2(ebc, ls, NULL); + lang_str_destroy(ls); + } /* Series link */ if ((str = htsmsg_get_str(m, "serieslink"))) diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 80ace9d2..c4242079 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -136,6 +136,7 @@ void epggrab_ota_load ( void ) if ((l = htsmsg_get_list_by_field(f))) _epggrab_ota_load_one((epggrab_module_ota_t*)mod, l); } + htsmsg_destroy(m); } } @@ -171,6 +172,7 @@ void epggrab_ota_save ( void ) } hts_settings_save(m, "epggrab/otamux"); + htsmsg_destroy(m); } /* ************************************************************************** diff --git a/src/main.c b/src/main.c index 263bb6ce..1a6f4540 100644 --- a/src/main.c +++ b/src/main.c @@ -272,7 +272,7 @@ main(int argc, char **argv) htsp_port = 9982; /* Get current directory */ - tvheadend_cwd = dirname(dirname(strdup(argv[0]))); + tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); From 64b2e51c4260f68ccae870e48f674c6992317613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 25 Oct 2012 13:25:06 +0200 Subject: [PATCH 060/503] HTSP: Rename htsp.c to htsp_server.c --- Makefile | 2 +- src/channels.c | 2 +- src/dvr/dvr_db.c | 2 +- src/epg.c | 2 +- src/{htsp.c => htsp_server.c} | 2 +- src/{htsp.h => htsp_server.h} | 0 src/main.c | 2 +- src/service.c | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename src/{htsp.c => htsp_server.c} (99%) rename src/{htsp.h => htsp_server.h} (100%) diff --git a/Makefile b/Makefile index e8d3e37e..b1ed4b52 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ SRCS = src/main.c \ src/parser_latm.c \ src/tsdemux.c \ src/bitstream.c \ - src/htsp.c \ + src/htsp_server.c \ src/serviceprobe.c \ src/htsmsg.c \ src/htsmsg_binary.c \ diff --git a/src/channels.c b/src/channels.c index 686c3b16..d5f05b29 100644 --- a/src/channels.c +++ b/src/channels.c @@ -38,7 +38,7 @@ #include "dtable.h" #include "notify.h" #include "dvr/dvr.h" -#include "htsp.h" +#include "htsp_server.h" struct channel_tree channel_name_tree; static struct channel_tree channel_identifier_tree; diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 1edb2bc9..b26b51ce 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -27,7 +27,7 @@ #include "tvheadend.h" #include "dvr.h" #include "notify.h" -#include "htsp.h" +#include "htsp_server.h" #include "streaming.h" static int de_tally; diff --git a/src/epg.c b/src/epg.c index 3a658b27..c123a7b2 100644 --- a/src/epg.c +++ b/src/epg.c @@ -30,7 +30,7 @@ #include "settings.h" #include "epg.h" #include "dvr/dvr.h" -#include "htsp.h" +#include "htsp_server.h" #include "epggrab.h" /* Broadcast hashing */ diff --git a/src/htsp.c b/src/htsp_server.c similarity index 99% rename from src/htsp.c rename to src/htsp_server.c index 507f4973..450ff193 100644 --- a/src/htsp.c +++ b/src/htsp_server.c @@ -35,7 +35,7 @@ #include "tcp.h" #include "packet.h" #include "access.h" -#include "htsp.h" +#include "htsp_server.h" #include "streaming.h" #include "psi.h" #include "htsmsg_binary.h" diff --git a/src/htsp.h b/src/htsp_server.h similarity index 100% rename from src/htsp.h rename to src/htsp_server.h diff --git a/src/main.c b/src/main.c index 1a6f4540..4fe455cd 100644 --- a/src/main.c +++ b/src/main.c @@ -49,7 +49,7 @@ #include "cwc.h" #include "capmt.h" #include "dvr/dvr.h" -#include "htsp.h" +#include "htsp_server.h" #include "rawtsinput.h" #include "avahi.h" #include "iptv_input.h" diff --git a/src/service.c b/src/service.c index 07805d3e..db8f570d 100644 --- a/src/service.c +++ b/src/service.c @@ -46,7 +46,7 @@ #include "serviceprobe.h" #include "atomic.h" #include "dvb/dvb.h" -#include "htsp.h" +#include "htsp_server.h" #include "lang_codes.h" #define SERVICE_HASH_WIDTH 101 From 174bc64c69dcf6bd87f7b6708fe687f84348785b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 25 Oct 2012 19:37:34 +0200 Subject: [PATCH 061/503] HTTP: make sure we don't send (and deref) packets before the mime type etc has been send --- src/webui/webui.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index 7919f040..35ffce49 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -196,8 +196,10 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: - muxer_write_pkt(mux, sm->sm_type, sm->sm_data); - sm->sm_data = NULL; + if(started) { + muxer_write_pkt(mux, sm->sm_type, sm->sm_data); + sm->sm_data = NULL; + } break; case SMT_START: From aecded7c4e93189c6abbb741b2bd3c10b76fe31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 27 Oct 2012 19:14:39 +0200 Subject: [PATCH 062/503] fix timing issues with rawtsinput (file input). --- src/rawtsinput.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rawtsinput.c b/src/rawtsinput.c index a947de56..bd4a5098 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -267,7 +267,7 @@ process_ts_packet(rawts_t *rt, uint8_t *tsb) slp.tv_sec = d / 1000000; slp.tv_nsec = (d % 1000000) * 1000; - clock_nanosleep(CLOCK_MONOTONIC_COARSE, TIMER_ABSTIME, &slp, NULL); + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &slp, NULL); didsleep = 1; } t->s_pcr_last = pcr; From 2ac1b249df62a5ba6220ea3e428b09d41b61fc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 30 Oct 2012 10:36:43 +0100 Subject: [PATCH 063/503] Don't pass uninitialized mem to epoll_ctl() --- src/dvb/dvb_adapter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 650321f3..6250363d 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -863,6 +863,7 @@ dvb_adapter_input_dvr(void *aux) /* Create poll */ efd = epoll_create(2); + memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = fd; epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); From b1760d684f0ba7e6097ce121ce8c0c167185078f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 30 Oct 2012 10:37:04 +0100 Subject: [PATCH 064/503] HTSP: Add possibility to subscribe to a channelName --- src/htsp_server.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 450ff193..d1f65f60 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1112,16 +1112,21 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) uint32_t chid, sid, weight, req90khz, normts; channel_t *ch; htsp_subscription_t *hs; - - if(htsmsg_get_u32(in, "channelId", &chid)) - return htsp_error("Missing argument 'channelId'"); - + const char *str; if(htsmsg_get_u32(in, "subscriptionId", &sid)) return htsp_error("Missing argument 'subscriptionId'"); - if((ch = channel_find_by_identifier(chid)) == NULL) - return htsp_error("Requested channel does not exist"); + if(!htsmsg_get_u32(in, "channelId", &chid)) { + if((ch = channel_find_by_identifier(chid)) == NULL) + return htsp_error("Requested channel does not exist"); + } else if((str = htsmsg_get_str(in, "channelName")) != NULL) { + if((ch = channel_find_by_name(str, 0, 0)) == NULL) + return htsp_error("Requested channel does not exist"); + + } else { + return htsp_error("Missing argument 'channelId' or 'channelName'"); + } weight = htsmsg_get_u32_or_default(in, "weight", 150); req90khz = htsmsg_get_u32_or_default(in, "90khz", 0); normts = htsmsg_get_u32_or_default(in, "normts", 0); From f83df17715d88a92e6b094d6321629597030c805 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 24 Oct 2012 21:09:38 +0100 Subject: [PATCH 065/503] Added quantal and removed hardy from latest dev builds. --- support/launchpad-ppa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/launchpad-ppa b/support/launchpad-ppa index cf3c0762..c3f5936e 100755 --- a/support/launchpad-ppa +++ b/support/launchpad-ppa @@ -17,7 +17,7 @@ CMD=$(basename $0) # Configuration TVH_ROOT=$(cd $(dirname $0)/..; pwd) -[ -z "$TVH_DIST" ] && TVH_DIST="hardy lucid natty oneiric precise wheezy" +[ -z "$TVH_DIST" ] && TVH_DIST="lucid natty oneiric precise quantal" [ -z "$TVH_ARCH" ] && TVH_ARCH="i386 amd64" # Options From d761985f3b6c0002e5d4f39c6ded3d00a0d57ed9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 30 Oct 2012 17:27:54 +0000 Subject: [PATCH 066/503] Add protection to stop opentv crashing on bad title descriptor. Relates to #1367. --- src/epggrab/module/opentv.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 0778ca82..e1fd6ee3 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -217,6 +217,8 @@ static char *_opentv_parse_string int ok = 0; char *ret, *tmp; + if (len <= 0) return NULL; + // Note: unlikely decoded string will be longer (though its possible) ret = tmp = malloc(2*len); *ret = 0; @@ -248,15 +250,17 @@ static int _opentv_parse_event_record if (rlen+2 <= len) { switch (rtag) { case 0xb5: // title - ev->start = (((int)buf[2] << 9) | (buf[3] << 1)) - + mjd; - ev->stop = (((int)buf[4] << 9) | (buf[5] << 1)) - + ev->start; - ev->cat = buf[6]; - if (prov->genre) - ev->cat = prov->genre->map[ev->cat]; - if (!ev->title) - ev->title = _opentv_parse_string(prov, buf+9, rlen-7); + if (rlen >= 7) { + ev->start = (((int)buf[2] << 9) | (buf[3] << 1)) + + mjd; + ev->stop = (((int)buf[4] << 9) | (buf[5] << 1)) + + ev->start; + ev->cat = buf[6]; + if (prov->genre) + ev->cat = prov->genre->map[ev->cat]; + if (!ev->title) + ev->title = _opentv_parse_string(prov, buf+9, rlen-7); + } break; case 0xb9: // summary if (!ev->summary) From 6b2429bad7d275676084c1eb2baa810f6273a22d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 31 Oct 2012 10:34:13 +0000 Subject: [PATCH 067/503] Fix mistake in dd_progid parsing. --- src/epggrab/module/xmltv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 18c53ef8..55f0bc62 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -208,7 +208,10 @@ static void parse_xmltv_dd_progid snprintf(buf, sizeof(buf)-1, "ddprogid://%s/%s", mod->id, s); /* SH - series without episode id so ignore */ - if (strncmp("SH", s, 2)) *uri = strdup(buf); + if (strncmp("SH", s, 2)) + *uri = strdup(buf); + else + *suri = strdup(buf); /* Episode */ if (!strncmp("EP", s, 2)) { From a3a917cc2947822abd09f57bbabe4620f2b4271c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 31 Oct 2012 13:17:07 +0000 Subject: [PATCH 068/503] Ref #1352 - check return value of setuid/setgid calls. Also slightly changed the logic so its possible to fork as non-root, though you must explicitly list your username and group with -u and -g as I do not want to break built in defaults for compatibility. --- src/main.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/main.c b/src/main.c index 4fe455cd..580a151c 100644 --- a/src/main.c +++ b/src/main.c @@ -270,6 +270,8 @@ main(int argc, char **argv) int crash = 0; webui_port = 9981; htsp_port = 9982; + gid_t gid; + uid_t uid; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); @@ -354,34 +356,52 @@ main(int argc, char **argv) signal(SIGPIPE, handle_sigpipe); + log_stderr = 1; + log_decorate = isatty(2); + if(forkaway) { grp = getgrnam(groupnam ?: "video"); pw = usernam ? getpwnam(usernam) : NULL; - if(daemon(0, 0)) { - exit(2); - } pidfile = fopen(pidpath, "w+"); - if(pidfile != NULL) { - fprintf(pidfile, "%d\n", getpid()); - fclose(pidfile); - } if(grp != NULL) { - setgid(grp->gr_gid); + gid = grp->gr_gid; } else { - setgid(1); + gid = 1; } if (pw != NULL) { - gid_t glist[10]; - int gnum = get_user_groups(pw, glist, 10); - setgroups(gnum, glist); - setuid(pw->pw_uid); + if (getuid() != pw->pw_uid) { + gid_t glist[10]; + int gnum; + gnum = get_user_groups(pw, glist, 10); + if (setgroups(gnum, glist)) { + tvhlog(LOG_ALERT, "START", "setgroups() failed, do you have permission?"); + return 1; + } + } + uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { - setuid(1); + uid = 1; + } + if ((getgid() != gid) && setgid(gid)) { + tvhlog(LOG_ALERT, "START", "setgid() failed, do you have permission?"); + return 1; + } + if ((getuid() != uid) && setuid(uid)) { + tvhlog(LOG_ALERT, "START", "setuid() failed, do you have permission?"); + return 1; + } + + if(daemon(0, 0)) { + exit(2); + } + if(pidfile != NULL) { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); } umask(0); From 31b1d5b57d0148bad30861f74ba10b3f385a9a73 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 31 Oct 2012 13:21:36 +0000 Subject: [PATCH 069/503] Refs #1226 - remove block to creating empty channels. --- src/channels.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/channels.c b/src/channels.c index d5f05b29..7c8112ec 100644 --- a/src/channels.c +++ b/src/channels.c @@ -210,14 +210,14 @@ channel_find_by_name(const char *name, int create, int channel_number) { channel_t skel, *ch; - if (!name || !*name) return NULL; - lock_assert(&global_lock); - skel.ch_name = (char *)name; - ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp); - if(ch != NULL || create == 0) - return ch; + if (name) { + skel.ch_name = (char *)name; + ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp); + if(ch != NULL || create == 0) + return ch; + } return channel_create2(name, channel_number); } From fbe2db07101806acdfe9a667ff9daf6766b1a199 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 31 Oct 2012 13:40:35 +0000 Subject: [PATCH 070/503] Refs #1355, Refs #1356 - Fix problem with DVR dup detect. I have completely removed global duplicate detection at this stage until such time as I can do things properly. This means a user can ALWAYS manually override and force a recording of a show. For now duplicate detection only exists within the context or a series link. So it will only record the "first?" instance of a given episode in a give series link. This is still a bit of a hack until I provide the user with the ability to control the configuration and implement all the hooks properly. --- src/dvr/dvr_autorec.c | 8 ++------ src/dvr/dvr_db.c | 13 ------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index e4902d92..4eff0435 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -562,14 +562,10 @@ void dvr_autorec_check_event(epg_broadcast_t *e) { dvr_autorec_entry_t *dae; - dvr_entry_t *existingde; TAILQ_FOREACH(dae, &autorec_entries, dae_link) - if(autorec_cmp(dae, e)) { - existingde = dvr_entry_find_by_event_fuzzy(e); - if (existingde == NULL) - dvr_entry_create_by_autorec(e, dae); - } + if(autorec_cmp(dae, e)) + dvr_entry_create_by_autorec(e, dae); // Note: no longer updating event here as it will be done from EPG // anyway } diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index b26b51ce..fe53120e 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -258,19 +258,6 @@ static dvr_entry_t *_dvr_entry_create ( if(de->de_start == start && de->de_sched_state != DVR_COMPLETED) return NULL; - /* Reject duplicate episodes (unless earlier) */ - if (e && cfg->dvr_dup_detect_episode) { - de = dvr_entry_find_by_episode(e); - if (de) { - if (de->de_start > start) { - dvr_event_replaced(de->de_bcast, e); - return de; - } else { - return NULL; - } - } - } - de = calloc(1, sizeof(dvr_entry_t)); de->de_id = ++de_tally; From 1334869bf0ea668e58e1c72daa3c09c1a95b5d81 Mon Sep 17 00:00:00 2001 From: "Alexey I. Froloff" Date: Wed, 24 Oct 2012 21:10:15 +0400 Subject: [PATCH 071/503] [PR-172] Fix compilatioin issues on OpenWRT Fix SHA1_* function names Fix unused variables and functions --- src/trap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/trap.c b/src/trap.c index bd761186..b0fe543b 100644 --- a/src/trap.c +++ b/src/trap.c @@ -67,6 +67,7 @@ sappend(char *buf, size_t l, const char *fmt, ...) /** * */ +#if ENABLE_EXECINFO static int add2lineresolve(const char *binary, void *addr, char *buf0, size_t buflen) { @@ -126,6 +127,7 @@ add2lineresolve(const char *binary, void *addr, char *buf0, size_t buflen) close(fd[0]); return 0; } +#endif /* ENABLE_EXECINFO */ @@ -133,8 +135,8 @@ static void traphandler(int sig, siginfo_t *si, void *UC) { ucontext_t *uc = UC; - char buf[200]; #if ENABLE_EXECINFO + char buf[200]; static void *frames[MAXFRAMES]; int nframes = backtrace(frames, MAXFRAMES); Dl_info dli; @@ -243,9 +245,9 @@ trap_init(const char *ver) char *m = malloc(st.st_size); if(m != NULL) { if(read(fd, m, st.st_size) == st.st_size) { - SHA_Init(&binsum); - SHA_Update(&binsum, (void *)m, st.st_size); - SHA_Final(digest, &binsum); + SHA1_Init(&binsum); + SHA1_Update(&binsum, (void *)m, st.st_size); + SHA1_Final(digest, &binsum); } free(m); } From 7367f713c1792637015f284d0e145cbd5c06335b Mon Sep 17 00:00:00 2001 From: Tiago Pierezan Camargo Date: Wed, 24 Oct 2012 22:17:05 -0200 Subject: [PATCH 072/503] [PR-173] Stype fix for NET POA Cabo network --- src/service.c | 2 ++ src/service.h | 1 + src/serviceprobe.c | 1 + 3 files changed, 4 insertions(+) diff --git a/src/service.c b/src/service.c index db8f570d..c193ef52 100644 --- a/src/service.c +++ b/src/service.c @@ -763,6 +763,7 @@ static struct strtab stypetab[] = { { "SDTV", ST_DN_SDTV }, { "HDTV", ST_DN_HDTV }, { "SDTV", ST_SK_SDTV }, + { "SDTV", ST_NE_SDTV }, { "SDTV-AC", ST_AC_SDTV }, { "HDTV-AC", ST_AC_HDTV }, }; @@ -789,6 +790,7 @@ service_is_tv(service_t *t) t->s_servicetype == ST_DN_SDTV || t->s_servicetype == ST_DN_HDTV || t->s_servicetype == ST_SK_SDTV || + t->s_servicetype == ST_NE_SDTV || t->s_servicetype == ST_AC_SDTV || t->s_servicetype == ST_AC_HDTV; } diff --git a/src/service.h b/src/service.h index bf10d74e..11e39b5d 100644 --- a/src/service.h +++ b/src/service.h @@ -324,6 +324,7 @@ typedef struct service { ST_HDTV = 0x11, /* HDTV (MPEG2) */ ST_AC_SDTV = 0x16, /* Advanced codec SDTV */ ST_AC_HDTV = 0x19, /* Advanced codec HDTV */ + ST_NE_SDTV = 0x80, /* NET POA - Cabo SDTV */ ST_EX_HDTV = 0x91, /* Bell TV HDTV */ ST_EX_SDTV = 0x96, /* Bell TV SDTV */ ST_EP_HDTV = 0xA0, /* Bell TV tiered HDTV */ diff --git a/src/serviceprobe.c b/src/serviceprobe.c index 7cc6282a..62dcc211 100644 --- a/src/serviceprobe.c +++ b/src/serviceprobe.c @@ -198,6 +198,7 @@ serviceprobe_thread(void *aux) case ST_EX_SDTV: case ST_DN_SDTV: case ST_SK_SDTV: + case ST_NE_SDTV: str = "SDTV"; break; case ST_HDTV: From 3cd6336314e7e082d6d8a5804a3f0944345b96d0 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 31 Oct 2012 17:29:42 +0000 Subject: [PATCH 073/503] Issue #1376 - add option to manually disable full mux rx. It appears this mode can cause problems on some systems, particulary related to USB tuners. Can cause high CPU load. --- src/dvb/dvb.h | 3 +++ src/dvb/dvb_adapter.c | 24 ++++++++++++++++++++++++ src/webui/extjs_dvb.c | 4 ++++ src/webui/static/app/dvb.js | 6 +++++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 2b0460d5..847074a9 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -212,6 +212,7 @@ typedef struct th_dvb_adapter { uint32_t tda_diseqc_version; uint32_t tda_diseqc_repeats; uint32_t tda_disable_pmt_monitor; + uint32_t tda_disable_full_mux_rx; char *tda_displayname; char *tda_fe_path; @@ -363,6 +364,8 @@ void dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda, void dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on); +void dvb_adapter_set_disable_full_mux_rx(th_dvb_adapter_t *tda, int on); + void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src); void dvb_adapter_clean(th_dvb_adapter_t *tda); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 6250363d..5a8ccf95 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -94,6 +94,7 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "extrapriority", tda->tda_extrapriority); htsmsg_add_u32(m, "skip_initialscan", tda->tda_skip_initialscan); htsmsg_add_u32(m, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); + htsmsg_add_u32(m, "disable_full_mux_rx", tda->tda_disable_full_mux_rx); hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier); htsmsg_destroy(m); } @@ -364,6 +365,25 @@ dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on) } +/** + * + */ +void +dvb_adapter_set_disable_full_mux_rx(th_dvb_adapter_t *tda, int on) +{ + if(tda->tda_disable_full_mux_rx == on) + return; + + lock_assert(&global_lock); + + tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" disabled full MUX receive set to: %s", + tda->tda_displayname, on ? "On" : "Off"); + + tda->tda_disable_full_mux_rx = on; + tda_save(tda); +} + + /** * */ @@ -388,6 +408,9 @@ check_full_stream(th_dvb_adapter_t *tda) struct dmx_pes_filter_params dmx_param; int r; + if(tda->tda_disable_full_mux_rx) + return 0; + if(tda->tda_hostconnection == HOSTCONNECTION_USB12) return 0; // Don't even bother, device <-> host interface is too slow @@ -658,6 +681,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor); + htsmsg_get_u32(c, "disable_full_mux_rx", &tda->tda_disable_full_mux_rx); } htsmsg_destroy(l); } diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 4ae0d35d..525ed10e 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -157,6 +157,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "sidtochan", tda->tda_sidtochan); htsmsg_add_u32(r, "nitoid", tda->tda_nitoid); htsmsg_add_u32(r, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); + htsmsg_add_u32(r, "disable_full_mux_rx", tda->tda_disable_full_mux_rx); htsmsg_add_str(r, "diseqcversion", ((const char *[]){"DiSEqC 1.0 / 2.0", "DiSEqC 1.1 / 2.1"}) @@ -199,6 +200,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) s = http_arg_get(&hc->hc_req_args, "disable_pmt_monitor"); dvb_adapter_set_disable_pmt_monitor(tda, !!s); + s = http_arg_get(&hc->hc_req_args, "disable_full_mux_rx"); + dvb_adapter_set_disable_full_mux_rx(tda, !!s); + if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL) dvb_adapter_set_nitoid(tda, atoi(s)); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 48365610..18a823ba 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1089,7 +1089,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { }, [ 'name', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', - ,'disable_pmt_monitor', 'idleclose' ]); + ,'disable_pmt_monitor', 'disable_full_mux_rx', 'idleclose' ]); function saveConfForm() { confform.getForm().submit({ @@ -1135,6 +1135,10 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { fieldLabel : 'Monitor signal quality', name : 'qmon' }), + new Ext.form.Checkbox({ + fieldLabel : 'Disable full MUX reception', + name : 'disable_full_mux_rx' + }), new Ext.form.Checkbox({ fieldLabel : 'Disable PMT monitoring', name : 'disable_pmt_monitor' From 4515f5a2d1a022bf05cd07ffa647b25f444de550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 1 Nov 2012 13:11:11 +0100 Subject: [PATCH 074/503] fixed file suffix for pass-through recording --- src/muxer.c | 30 +++++++++++++++++++++++++++--- src/muxer.h | 13 ++++++++----- src/muxer_pass.c | 6 +++--- src/muxer_tvh.c | 6 +++--- src/webui/webui.c | 2 +- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/muxer.c b/src/muxer.c index 763219f4..b0c9d99f 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -89,7 +89,7 @@ static struct strtab container_video_file_suffix[] = { * Get the mime type for a container */ const char* -muxer_container_mimetype(muxer_container_type_t mc, int video) +muxer_container_type2mime(muxer_container_type_t mc, int video) { const char *str; @@ -141,7 +141,7 @@ muxer_container_type2txt(muxer_container_type_t mc) /** - * Convert a string to a container type + * Convert a container name to a container type */ muxer_container_type_t muxer_container_txt2type(const char *str) @@ -159,6 +159,28 @@ muxer_container_txt2type(const char *str) } +/** + * Convert a mime-string to a container type + */ +muxer_container_type_t +muxer_container_mime2type(const char *str) +{ + muxer_container_type_t mc; + + if(!str) + return MC_UNKNOWN; + + mc = str2val(str, container_video_mime); + if(mc == -1) + mc = str2val(str, container_audio_mime); + + if(mc == -1) + return MC_UNKNOWN; + + return mc; +} + + /** * Create a new muxer */ @@ -200,6 +222,7 @@ const char* muxer_suffix(muxer_t *m, const struct streaming_start *ss) { const char *mime; + muxer_container_type_t mc; int video; if(!m || !ss) @@ -207,8 +230,9 @@ muxer_suffix(muxer_t *m, const struct streaming_start *ss) mime = m->m_mime(m, ss); video = memcmp("audio", mime, 5); + mc = muxer_container_mime2type(mime); - return muxer_container_suffix(m->m_container, video); + return muxer_container_suffix(mc, video); } diff --git a/src/muxer.h b/src/muxer.h index 77767f3e..766c5dd4 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -56,11 +56,14 @@ typedef struct muxer { } muxer_t; -// type <==> txt converters -const char * muxer_container_type2txt(muxer_container_type_t mc); -muxer_container_type_t muxer_container_txt2type(const char *str); -const char* muxer_container_mimetype(muxer_container_type_t mc, int video); -const char* muxer_container_suffix (muxer_container_type_t mc, int video); +// type <==> string converters +const char * muxer_container_type2txt (muxer_container_type_t mc); +const char* muxer_container_type2mime (muxer_container_type_t mc, int video); + +muxer_container_type_t muxer_container_txt2type (const char *str); +muxer_container_type_t muxer_container_mime2type (const char *str); + +const char* muxer_container_suffix(muxer_container_type_t mc, int video); // Muxer factory muxer_t *muxer_create(muxer_container_type_t mc); diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 20a986a7..965f130c 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -90,11 +90,11 @@ pass_muxer_mime(muxer_t* m, const struct streaming_start *ss) mc = MC_UNKNOWN; if(has_video) - return muxer_container_mimetype(mc, 1); + return muxer_container_type2mime(mc, 1); else if(has_audio) - return muxer_container_mimetype(mc, 0); + return muxer_container_type2mime(mc, 0); else - return muxer_container_mimetype(MC_UNKNOWN, 0); + return muxer_container_type2mime(MC_UNKNOWN, 0); } diff --git a/src/muxer_tvh.c b/src/muxer_tvh.c index dd335145..8555ba91 100644 --- a/src/muxer_tvh.c +++ b/src/muxer_tvh.c @@ -56,11 +56,11 @@ tvh_muxer_mime(muxer_t* m, const struct streaming_start *ss) } if(has_video) - return muxer_container_mimetype(m->m_container, 1); + return muxer_container_type2mime(m->m_container, 1); else if(has_audio) - return muxer_container_mimetype(m->m_container, 0); + return muxer_container_type2mime(m->m_container, 0); else - return muxer_container_mimetype(MC_UNKNOWN, 0); + return muxer_container_type2mime(MC_UNKNOWN, 0); } diff --git a/src/webui/webui.c b/src/webui/webui.c index 35ffce49..6e5ab39c 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -805,7 +805,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) } fname = strdup(de->de_filename); - content = muxer_container_mimetype(de->de_mc, 1); + content = muxer_container_type2mime(de->de_mc, 1); postfix = muxer_container_suffix(de->de_mc, 1); pthread_mutex_unlock(&global_lock); From 43d0814745a52f18061525ad9a28ae9d83fc121e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 4 Nov 2012 20:05:40 +0100 Subject: [PATCH 075/503] fixed a minor typo (mostly cosmetics) --- src/muxer_pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 965f130c..299506d8 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -223,7 +223,7 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) rem = pm->pm_pc % TS_INJECTION_RATE; if(!rem) { pm->pm_pat[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); - pm->pm_pmt[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); + pm->pm_pmt[3] = (pm->pm_pmt[3] & 0xf0) | (pm->pm_ic & 0x0f); pass_muxer_write(m, pm->pm_pmt, 188); pass_muxer_write(m, pm->pm_pat, 188); pm->pm_ic++; From 03ff9727564d991b40e0f0fb0bb3ea06d00d8e65 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 5 Nov 2012 10:13:24 +0000 Subject: [PATCH 076/503] Fix #1377 - check for EOVERFLOW when reading from DVB device. This can be returned as a result of a failure to read quickly enough from the DVR device. This appears to happen quite regularly on channel zap for certain cards. It's non-fatal and the system will auto recover immediately. For now I've left the exit on other error in, but have added an error message so we know its happening (the biggest problem was this was happening silently before). This may also relate to #1134, so might be worth back porting to 3.2. --- src/dvb/dvb_adapter.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 5a8ccf95..5324d7f7 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -906,8 +906,16 @@ dvb_adapter_input_dvr(void *aux) if (c < 0) { if (errno == EAGAIN || errno == EINTR) continue; - else + else if (errno == EOVERFLOW) { + tvhlog(LOG_WARNING, "dvb", "\"%s\" read() EOVERFLOW", + tda->tda_identifier); + continue; + } else { + // TODO: should we try to recover? + tvhlog(LOG_ERR, "dvb", "\"%s\" read() error %d", + tda->tda_identifier, errno); break; + } } r += c; From 8f44e28535131a92e256eb5028c9c0355fb6cb1f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 5 Nov 2012 10:17:34 +0000 Subject: [PATCH 077/503] Minor formatting fix. --- src/dvb/dvb_adapter.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 5324d7f7..87be0c64 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -924,7 +924,6 @@ dvb_adapter_input_dvr(void *aux) int wakeup_table_feed = 0; // Just wanna wakeup once - pthread_mutex_lock(&tda->tda_delivery_mutex); if(LIST_FIRST(&tda->tda_streaming_pad.sp_targets) != NULL) { @@ -937,28 +936,26 @@ dvb_adapter_input_dvr(void *aux) pktbuf_ref_dec(pb); } - - /* Process */ while (r >= 188) { /* sync */ if (tsb[i] == 0x47) { + int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; + if(tda->tda_table_filter[pid]) { + if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error + dvb_table_feed_t *dtf = malloc(sizeof(dvb_table_feed_t)); + memcpy(dtf->dtf_tsb, tsb + i, 188); + TAILQ_INSERT_TAIL(&tda->tda_table_feed, dtf, dtf_link); + wakeup_table_feed = 1; + } + } else { + LIST_FOREACH(t, &tda->tda_transports, s_active_link) + if(t->s_dvb_mux_instance == tda->tda_mux_current) + ts_recv_packet1(t, tsb + i, NULL); + } - if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error - int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; - if(tda->tda_table_filter[pid]) { - dvb_table_feed_t *dtf = malloc(sizeof(dvb_table_feed_t)); - memcpy(dtf->dtf_tsb, tsb + i, 188); - TAILQ_INSERT_TAIL(&tda->tda_table_feed, dtf, dtf_link); - wakeup_table_feed = 1; - } - } - - LIST_FOREACH(t, &tda->tda_transports, s_active_link) - if(t->s_dvb_mux_instance == tda->tda_mux_current) - ts_recv_packet1(t, tsb + i, NULL); i += 188; r -= 188; From a9692b9514eca41a5da1bef56914f264fbc331ed Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 5 Nov 2012 10:38:05 +0000 Subject: [PATCH 078/503] Restructure dvb adapter init to cope with configuration that is needed before we can decide whether to use full mux mode or not. --- src/dvb/dvb_adapter.c | 62 +++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 87be0c64..3776dcc6 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -506,21 +506,11 @@ tda_add(int adapter_num) TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - if(check_full_stream(tda)) { - tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", path); - dvb_input_raw_setup(tda); - } else { - tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", path); - dvb_input_filtered_setup(tda); - } - - if(tda->tda_sat) - dvb_satconf_init(tda); - gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); } + /** * */ @@ -559,10 +549,23 @@ tda_add_from_file(const char *filename) tda->tda_displayname = strdup(filename); TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - - dvb_input_raw_setup(tda); } +/** + * Initiliase input + */ +static void tda_init_input (th_dvb_adapter_t *tda) +{ + if(tda->tda_type == -1 || check_full_stream(tda)) { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath); + dvb_input_raw_setup(tda); + } else { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath); + dvb_input_filtered_setup(tda); + } +} + + /** * @@ -633,36 +636,37 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) TAILQ_INIT(&dvb_adapters); + /* Initialise hardware */ for(i = 0; i < 32; i++) if ((1 << i) & adapter_mask) tda_add(i); + /* Initialise rawts test file */ if(rawfile) tda_add_from_file(rawfile); - + /* Load configuration */ l = hts_settings_load("dvbadapters"); if(l != NULL) { HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) - continue; + continue; name = htsmsg_get_str(c, "displayname"); if((s = htsmsg_get_str(c, "type")) == NULL || - (type = dvb_str_to_adaptertype(s)) < 0) - continue; + (type = dvb_str_to_adaptertype(s)) < 0) + continue; if((tda = dvb_adapter_find_by_identifier(f->hmf_name)) == NULL) { - /* Not discovered by hardware, create it */ - - tda = tda_alloc(); - tda->tda_identifier = strdup(f->hmf_name); - tda->tda_type = type; - TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); + /* Not discovered by hardware, create it */ + tda = tda_alloc(); + tda->tda_identifier = strdup(f->hmf_name); + tda->tda_type = type; + TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); } else { - if(type != tda->tda_type) - continue; /* Something is wrong, ignore */ + if(type != tda->tda_type) + continue; /* Something is wrong, ignore */ } free(tda->tda_displayname); @@ -686,8 +690,14 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_destroy(l); } - TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + tda_init_input(tda); + + if(tda->tda_sat) + dvb_satconf_init(tda); + dvb_mux_load(tda); + } } From d5deb9d19d57feef8acf25df2877539b4af0d39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 5 Nov 2012 13:05:43 +0100 Subject: [PATCH 079/503] HTSP: Handle partial write()s --- src/htsp_server.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index d1f65f60..8fcbdd41 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1469,24 +1469,30 @@ htsp_write_scheduler(void *aux) r = htsmsg_binary_serialize(hm->hm_msg, &dptr, &dlen, INT32_MAX); -#if 0 - if(hm->hm_pktref) { - usleep(hm->hm_payloadsize * 3); - } -#endif htsp_msg_destroy(hm); - - /* ignore return value */ - r = write(htsp->htsp_fd, dptr, dlen); - if(r != dlen) - tvhlog(LOG_INFO, "htsp", "%s: Write error -- %s", - htsp->htsp_logname, strerror(errno)); - free(dptr); + + void *freeme = dptr; + + while(dlen > 0) { + r = write(htsp->htsp_fd, dptr, dlen); + if(r < 1) { + tvhlog(LOG_INFO, "htsp", "%s: Write error -- %s", + htsp->htsp_logname, strerror(errno)); + break; + } + + dptr += r; + dlen -= r; + } + + free(freeme); pthread_mutex_lock(&htsp->htsp_out_mutex); - if(r != dlen) + if(dlen) break; } + // Shutdown socket to make receive thread terminate entire HTSP connection + shutdown(htsp->htsp_fd, SHUT_RDWR); pthread_mutex_unlock(&htsp->htsp_out_mutex); return NULL; } From 0d8d8a2da1412476bf8df409fe7c06e2af498c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 5 Nov 2012 14:27:00 +0100 Subject: [PATCH 080/503] h264parser: Treat SPS and PPS id as unsigned There is a change of crash otherwise if we get a corrupt bitstream --- src/parser_h264.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser_h264.c b/src/parser_h264.c index 67a82304..0653227f 100644 --- a/src/parser_h264.c +++ b/src/parser_h264.c @@ -355,7 +355,7 @@ h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype, int *isfield) { h264_private_t *p; - int slice_type, pps_id, sps_id; + unsigned int slice_type, pps_id, sps_id; if((p = st->es_priv) == NULL) return -1; From 2ef7dac5971401e536e002d6bc61638c5d55923e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 6 Nov 2012 12:48:50 +0100 Subject: [PATCH 081/503] Plug memory leak --- src/webui/extjs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 7ede18ae..10829e2e 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -619,6 +619,7 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) if ( str ) save |= epggrab_enable_module_by_id(str, u32); } } + htsmsg_destroy(array); } } if (save) epggrab_save(); From a2cf987afa2b3c56c71b48ab507df5d485ed37be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 7 Nov 2012 09:27:58 +0100 Subject: [PATCH 082/503] subscriptions: Plug possible memleak --- src/subscriptions.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/subscriptions.c b/src/subscriptions.c index 1cae0a4f..2f56825b 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -79,9 +79,14 @@ subscription_link_service(th_subscription_t *s, service_t *t) pthread_mutex_lock(&t->s_stream_mutex); - if(TAILQ_FIRST(&t->s_components) != NULL) + if(TAILQ_FIRST(&t->s_components) != NULL) { + + if(s->ths_start_message != NULL) + streaming_msg_free(s->ths_start_message); + s->ths_start_message = streaming_msg_create_data(SMT_START, service_build_stream_start(t)); + } // Link to service output streaming_target_connect(&t->s_streaming_pad, &s->ths_input); From 9338b9529e2ee4b69640887c5998d6ee2cca8601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 7 Nov 2012 09:28:18 +0100 Subject: [PATCH 083/503] service: Plug memory leak --- src/service.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/service.c b/src/service.c index c193ef52..6548f104 100644 --- a/src/service.c +++ b/src/service.c @@ -110,18 +110,21 @@ stream_clean(elementary_stream_t *st) st->es_global_data_len = 0; } - /** * */ void -service_stream_destroy(service_t *t, elementary_stream_t *st) +service_stream_destroy(service_t *t, elementary_stream_t *es) { if(t->s_status == SERVICE_RUNNING) - stream_clean(st); - TAILQ_REMOVE(&t->s_components, st, es_link); - free(st->es_nicename); - free(st); + stream_clean(es); + + avgstat_flush(&es->es_rate); + avgstat_flush(&es->es_cc_errors); + + TAILQ_REMOVE(&t->s_components, es, es_link); + free(es->es_nicename); + free(es); } /** @@ -489,17 +492,17 @@ service_destroy(service_t *t) free(t->s_provider); free(t->s_dvb_charset); - while((st = TAILQ_FIRST(&t->s_components)) != NULL) { - TAILQ_REMOVE(&t->s_components, st, es_link); - free(st->es_nicename); - free(st); - } + while((st = TAILQ_FIRST(&t->s_components)) != NULL) + service_stream_destroy(t, st); free(t->s_pat_section); free(t->s_pmt_section); sbuf_free(&t->s_tsbuf); + avgstat_flush(&t->s_cc_errors); + avgstat_flush(&t->s_rate); + service_unref(t); if(ch != NULL) { From 7a7f57072d7576a754d98441530ba8d0f8dce8a0 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 7 Nov 2012 10:45:58 +0000 Subject: [PATCH 084/503] Fix mistake in EPG episode numbering serialisation. --- src/epg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/epg.c b/src/epg.c index c123a7b2..eb6a6a88 100644 --- a/src/epg.c +++ b/src/epg.c @@ -746,13 +746,13 @@ static htsmsg_t *epg_episode_num_serialize ( epg_episode_num_t *num ) if (num->e_cnt) htsmsg_add_u32(m, "e_cnt", num->e_cnt); if (num->s_num) - htsmsg_add_u32(m, "s_num", num->e_num); + htsmsg_add_u32(m, "s_num", num->s_num); if (num->s_cnt) - htsmsg_add_u32(m, "s_cnt", num->e_cnt); + htsmsg_add_u32(m, "s_cnt", num->s_cnt); if (num->p_num) - htsmsg_add_u32(m, "p_num", num->e_num); + htsmsg_add_u32(m, "p_num", num->p_num); if (num->p_cnt) - htsmsg_add_u32(m, "p_cnt", num->e_cnt); + htsmsg_add_u32(m, "p_cnt", num->p_cnt); if (num->text) htsmsg_add_str(m, "text", num->text); return m; From ddad1d21dcd64e5d51dc24c46a4f7ec24f0673b1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 7 Nov 2012 11:40:40 +0000 Subject: [PATCH 085/503] Tarball generation script. --- support/tarball | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 support/tarball diff --git a/support/tarball b/support/tarball new file mode 100755 index 00000000..ca65b664 --- /dev/null +++ b/support/tarball @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Build tarball of the current directory +# + +# Exit +function die +{ + echo "ERROR: $*" + exit 1 +} + +# Switch dir +SRCDIR=$(dirname $0)/.. +cd $SRCDIR + +# Arguments +REL=$1 +if [ ! -z "$REL" ]; then + git checkout $REL || die "could not checkout $REL" +fi + +# Clean +git checkout . || die "could not clean git tree" + +# Version +VER=$(./support/version) +echo $VER | grep -q dirty && die "git tree is not clean" +VER1=$(echo $VER | sed 's/~.*//') +echo $VER1 + +# Temp directory +TMPDIR=/tmp/tvhtar-$$ +mkdir -p $TMPDIR +trap "rm -rf $TMPDIR" EXIT + +# Copy +DSTDIR=$TMPDIR/tvheadend-$VER1 +mkdir $DSTDIR +git archive HEAD | tar -x -C $DSTDIR + +# Remove stuff we don't need +rm -rf $DSTDIR/.gitignore + +# Fix changelog (store version) +$DSTDIR/support/changelog $DSTDIR/debian/changelog "" $VER + +# Build tarball +TARFILE=$(cd $SRCDIR/..; pwd)/tvheadend-$VER1.tar.gz +tar -C $TMPDIR -zcf $TARFILE tvheadend-$VER1 + +# Done +echo "Created $TARFILE" From 03f7bc187bf9872c0582ccaebfa71b8348e1b780 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 7 Nov 2012 23:02:10 +0000 Subject: [PATCH 086/503] Tweak to the full mux configuration, all USB is now filtered mode by default. --- src/dvb/dvb.h | 4 ++-- src/dvb/dvb_adapter.c | 33 ++++++++++++++++++++++++--------- src/webui/extjs_dvb.c | 6 +++--- src/webui/static/app/dvb.js | 17 +++++++++++++---- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 847074a9..6b42de90 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -212,7 +212,7 @@ typedef struct th_dvb_adapter { uint32_t tda_diseqc_version; uint32_t tda_diseqc_repeats; uint32_t tda_disable_pmt_monitor; - uint32_t tda_disable_full_mux_rx; + int32_t tda_full_mux_rx; char *tda_displayname; char *tda_fe_path; @@ -364,7 +364,7 @@ void dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda, void dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on); -void dvb_adapter_set_disable_full_mux_rx(th_dvb_adapter_t *tda, int on); +void dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int r); void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 3776dcc6..3a971d02 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -94,7 +94,7 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "extrapriority", tda->tda_extrapriority); htsmsg_add_u32(m, "skip_initialscan", tda->tda_skip_initialscan); htsmsg_add_u32(m, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); - htsmsg_add_u32(m, "disable_full_mux_rx", tda->tda_disable_full_mux_rx); + htsmsg_add_s32(m, "full_mux_rx", tda->tda_full_mux_rx); hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier); htsmsg_destroy(m); } @@ -369,17 +369,23 @@ dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on) * */ void -dvb_adapter_set_disable_full_mux_rx(th_dvb_adapter_t *tda, int on) +dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int on) { - if(tda->tda_disable_full_mux_rx == on) + const char* label[] = { "Auto", "Off", "On" }; + + if (on < -1) on = -1; + if (on > 1) on = 1; + + if(tda->tda_full_mux_rx == on) return; lock_assert(&global_lock); - tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" disabled full MUX receive set to: %s", - tda->tda_displayname, on ? "On" : "Off"); + tvhlog(LOG_NOTICE, "dvb", + "Adapter \"%s\" disabled full MUX receive set to: %s", + tda->tda_displayname, label[on+1]); - tda->tda_disable_full_mux_rx = on; + tda->tda_full_mux_rx = on; tda_save(tda); } @@ -408,12 +414,15 @@ check_full_stream(th_dvb_adapter_t *tda) struct dmx_pes_filter_params dmx_param; int r; - if(tda->tda_disable_full_mux_rx) - return 0; + if(tda->tda_full_mux_rx != -1) + return tda->tda_full_mux_rx; if(tda->tda_hostconnection == HOSTCONNECTION_USB12) return 0; // Don't even bother, device <-> host interface is too slow + if(tda->tda_hostconnection == HOSTCONNECTION_USB480) + return 0; // USB in general appears to have CPU loading issues? + int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); if(fd == -1) return 0; @@ -462,6 +471,7 @@ tda_add(int adapter_num) tda->tda_fe_path = strdup(fname); tda->tda_fe_fd = -1; tda->tda_dvr_pipe[0] = -1; + tda->tda_full_mux_rx = -1; tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); @@ -542,6 +552,8 @@ tda_add_from_file(const char *filename) tda->tda_idlescan = 0; tda->tda_sat = 0; + + tda->tda_full_mux_rx = 1; /* Come up with an initial displayname, user can change it and it will be overridden by any stored settings later on */ @@ -632,6 +644,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_field_t *f; const char *name, *s; int i, type; + uint32_t u32; th_dvb_adapter_t *tda; TAILQ_INIT(&dvb_adapters); @@ -685,7 +698,9 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor); - htsmsg_get_u32(c, "disable_full_mux_rx", &tda->tda_disable_full_mux_rx); + if (htsmsg_get_s32(c, "full_mux_rx", &tda->tda_full_mux_rx)) + if (!htsmsg_get_u32(c, "disable_full_mux_rx", &u32) && u32) + tda->tda_full_mux_rx = 0; } htsmsg_destroy(l); } diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 525ed10e..d1ce5b15 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -157,7 +157,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "sidtochan", tda->tda_sidtochan); htsmsg_add_u32(r, "nitoid", tda->tda_nitoid); htsmsg_add_u32(r, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); - htsmsg_add_u32(r, "disable_full_mux_rx", tda->tda_disable_full_mux_rx); + htsmsg_add_u32(r, "full_mux_rx", tda->tda_full_mux_rx+1); htsmsg_add_str(r, "diseqcversion", ((const char *[]){"DiSEqC 1.0 / 2.0", "DiSEqC 1.1 / 2.1"}) @@ -200,8 +200,8 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) s = http_arg_get(&hc->hc_req_args, "disable_pmt_monitor"); dvb_adapter_set_disable_pmt_monitor(tda, !!s); - s = http_arg_get(&hc->hc_req_args, "disable_full_mux_rx"); - dvb_adapter_set_disable_full_mux_rx(tda, !!s); + s = http_arg_get(&hc->hc_req_args, "full_mux_rx"); + dvb_adapter_set_full_mux_rx(tda, atoi(s)-1); if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL) dvb_adapter_set_nitoid(tda, atoi(s)); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 18a823ba..dcda443a 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1089,7 +1089,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { }, [ 'name', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', - ,'disable_pmt_monitor', 'disable_full_mux_rx', 'idleclose' ]); + ,'disable_pmt_monitor', 'full_mux_rx', 'idleclose' ]); function saveConfForm() { confform.getForm().submit({ @@ -1135,9 +1135,18 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { fieldLabel : 'Monitor signal quality', name : 'qmon' }), - new Ext.form.Checkbox({ - fieldLabel : 'Disable full MUX reception', - name : 'disable_full_mux_rx' + new Ext.form.ComboBox({ + fieldLabel : 'Full mux reception', + name : 'full_mux_rx', + hiddenName: 'full_mux_rx', + displayField: 'num', + valueField: 'str', + editable : false, + allowBlank : false, + mode : 'remote', + triggerAction : 'all', + fields: [ 'num', 'str' ], + store : [ [0, 'Auto'], [1, 'Off'], [2, 'On'] ] }), new Ext.form.Checkbox({ fieldLabel : 'Disable PMT monitoring', From 683b0f9c8bf4feaa0f3a17b1114b519005072e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 8 Nov 2012 14:21:39 +0100 Subject: [PATCH 087/503] HTSP: Plug possible memory leak at HTSP disconnect If subscriber is slow we will leak memory because buffered packets will not be free'd upon HTSP session close --- src/htsp_server.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index 8fcbdd41..b8c75be4 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1564,6 +1564,17 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, pthread_mutex_unlock(&htsp.htsp_out_mutex); pthread_join(htsp.htsp_writer_thread, NULL); + + htsp_msg_q_t *hmq; + + TAILQ_FOREACH(hmq, &htsp.htsp_active_output_queues, hmq_link) { + htsp_msg_t *hm; + while((hm = TAILQ_FIRST(&hmq->hmq_q)) != NULL) { + TAILQ_REMOVE(&hmq->hmq_q, hm, hm_link); + htsp_msg_destroy(hm); + } + } + close(fd); } From fd6f813d83a8a916d7f8012acf15b49e408a916f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 14 Nov 2012 11:00:50 +0100 Subject: [PATCH 088/503] fix 'use after free' of the htsp logname when the connection to the client is lost. --- src/htsp_server.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index b8c75be4..da8d34bd 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1553,11 +1553,6 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, pthread_mutex_unlock(&global_lock); - free(htsp.htsp_logname); - free(htsp.htsp_peername); - free(htsp.htsp_username); - free(htsp.htsp_clientname); - pthread_mutex_lock(&htsp.htsp_out_mutex); htsp.htsp_writer_run = 0; pthread_cond_signal(&htsp.htsp_out_cond); @@ -1565,6 +1560,11 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, pthread_join(htsp.htsp_writer_thread, NULL); + free(htsp.htsp_logname); + free(htsp.htsp_peername); + free(htsp.htsp_username); + free(htsp.htsp_clientname); + htsp_msg_q_t *hmq; TAILQ_FOREACH(hmq, &htsp.htsp_active_output_queues, hmq_link) { From 254872a8f8ec5c43c0dce86f1e521f9c22e361df Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 11 Nov 2012 20:08:35 +0000 Subject: [PATCH 089/503] Issue #1369 - move iptv service type store inside main tab function else it causes a 401 error for non-admin UI users. --- src/webui/static/app/iptv.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/webui/static/app/iptv.js b/src/webui/static/app/iptv.js index 7914685f..4cd4f35e 100644 --- a/src/webui/static/app/iptv.js +++ b/src/webui/static/app/iptv.js @@ -1,19 +1,19 @@ -tvheadend.servicetypeStore = new Ext.data.JsonStore({ - root : 'entries', - id : 'val', - url : '/iptv/services', - baseParams : { - op : 'servicetypeList' - }, - fields : [ 'val', 'str' ], - autoLoad : true -}); - /** * IPTV service grid */ tvheadend.iptv = function(adapterId) { + var servicetypeStore = new Ext.data.JsonStore({ + root : 'entries', + id : 'val', + url : '/iptv/services', + baseParams : { + op : 'servicetypeList' + }, + fields : [ 'val', 'str' ], + autoLoad : false + }); + var fm = Ext.form; var enabledColumn = new Ext.grid.CheckColumn({ From c05f93da5321a8b14f209814e78a6a1581232540 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 11 Nov 2012 20:36:16 +0000 Subject: [PATCH 090/503] Issue #1393 - minor aesthetic change, remove trailing slash from user config DVR directory. --- src/dvr/dvr_rec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index e0ba9b35..dd0effd7 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -206,6 +206,11 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) snprintf(path, sizeof(path), "%s", cfg->dvr_storage); + /* Remove trailing slash */ + + if (path[strlen(path)-1] == '/') + path[strlen(path)-1] = '\0'; + /* Append per-day directory */ if(cfg->dvr_flags & DVR_DIR_PER_DAY) { From 99e6f4bd4f63915ddb8ec37ea577332a7a732e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 13:20:07 +0100 Subject: [PATCH 091/503] Add tvh_strbegins() helper --- src/tvheadend.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tvheadend.h b/src/tvheadend.h index c0790606..e117f6ab 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -432,6 +432,14 @@ extern void scopedunlock(pthread_mutex_t **mtxp); #define tvh_strlcatf(buf, size, fmt...) \ snprintf((buf) + strlen(buf), (size) - strlen(buf), fmt) +static inline const char *tvh_strbegins(const char *s1, const char *s2) +{ + while(*s2) + if(*s1++ != *s2++) + return NULL; + return s1; +} + int tvh_open(const char *pathname, int flags, mode_t mode); int tvh_socket(int domain, int type, int protocol); From 890acb780ca8495728d422695a5af51801e08763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 13:20:18 +0100 Subject: [PATCH 092/503] HTSP: Add option to serve files over HTSP Currently this is only used to serve recorded files --- src/htsp_server.c | 193 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 4 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index da8d34bd..f2db2617 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "tvheadend.h" #include "channels.h" @@ -64,6 +65,7 @@ extern char *dvr_storage; LIST_HEAD(htsp_connection_list, htsp_connection); LIST_HEAD(htsp_subscription_list, htsp_subscription); +LIST_HEAD(htsp_file_list, htsp_file); TAILQ_HEAD(htsp_msg_queue, htsp_msg); TAILQ_HEAD(htsp_msg_q_queue, htsp_msg_q); @@ -138,10 +140,9 @@ typedef struct htsp_connection { htsp_msg_q_t htsp_hmq_epg; htsp_msg_q_t htsp_hmq_qstatus; - /** - * - */ struct htsp_subscription_list htsp_subscriptions; + struct htsp_file_list htsp_files; + int htsp_file_id; uint32_t htsp_granted_access; @@ -177,6 +178,19 @@ typedef struct htsp_subscription { } htsp_subscription_t; + +/** + * + */ +typedef struct htsp_file { + LIST_ENTRY(htsp_file) hf_link; + int hf_id; // ID sent to client + int hf_fd; // Our file descriptor + char *hf_path; // For logging +} htsp_file_t; + + + #define HTSP_DEFAULT_QUEUE_DEPTH 500000 /* ************************************************************************** @@ -1235,6 +1249,169 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in) return NULL; } + +/** + * + */ +static htsmsg_t * +htsp_method_open_path(htsp_connection_t *htsp, const char *path) +{ + struct stat st; + int fd = open(path, O_RDONLY); + tvhlog(LOG_DEBUG, "HTSP", "Opening file %s -- %s", path, fd < 0 ? strerror(errno) : "OK"); + if(fd == -1) + return htsp_error("Unable to open file"); + + htsp_file_t *hf = calloc(1, sizeof(htsp_file_t)); + hf->hf_fd = fd; + hf->hf_id = ++htsp->htsp_file_id; + hf->hf_path = strdup(path); + LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link); + + htsmsg_t *rep = htsmsg_create_map(); + htsmsg_add_u32(rep, "id", hf->hf_id); + + if(!fstat(hf->hf_fd, &st)) { + htsmsg_add_u64(rep, "size", st.st_size); + htsmsg_add_u64(rep, "mtime", st.st_mtime); + } + + return rep; +} + + +/** + * Open file + */ +static htsmsg_t * +htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) +{ + const char *str, *s2; + const char *filename = NULL; + if((str = htsmsg_get_str(in, "file")) == NULL) + return htsp_error("Missing argument 'file'"); + + if((s2 = tvh_strbegins(str, "dvr/")) != NULL) { + dvr_entry_t *de = dvr_entry_find_by_id(atoi(s2)); + if(de == NULL) + return htsp_error("DVR entry does not exist"); + + if(de->de_filename == NULL) + return htsp_error("DVR entry does not have a file yet"); + + filename = de->de_filename; + } else { + return htsp_error("Unknown file"); + } + + return htsp_method_open_path(htsp, filename); +} + +/** + * + */ +static htsp_file_t * +file_find(const htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf; + + int id = htsmsg_get_u32_or_default(in, "id", 0); + + LIST_FOREACH(hf, &htsp->htsp_files, hf_link) { + if(hf->hf_id == id) + return hf; + } + return NULL; +} + + + +/** + * + */ +static htsmsg_t * +htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf = file_find(htsp, in); + uint64_t off; + uint32_t size; + + if(htsmsg_get_u64(in, "offset", &off)) + return htsp_error("Missing field 'offset'"); + + if(htsmsg_get_u32(in, "size", &size)) + return htsp_error("Missing field 'size'"); + + if(hf == NULL) + return htsp_error("Unknown file id"); + + void *m = malloc(size); + if(m == NULL) + return htsp_error("Too big segment"); + + int r = pread(hf->hf_fd, m, size, off); + if(r < 0) { + free(m); + return htsp_error("Read error"); + } + + htsmsg_t *rep = htsmsg_create_map(); + htsmsg_add_bin(rep, "data", m, r); + free(m); + return rep; +} + + +/** + * + */ +static void +htsp_file_destroy(htsp_file_t *hf) +{ + tvhlog(LOG_DEBUG, "HTSP", "Closed opened file %s", hf->hf_path); + free(hf->hf_path); + close(hf->hf_fd); + LIST_REMOVE(hf, hf_link); + free(hf); +} + +/** + * + */ +static htsmsg_t * +htsp_method_file_close(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf = file_find(htsp, in); + + if(hf == NULL) + return htsp_error("Unknown file id"); + + htsp_file_destroy(hf); + return htsmsg_create_map(); +} + + +/** + * + */ +static htsmsg_t * +htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf = file_find(htsp, in); + struct stat st; + + if(hf == NULL) + return htsp_error("Unknown file id"); + + htsmsg_t *rep = htsmsg_create_map(); + if(!fstat(hf->hf_fd, &st)) { + htsmsg_add_u64(rep, "size", st.st_size); + htsmsg_add_u64(rep, "mtime", st.st_mtime); + } + return rep; +} + + /** * HTSP methods */ @@ -1260,6 +1437,10 @@ struct { { "subscribe", htsp_method_subscribe, ACCESS_STREAMING}, { "unsubscribe", htsp_method_unsubscribe, ACCESS_STREAMING}, { "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING}, + { "openFile", htsp_method_file_open, ACCESS_RECORDER}, + { "readFile", htsp_method_file_read, ACCESS_RECORDER}, + { "closeFile", htsp_method_file_close, ACCESS_RECORDER}, + { "statFile", htsp_method_file_stat, ACCESS_RECORDER}, }; #define NUM_METHODS (sizeof(htsp_methods) / sizeof(htsp_methods[0])) @@ -1575,9 +1756,13 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, } } + htsp_file_t *hf; + while((hf = LIST_FIRST(&htsp.htsp_files)) != NULL) + htsp_file_destroy(hf); + close(fd); } - + /** * Fire up HTSP server */ From 36d097232eb6131d2b7096950146e59b10327b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 13:25:23 +0100 Subject: [PATCH 093/503] HTSP: Use lseek() + read() instead of pread() Does not build out-of-the-box on lucid otherwise --- src/htsp_server.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index f2db2617..fa137686 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1345,11 +1345,14 @@ htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in) if(hf == NULL) return htsp_error("Unknown file id"); + if(lseek(hf->hf_fd, off, SEEK_SET) != off) + return htsp_error("Seek error"); + void *m = malloc(size); if(m == NULL) return htsp_error("Too big segment"); - int r = pread(hf->hf_fd, m, size, off); + int r = read(hf->hf_fd, m, size); if(r < 0) { free(m); return htsp_error("Read error"); From a63d4ad9fdf9c9d4a2984607c32bde9d75d94a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 14:08:41 +0100 Subject: [PATCH 094/503] dvb: Actually set tdmi_uncorrected_blocks to FEC rate --- src/dvb/dvb_fe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 1ebfda3a..c3355d30 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -133,7 +133,8 @@ dvb_fe_monitor(void *aux) /* Read FEC counter (delta) */ fec = dvb_fe_get_unc(tda); - + tdmi->tdmi_uncorrected_blocks = fec; + tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec; if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE) tdmi->tdmi_fec_err_ptr = 0; From dfc924fc350a02c4f935f4851e273b8dc9654cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 16:41:05 +0100 Subject: [PATCH 095/503] Display per-adapter status information in the Status tab in UI --- src/dvb/dvb.h | 3 +- src/dvb/dvb_fe.c | 69 ++++++++++++++++++++------- src/webui/static/app/dvb.js | 6 ++- src/webui/static/app/status.js | 75 +++++++++++++++++++++++++++++- src/webui/static/app/tvadapters.js | 7 +-- 5 files changed, 136 insertions(+), 24 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 6b42de90..92fcaf2c 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -98,7 +98,8 @@ typedef struct th_dvb_mux_instance { struct th_dvb_adapter *tdmi_adapter; uint16_t tdmi_snr, tdmi_signal; - uint32_t tdmi_ber, tdmi_uncorrected_blocks; + uint32_t tdmi_ber, tdmi_unc; + float tdmi_unc_avg; #define TDMI_FEC_ERR_HISTOGRAM_SIZE 10 uint32_t tdmi_fec_err_histogram[TDMI_FEC_ERR_HISTOGRAM_SIZE]; diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index c3355d30..b90d5abb 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -90,13 +90,16 @@ dvb_fe_monitor(void *aux) { th_dvb_adapter_t *tda = aux; fe_status_t fe_status; - int status, v, update = 0, vv, i, fec, q; + int status, v, vv, i, fec, q; th_dvb_mux_instance_t *tdmi = tda->tda_mux_current; char buf[50]; signal_status_t sigstat; streaming_message_t sm; struct service *t; + int store = 0; + int notify = 0; + gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); if(tdmi == NULL) @@ -133,7 +136,11 @@ dvb_fe_monitor(void *aux) /* Read FEC counter (delta) */ fec = dvb_fe_get_unc(tda); - tdmi->tdmi_uncorrected_blocks = fec; + + if(tdmi->tdmi_unc != fec) { + tdmi->tdmi_unc = fec; + notify = 1; + } tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec; if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE) @@ -145,7 +152,13 @@ dvb_fe_monitor(void *aux) v++; vv += tdmi->tdmi_fec_err_histogram[i]; } - vv = vv / TDMI_FEC_ERR_HISTOGRAM_SIZE; + + float avg = (float)vv / TDMI_FEC_ERR_HISTOGRAM_SIZE; + + if(tdmi->tdmi_unc_avg != avg) { + tdmi->tdmi_unc_avg = avg; + notify = 1; + } if(v == 0) { status = TDMI_FE_OK; @@ -155,27 +168,35 @@ dvb_fe_monitor(void *aux) status = TDMI_FE_CONSTANT_FEC; } + int v; /* bit error rate */ - if(ioctl(tda->tda_fe_fd, FE_READ_BER, &tdmi->tdmi_ber) == -1) - tdmi->tdmi_ber = -2; + if(ioctl(tda->tda_fe_fd, FE_READ_BER, &v) != -1 && v != tdmi->tdmi_ber) { + tdmi->tdmi_ber = v; + notify = 1; + } /* signal strength */ - if(ioctl(tda->tda_fe_fd, FE_READ_SIGNAL_STRENGTH, &tdmi->tdmi_signal) == -1) - tdmi->tdmi_signal = -2; + if(ioctl(tda->tda_fe_fd, FE_READ_SIGNAL_STRENGTH, &v) != -1 && v != tdmi->tdmi_signal) { + tdmi->tdmi_signal = v; + notify = 1; + } /* signal/noise ratio */ - if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &tdmi->tdmi_snr) == -1) - tdmi->tdmi_snr = -2; + if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &v) == -1 && v != tdmi->tdmi_snr) { + tdmi->tdmi_snr = v; + notify = 1; + } } if(status != tdmi->tdmi_fe_status) { tdmi->tdmi_fe_status = status; dvb_mux_nicename(buf, sizeof(buf), tdmi); - tvhlog(LOG_DEBUG, + tvhlog(LOG_DEBUG, "dvb", "\"%s\" on adapter \"%s\", status changed to %s", buf, tda->tda_displayname, dvb_mux_status(tdmi)); - update = 1; + store = 1; + notify = 1; } if(status != TDMI_FE_UNKNOWN) { @@ -187,26 +208,40 @@ dvb_fe_monitor(void *aux) } if(q != tdmi->tdmi_quality) { tdmi->tdmi_quality = q; - update = 1; + store = 1; + notify = 1; } - } + } - if(update) { + if(notify) { htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); + htsmsg_add_u32(m, "signal", tdmi->tdmi_signal); + htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); + htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); + htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); notify_by_msg("dvbMux", m); - dvb_mux_save(tdmi); + m = htsmsg_create_map(); + htsmsg_add_str(m, "identifier", tda->tda_identifier); + htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100)); + htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); + htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); + htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); + htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg); + notify_by_msg("tvAdapter", m); } + if(store) + dvb_mux_save(tdmi); + /* Streaming message */ sigstat.status_text = dvb_mux_status(tdmi); sigstat.snr = tdmi->tdmi_snr; sigstat.signal = tdmi->tdmi_signal; sigstat.ber = tdmi->tdmi_ber; - sigstat.unc = tdmi->tdmi_uncorrected_blocks; + sigstat.unc = tdmi->tdmi_unc; sm.sm_type = SMT_SIGNAL_STATUS; sm.sm_data = &sigstat; LIST_FOREACH(t, &tda->tda_transports, s_active_link) diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index dcda443a..746ac7d6 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1237,7 +1237,11 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { + '

Status

' + '

Currently tuned to:

{currentMux} ' + '

Services:

{services}' + '

Muxes:

{muxes}' - + '

Muxes awaiting initial scan:

{initialMuxes}'); + + '

Muxes awaiting initial scan:

{initialMuxes}' + + '

Signal Strength:

{signal}%' + + '

Bit Error Rate:

{ber}/s' + + '

Uncorrected Bit Errors:

{uncavg}/s' + ); var infoPanel = new Ext.Panel({ title : 'Information and capabilities', diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index 44f999d2..bf3c2cbb 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -1,7 +1,7 @@ /** * */ -tvheadend.status = function() { +tvheadend.status_subs = function() { tvheadend.subsStore = new Ext.data.JsonStore({ root : 'entries', @@ -117,7 +117,8 @@ tvheadend.status = function() { renderer: renderBw } ]); - var panel = new Ext.grid.GridPanel({ + var subs = new Ext.grid.GridPanel({ + border: false, loadMask : true, stripeRows : true, disableSelection : true, @@ -125,10 +126,80 @@ tvheadend.status = function() { iconCls : 'eye', store : tvheadend.subsStore, cm : subsCm, + flex: 1, viewConfig : { forceFit : true } }); + return subs; +} + + +/** + * + */ +tvheadend.status_adapters = function() { + + var signal = new Ext.ux.grid.ProgressColumn({ + header : "Signal Strength", + dataIndex : 'signal', + width : 85, + textPst : '%', + colored : true + }); + + var cm = new Ext.grid.ColumnModel([{ + width : 50, + header : "Name", + dataIndex : 'name' + },{ + width : 50, + header : "Hardware device", + dataIndex : 'path' + },{ + width : 100, + header : "Currently tuned to", + dataIndex : 'currentMux' + },{ + width : 50, + header : "Bit error rate", + dataIndex : 'ber' + },{ + width : 50, + header : "Uncorrected bit error rate", + dataIndex : 'uncavg' + }, signal]); + + var panel = new Ext.grid.GridPanel({ + border: false, + loadMask : true, + stripeRows : true, + disableSelection : true, + title : 'Adapters', + iconCls : 'hardware', + store : tvheadend.tvAdapterStore, + cm : cm, + flex: 1, + viewConfig : { + forceFit : true + } + }); + return panel; +} + + + + +tvheadend.status = function() { + + var panel = new Ext.Panel({ + border: false, + layout : 'vbox', + title : 'Status', + iconCls : 'eye', + items : [ new tvheadend.status_subs, new tvheadend.status_adapters ] + }); + return panel; } diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index 0d3fcc56..d6928ed8 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -5,9 +5,10 @@ tvheadend.tvAdapterStore = new Ext.data.JsonStore({ root : 'entries', id : 'identifier', fields : [ 'identifier', 'type', 'name', 'path', 'devicename', - 'hostconnection', 'currentMux', 'services', 'muxes', 'initialMuxes', - 'satConf', 'deliverySystem', 'freqMin', 'freqMax', 'freqStep', - 'symrateMin', 'symrateMax' ], + 'hostconnection', 'currentMux', 'services', 'muxes', 'initialMuxes', + 'satConf', 'deliverySystem', 'freqMin', 'freqMax', 'freqStep', + 'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg'], + autoLoad : true, url : 'tv/adapter' }); From 2990888509f93eff4123c582908570ad5af799a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 15 Nov 2012 16:54:06 +0100 Subject: [PATCH 096/503] dvb: Include signal status in adapter_msg if we are tuned --- src/dvb/dvb_adapter.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 3a971d02..1847304a 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -1042,8 +1042,16 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "initialMuxes", tda->tda_initial_num_mux); if(tda->tda_mux_current != NULL) { + th_dvb_mux_instance_t *tdmi = tda->tda_mux_current; + dvb_mux_nicename(buf, sizeof(buf), tda->tda_mux_current); htsmsg_add_str(m, "currentMux", buf); + + htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100)); + htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); + htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); + htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); + htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg); } if(tda->tda_rootpath == NULL) From e529c1d2d68f7ed497cd3d9f801f765fcd5dbac5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 15 Nov 2012 16:06:25 +0000 Subject: [PATCH 097/503] htsp: bump version to 7 for 3.4 release. --- src/htsp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index fa137686..612d5f27 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -53,7 +53,7 @@ static void *htsp_server, *htsp_server_2; -#define HTSP_PROTO_VERSION 6 +#define HTSP_PROTO_VERSION 7 #define HTSP_ASYNC_OFF 0x00 #define HTSP_ASYNC_ON 0x01 From 3b475ee66d159e778f41fbafc2c2fbc632dd1b4f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 15 Nov 2012 17:01:03 +0000 Subject: [PATCH 098/503] htsp: some re-structuring and additons to HTSP file support. --- src/htsp_server.c | 201 +++++++++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 82 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 612d5f27..70418b4a 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -358,6 +358,69 @@ htsp_generate_challenge(htsp_connection_t *htsp) return n != 32; } +/* ************************************************************************** + * File helpers + * *************************************************************************/ + +/** + * + */ +static htsmsg_t * +htsp_file_open(htsp_connection_t *htsp, const char *path) +{ + struct stat st; + int fd = open(path, O_RDONLY); + tvhlog(LOG_DEBUG, "HTSP", "Opening file %s -- %s", path, fd < 0 ? strerror(errno) : "OK"); + if(fd == -1) + return htsp_error("Unable to open file"); + + htsp_file_t *hf = calloc(1, sizeof(htsp_file_t)); + hf->hf_fd = fd; + hf->hf_id = ++htsp->htsp_file_id; + hf->hf_path = strdup(path); + LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link); + + htsmsg_t *rep = htsmsg_create_map(); + htsmsg_add_u32(rep, "id", hf->hf_id); + + if(!fstat(hf->hf_fd, &st)) { + htsmsg_add_u64(rep, "size", st.st_size); + htsmsg_add_u64(rep, "mtime", st.st_mtime); + } + + return rep; +} + +/** + * + */ +static htsp_file_t * +htsp_file_find(const htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf; + + int id = htsmsg_get_u32_or_default(in, "id", 0); + + LIST_FOREACH(hf, &htsp->htsp_files, hf_link) { + if(hf->hf_id == id) + return hf; + } + return NULL; +} + +/** + * + */ +static void +htsp_file_destroy(htsp_file_t *hf) +{ + tvhlog(LOG_DEBUG, "HTSP", "Closed opened file %s", hf->hf_path); + free(hf->hf_path); + close(hf->hf_fd); + LIST_REMOVE(hf, hf_link); + free(hf); +} + /* ************************************************************************** * Output message generators * *************************************************************************/ @@ -1249,37 +1312,6 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in) return NULL; } - -/** - * - */ -static htsmsg_t * -htsp_method_open_path(htsp_connection_t *htsp, const char *path) -{ - struct stat st; - int fd = open(path, O_RDONLY); - tvhlog(LOG_DEBUG, "HTSP", "Opening file %s -- %s", path, fd < 0 ? strerror(errno) : "OK"); - if(fd == -1) - return htsp_error("Unable to open file"); - - htsp_file_t *hf = calloc(1, sizeof(htsp_file_t)); - hf->hf_fd = fd; - hf->hf_id = ++htsp->htsp_file_id; - hf->hf_path = strdup(path); - LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link); - - htsmsg_t *rep = htsmsg_create_map(); - htsmsg_add_u32(rep, "id", hf->hf_id); - - if(!fstat(hf->hf_fd, &st)) { - htsmsg_add_u64(rep, "size", st.st_size); - htsmsg_add_u64(rep, "mtime", st.st_mtime); - } - - return rep; -} - - /** * Open file */ @@ -1304,50 +1336,31 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) return htsp_error("Unknown file"); } - return htsp_method_open_path(htsp, filename); + return htsp_file_open(htsp, filename); } -/** - * - */ -static htsp_file_t * -file_find(const htsp_connection_t *htsp, htsmsg_t *in) -{ - htsp_file_t *hf; - - int id = htsmsg_get_u32_or_default(in, "id", 0); - - LIST_FOREACH(hf, &htsp->htsp_files, hf_link) { - if(hf->hf_id == id) - return hf; - } - return NULL; -} - - - /** * */ static htsmsg_t * htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in) { - htsp_file_t *hf = file_find(htsp, in); + htsp_file_t *hf = htsp_file_find(htsp, in); uint64_t off; - uint32_t size; - - if(htsmsg_get_u64(in, "offset", &off)) - return htsp_error("Missing field 'offset'"); - - if(htsmsg_get_u32(in, "size", &size)) - return htsp_error("Missing field 'size'"); + uint64_t size; if(hf == NULL) return htsp_error("Unknown file id"); - if(lseek(hf->hf_fd, off, SEEK_SET) != off) - return htsp_error("Seek error"); + if(htsmsg_get_u64(in, "size", &size)) + return htsp_error("Missing field 'size'"); + /* Seek (optional) */ + if (!htsmsg_get_u64(in, "offset", &off)) + if(lseek(hf->hf_fd, off, SEEK_SET) != off) + return htsp_error("Seek error"); + + /* Read */ void *m = malloc(size); if(m == NULL) return htsp_error("Too big segment"); @@ -1364,27 +1377,13 @@ htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in) return rep; } - -/** - * - */ -static void -htsp_file_destroy(htsp_file_t *hf) -{ - tvhlog(LOG_DEBUG, "HTSP", "Closed opened file %s", hf->hf_path); - free(hf->hf_path); - close(hf->hf_fd); - LIST_REMOVE(hf, hf_link); - free(hf); -} - /** * */ static htsmsg_t * htsp_method_file_close(htsp_connection_t *htsp, htsmsg_t *in) { - htsp_file_t *hf = file_find(htsp, in); + htsp_file_t *hf = htsp_file_find(htsp, in); if(hf == NULL) return htsp_error("Unknown file id"); @@ -1393,14 +1392,13 @@ htsp_method_file_close(htsp_connection_t *htsp, htsmsg_t *in) return htsmsg_create_map(); } - /** * */ static htsmsg_t * htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in) { - htsp_file_t *hf = file_find(htsp, in); + htsp_file_t *hf = htsp_file_find(htsp, in); struct stat st; if(hf == NULL) @@ -1414,6 +1412,44 @@ htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in) return rep; } +/** + * + */ +static htsmsg_t * +htsp_method_file_seek(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_file_t *hf = htsp_file_find(htsp, in); + htsmsg_t *rep; + const char *str; + int64_t off; + int whence; + + if(hf == NULL) + return htsp_error("Unknown file id"); + + if (htsmsg_get_s64(in, "offset", &off)) + return htsp_error("Missing field 'offset'"); + + if ((str = htsmsg_get_str(in, "whence"))) { + if (!strcmp(str, "SEEK_SET")) + whence = SEEK_SET; + else if (!strcmp(str, "SEEK_CUR")) + whence = SEEK_CUR; + else if (!strcmp(str, "SEEK_END")) + whence = SEEK_END; + else + return htsp_error("Field 'whence' contained invalid value"); + } else { + whence = SEEK_CUR; + } + + if(lseek(hf->hf_fd, off, whence) != off) + return htsp_error("Seek error"); + + rep = htsmsg_create_map(); + htsmsg_add_s64(rep, "offset", off); + return rep; +} /** * HTSP methods @@ -1440,10 +1476,11 @@ struct { { "subscribe", htsp_method_subscribe, ACCESS_STREAMING}, { "unsubscribe", htsp_method_unsubscribe, ACCESS_STREAMING}, { "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING}, - { "openFile", htsp_method_file_open, ACCESS_RECORDER}, - { "readFile", htsp_method_file_read, ACCESS_RECORDER}, - { "closeFile", htsp_method_file_close, ACCESS_RECORDER}, - { "statFile", htsp_method_file_stat, ACCESS_RECORDER}, + { "fileOpen", htsp_method_file_open, ACCESS_RECORDER}, + { "fileRead", htsp_method_file_read, ACCESS_RECORDER}, + { "fileClose", htsp_method_file_close, ACCESS_RECORDER}, + { "fileStat", htsp_method_file_stat, ACCESS_RECORDER}, + { "fileSeek", htsp_method_file_seek, ACCESS_RECORDER}, }; #define NUM_METHODS (sizeof(htsp_methods) / sizeof(htsp_methods[0])) From 02e95bcd25b62e4dc16268baf0945fd7b4d61e9c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 15 Nov 2012 17:01:44 +0000 Subject: [PATCH 099/503] htsp: Provide recording path relative to dvr storage path. This can be useful clients to build recording (folder) hierarchy such as supported by XBMC. --- src/htsp_server.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index 70418b4a..34f94d78 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -510,6 +510,8 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method) { htsmsg_t *out = htsmsg_create_map(); const char *s = NULL, *error = NULL; + const char *p; + dvr_config_t *cfg; htsmsg_add_u32(out, "id", de->de_id); htsmsg_add_u32(out, "channel", de->de_channel->ch_id); @@ -522,6 +524,13 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method) if( de->de_desc && (s = lang_str_get(de->de_desc, NULL))) htsmsg_add_str(out, "description", s); + if( de->de_filename && de->de_config_name ) { + if ((cfg = dvr_config_find_by_name_default(de->de_config_name))) { + if ((p = tvh_strbegins(de->de_filename, cfg->dvr_storage))) + htsmsg_add_str(out, "path", p); + } + } + switch(de->de_sched_state) { case DVR_SCHEDULED: s = "scheduled"; From 3de3fe191aab05b327de1a8a1e17603bd0bc8d4c Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Mon, 12 Nov 2012 19:05:36 +0100 Subject: [PATCH 100/503] [PR-175] capmt: ignoring removal requests, fix segfault --- src/capmt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/capmt.c b/src/capmt.c index fb015fb1..c6685228 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -368,6 +368,8 @@ handle_ca0(capmt_t* capmt) { } else if (*request == CA_SET_DESCR) { ca = (ca_descr_t *)&buffer[sizeof(int)]; tvhlog(LOG_DEBUG, "capmt", "CA_SET_DESCR cai %d req %d par %d idx %d %02x%02x%02x%02x%02x%02x%02x%02x", cai, *request, ca->parity, ca->index, ca->cw[0], ca->cw[1], ca->cw[2], ca->cw[3], ca->cw[4], ca->cw[5], ca->cw[6], ca->cw[7]); + if (ca->index == -1) // skipping removal request + continue; if(ca->parity==0) { memcpy(&ca_info[cai][ca->index][EVEN_OFF],ca->cw,KEY_SIZE); // even key From 7943c8b6848fbd6fccaf7743e698a74a96bb56b5 Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Sun, 7 Oct 2012 19:40:38 +0200 Subject: [PATCH 101/503] [PR-175] capmt: add support for on-demand sockets per service id This commit fixes simultaneous channel decryption in capmt oscam mode. Initial channel PMT data and PMT updates are sent to oscam via different capmt socket connections (a socket is created per channel SID). OScam is stopping decrypting when the socket is closed. More details in this thread: http://www.streamboard.tv/wbb2/thread.php?threadid=33323 Thanks to @posixx for days of testing :) --- src/capmt.c | 174 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 36 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index c6685228..1bfc0300 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -77,6 +77,7 @@ #define INFO_SIZE (2+KEY_SIZE+KEY_SIZE) #define EVEN_OFF (2) #define ODD_OFF (2+KEY_SIZE) +#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux static unsigned char ca_info[MAX_CA][MAX_INDEX][INFO_SIZE]; /** @@ -189,7 +190,8 @@ typedef struct capmt { int capmt_oscam; /* capmt sockets */ - int capmt_sock; + int sids[MAX_SOCKETS]; + int capmt_sock[MAX_SOCKETS]; int capmt_sock_ca0[MAX_CA]; /* thread flags */ @@ -206,43 +208,128 @@ typedef struct capmt { * */ static int -capmt_send_msg(capmt_t *capmt, const uint8_t *buf, size_t len) +capmt_send_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len) { - return write(capmt->capmt_sock, buf, len); + if (capmt->capmt_oscam) { + int i, sent = 0; + + // dumping current SID table + for (i = 0; i < MAX_SOCKETS; i++) + tvhlog(LOG_DEBUG, "capmt", "%s: SOCKETS TABLE DUMP [%d]: sid=%d socket=%d", __FUNCTION__, i, capmt->sids[i], capmt->capmt_sock[i]); + if (sid == 0) { + tvhlog(LOG_DEBUG, "capmt", "%s: got empty SID - returning from function", __FUNCTION__); + return -1; + } + + // searching for the SID and socket + int found = 0; + for (i = 0; i < MAX_SOCKETS; i++) { + if (capmt->sids[i] == sid) { + found = 1; + break; + } + } + + if (found) + tvhlog(LOG_DEBUG, "capmt", "%s: found sid, reusing socket, i=%d", __FUNCTION__, i); + else { //not found - adding to first free in table + for (i = 0; i < MAX_SOCKETS; i++) { + if (capmt->sids[i] == 0) { + capmt->sids[i] = sid; + break; + } + } + } + if (i == MAX_SOCKETS) { + tvhlog(LOG_DEBUG, "capmt", "%s: no free space for new SID!!!", __FUNCTION__); + return -1; + } else { + capmt->sids[i] = sid; + tvhlog(LOG_DEBUG, "capmt", "%s: added: i=%d", __FUNCTION__, i); + } + + // opening socket and sending + if (capmt->capmt_sock[i] == 0) { + capmt->capmt_sock[i] = tvh_socket(AF_LOCAL, SOCK_STREAM, 0); + + struct sockaddr_un serv_addr_un; + memset(&serv_addr_un, 0, sizeof(serv_addr_un)); + serv_addr_un.sun_family = AF_LOCAL; + snprintf(serv_addr_un.sun_path, sizeof(serv_addr_un.sun_path), "%s", capmt->capmt_sockfile); + + if (connect(capmt->capmt_sock[i], (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) != 0) { + tvhlog(LOG_ERR, "capmt", "Cannot connect to %s, Do you have OSCam running?", capmt->capmt_sockfile); + capmt->capmt_sock[i] = 0; + } else + tvhlog(LOG_DEBUG, "capmt", "created socket with socket_fd=%d", capmt->capmt_sock[i]); + } + if (capmt->capmt_sock[i] > 0) { + sent = write(capmt->capmt_sock[i], buf, len); + tvhlog(LOG_DEBUG, "capmt", "socket_fd=%d len=%d sent=%d", capmt->capmt_sock[i], (int)len, sent); + if (sent != len) { + tvhlog(LOG_ERR, "capmt", "%s: len != sent", __FUNCTION__); + close(capmt->capmt_sock[i]); + capmt->capmt_sock[i] = 0; + } + } + return sent; + } + else // standard old capmt mode + return write(capmt->capmt_sock[0], buf, len); } static void capmt_send_stop(capmt_service_t *t) { - /* buffer for capmt */ - int pos = 0; - uint8_t buf[4094]; + if (t->ct_capmt->capmt_oscam) { + int i; + // searching for socket to close + for (i = 0; i < MAX_SOCKETS; i++) + if (t->ct_capmt->sids[i] == t->ct_service->s_dvb_service_id) + break; - capmt_header_t head = { - .capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 }, - .capmt_list_management = CAPMT_LIST_ONLY, - .program_number = t->ct_service->s_dvb_service_id, - .version_number = 0, - .current_next_indicator = 0, - .program_info_length = 0, - .capmt_cmd_id = CAPMT_CMD_NOT_SELECTED, - }; - memcpy(&buf[pos], &head, sizeof(head)); - pos += sizeof(head); + if (i == MAX_SOCKETS) { + tvhlog(LOG_DEBUG, "capmt", "%s: socket to close not found", __FUNCTION__); + return; + } - uint8_t end[] = { - 0x01, (t->ct_seq >> 8) & 0xFF, t->ct_seq & 0xFF, 0x00, 0x06 }; - memcpy(&buf[pos], end, sizeof(end)); - pos += sizeof(end); - buf[4] = ((pos - 6) >> 8); - buf[5] = ((pos - 6) & 0xFF); - buf[7] = t->ct_service->s_dvb_service_id >> 8; - buf[8] = t->ct_service->s_dvb_service_id & 0xFF; - buf[9] = 1; - buf[10] = ((pos - 5 - 12) & 0xF00) >> 8; - buf[11] = ((pos - 5 - 12) & 0xFF); + // closing socket (oscam handle this as event and stop decrypting) + tvhlog(LOG_DEBUG, "capmt", "%s: closing socket i=%d, socket_fd=%d", __FUNCTION__, i, t->ct_capmt->capmt_sock[i]); + t->ct_capmt->sids[i] = 0; + if (t->ct_capmt->capmt_sock[i] > 0) + close(t->ct_capmt->capmt_sock[i]); + t->ct_capmt->capmt_sock[i] = 0; + } else { // standard old capmt mode + /* buffer for capmt */ + int pos = 0; + uint8_t buf[4094]; + + capmt_header_t head = { + .capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 }, + .capmt_list_management = CAPMT_LIST_ONLY, + .program_number = t->ct_service->s_dvb_service_id, + .version_number = 0, + .current_next_indicator = 0, + .program_info_length = 0, + .capmt_cmd_id = CAPMT_CMD_NOT_SELECTED, + }; + memcpy(&buf[pos], &head, sizeof(head)); + pos += sizeof(head); + + uint8_t end[] = { + 0x01, (t->ct_seq >> 8) & 0xFF, t->ct_seq & 0xFF, 0x00, 0x06 }; + memcpy(&buf[pos], end, sizeof(end)); + pos += sizeof(end); + buf[4] = ((pos - 6) >> 8); + buf[5] = ((pos - 6) & 0xFF); + buf[7] = t->ct_service->s_dvb_service_id >> 8; + buf[8] = t->ct_service->s_dvb_service_id & 0xFF; + buf[9] = 1; + buf[10] = ((pos - 5 - 12) & 0xF00) >> 8; + buf[11] = ((pos - 5 - 12) & 0xFF); - capmt_send_msg(t->ct_capmt, buf, pos); + capmt_send_msg(t->ct_capmt, t->ct_service->s_dvb_service_id, buf, pos); + } } /** @@ -457,9 +544,12 @@ capmt_thread(void *aux) th_dvb_adapter_t *tda; while (capmt->capmt_running) { - capmt->capmt_sock = -1; for (i = 0; i < MAX_CA; i++) capmt->capmt_sock_ca0[i] = -1; + for (i = 0; i < MAX_SOCKETS; i++) { + capmt->sids[i] = 0; + capmt->capmt_sock[i] = 0; + } capmt->capmt_connected = 0; pthread_mutex_lock(&global_lock); @@ -470,14 +560,14 @@ capmt_thread(void *aux) pthread_mutex_unlock(&global_lock); /* open connection to camd.socket */ - capmt->capmt_sock = tvh_socket(AF_LOCAL, SOCK_STREAM, 0); + capmt->capmt_sock[0] = tvh_socket(AF_LOCAL, SOCK_STREAM, 0); struct sockaddr_un serv_addr_un; memset(&serv_addr_un, 0, sizeof(serv_addr_un)); serv_addr_un.sun_family = AF_LOCAL; snprintf(serv_addr_un.sun_path, sizeof(serv_addr_un.sun_path), "%s", capmt->capmt_sockfile); - if (connect(capmt->capmt_sock, (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) == 0) { + if (connect(capmt->capmt_sock[0], (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) == 0) { capmt->capmt_connected = 1; /* open connection to emulated ca0 device */ @@ -503,8 +593,9 @@ capmt_thread(void *aux) capmt->capmt_connected = 0; /* close opened sockets */ - if (capmt->capmt_sock > 0) - close(capmt->capmt_sock); + for (i = 0; i < MAX_SOCKETS; i++) + if (capmt->capmt_sock[i] > 0) + close(capmt->capmt_sock[i]); for (i = 0; i < MAX_CA; i++) if (capmt->capmt_sock_ca0[i] > 0) close(capmt->capmt_sock_ca0[i]); @@ -540,6 +631,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, capmt_t *capmt = ct->ct_capmt; int adapter_num = t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num; int total_caids = 0, current_caid = 0; + int i; caid_t *c; @@ -587,7 +679,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, if ((cce->cce_ecmsize == len) && !memcmp(cce->cce_ecm, data, len)) break; /* key already sent */ - if(capmt->capmt_sock == -1) { + if(!capmt->capmt_oscam && capmt->capmt_sock[0] == 0) { /* New key, but we are not connected (anymore), can not descramble */ ct->ct_keystate = CT_UNKNOWN; break; @@ -718,7 +810,17 @@ capmt_table_input(struct th_descrambler *td, struct service *t, buf[9] = pmtversion; pmtversion = (pmtversion + 1) & 0x1F; - capmt_send_msg(capmt, buf, pos); + int found = 0; + if (capmt->capmt_oscam) { + for (i = 0; i < MAX_SOCKETS; i++) { + if (capmt->sids[i] == sid) { + found = 1; + break; + } + } + } + if ((capmt->capmt_oscam && !found) || !capmt->capmt_oscam) + capmt_send_msg(capmt, sid, buf, pos); break; } default: From 249b4c1d0f077ef70fd59c0d7e6b8a6aa33ab213 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 16 Nov 2012 11:06:46 +0000 Subject: [PATCH 102/503] htsp: fix mistakes in fileSeek handler --- src/htsp_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 34f94d78..ef49a7c4 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1449,10 +1449,10 @@ htsp_method_file_seek(htsp_connection_t *htsp, htsmsg_t *in) else return htsp_error("Field 'whence' contained invalid value"); } else { - whence = SEEK_CUR; + whence = SEEK_SET; } - if(lseek(hf->hf_fd, off, whence) != off) + if ((off = lseek(hf->hf_fd, off, whence)) < 0) return htsp_error("Seek error"); rep = htsmsg_create_map(); From ed5d7e4977f0090f215898a297ae8906d7e9e879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Fri, 16 Nov 2012 16:27:29 +0100 Subject: [PATCH 103/503] make sure there is metadata available before atempting to mux it with a recording --- src/dvr/dvr_rec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index dd0effd7..9dd3c7f5 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -351,7 +351,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) return -1; } - if(cfg->dvr_flags & DVR_TAG_FILES) { + if(cfg->dvr_flags & DVR_TAG_FILES && de->de_bcast) { if(muxer_write_meta(de->de_mux, de->de_bcast)) { dvr_rec_fatal_error(de, "Unable to write meta data"); return -1; From 777e4108b96e6fa15ce3ed2bd0592a3e427bcea2 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 17 Nov 2012 12:43:41 +0000 Subject: [PATCH 104/503] webui: Fix mistake in previous update to iptv.js --- src/webui/static/app/iptv.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webui/static/app/iptv.js b/src/webui/static/app/iptv.js index 4cd4f35e..e1c9a41b 100644 --- a/src/webui/static/app/iptv.js +++ b/src/webui/static/app/iptv.js @@ -113,10 +113,10 @@ tvheadend.iptv = function(adapterId) { editable : false, mode : 'local', triggerAction : 'all', - store : tvheadend.servicetypeStore + store : servicetypeStore }), renderer : function(value, metadata, record, row, col, store) { - var val = value ? tvheadend.servicetypeStore.getById(value) : null; + var val = value ? servicetypeStore.getById(value) : null; return val ? val.get('str') : 'Unset'; } From 442d69544530fb0deea4a524512db0a6a3899f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 17 Nov 2012 15:32:23 +0100 Subject: [PATCH 105/503] make sure the default file descriptor for the passthrough muxer is an invalid one, not stdout. --- src/muxer_pass.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 299506d8..f6658ad5 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -334,7 +334,8 @@ pass_muxer_create(muxer_container_type_t mc) pm->m_write_pkt = pass_muxer_write_pkt; pm->m_close = pass_muxer_close; pm->m_destroy = pass_muxer_destroy; - + pm->pm_fd = -1; + return (muxer_t *)pm; } From 34ff0c20dba8413c2d833a01a78964089d2c70da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 17 Nov 2012 15:31:07 +0100 Subject: [PATCH 106/503] make sure the recording has been started correctly before atempting to write packets to the muxer. --- src/dvr/dvr_rec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 9dd3c7f5..1e279e7b 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -448,7 +448,8 @@ dvr_thread(void *aux) switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: - if(dispatch_clock > de->de_start - (60 * de->de_start_extra)) { + if(started && + dispatch_clock > de->de_start - (60 * de->de_start_extra)) { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); From 70f0801c9b30e0704835f0a085aaecb8c7a562f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 17 Nov 2012 15:35:40 +0100 Subject: [PATCH 107/503] make sure the default file descriptor for the matroska muxer is an invalid one, not stdout. --- src/dvr/mkmux.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dvr/mkmux.c b/src/dvr/mkmux.c index cf95e95e..de9d8dff 100644 --- a/src/dvr/mkmux.c +++ b/src/dvr/mkmux.c @@ -801,6 +801,9 @@ mk_write_cues(mk_mux_t *mkm) mk_mux_t *mk_mux_create(void) { mk_mux_t *mkm = calloc(1, sizeof(struct mk_mux)); + + mkm->fd = -1; + return mkm; } From be20dd6488f920798c2825f566952686a1713c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 09:17:14 +0100 Subject: [PATCH 108/503] HTSP: seems writer must deal with EAGAIN and EWOULDBLOCK --- src/htsp_server.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index ef49a7c4..dc17c199 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1705,12 +1705,17 @@ htsp_write_scheduler(void *aux) while(dlen > 0) { r = write(htsp->htsp_fd, dptr, dlen); - if(r < 1) { + if(r < 0) { + if(errno == EAGAIN || errno == EWOULDBLOCK) + continue; tvhlog(LOG_INFO, "htsp", "%s: Write error -- %s", htsp->htsp_logname, strerror(errno)); break; } - + if(r == 0) { + tvhlog(LOG_ERR, "htsp", "%s: write() returned 0", + htsp->htsp_logname); + } dptr += r; dlen -= r; } From 5a07328a9a9a583cda25def5bacfb86c31ed8984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 09:17:44 +0100 Subject: [PATCH 109/503] HTPS: Maintain a global list of HTSP connections This is useful for post mortem debugging --- src/htsp_server.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index dc17c199..05ff068f 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -71,6 +71,7 @@ TAILQ_HEAD(htsp_msg_queue, htsp_msg); TAILQ_HEAD(htsp_msg_q_queue, htsp_msg_q); static struct htsp_connection_list htsp_async_connections; +static struct htsp_connection_list htsp_connections; static void htsp_streaming_input(void *opaque, streaming_message_t *sm); @@ -107,6 +108,8 @@ typedef struct htsp_msg_q { * */ typedef struct htsp_connection { + LIST_ENTRY(htsp_connection) htsp_link; + int htsp_fd; struct sockaddr_in *htsp_peer; @@ -1760,6 +1763,10 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, htsp.htsp_peer = source; htsp.htsp_writer_run = 1; + pthread_mutex_lock(&global_lock); + LIST_INSERT_HEAD(&htsp_connections, &htsp, htsp_link); + pthread_mutex_unlock(&global_lock); + pthread_create(&htsp.htsp_writer_thread, NULL, htsp_write_scheduler, &htsp); /** @@ -1786,6 +1793,8 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, if(htsp.htsp_async_mode) LIST_REMOVE(&htsp, htsp_async_link); + LIST_REMOVE(&htsp, htsp_link); + pthread_mutex_unlock(&global_lock); pthread_mutex_lock(&htsp.htsp_out_mutex); From 01db78aea058d62d7b5bbeaf5192a171899c0fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 13:16:58 +0100 Subject: [PATCH 110/503] Upgrade htsmsg code from Showtime's codebase We do this to get access to floating point in JSON --- Makefile | 2 + src/htsbuf.c | 182 ++++++++++++++++--- src/htsbuf.h | 29 ++- src/htsmsg.c | 174 +++++++++++++----- src/htsmsg.h | 114 +++++++----- src/htsmsg_json.c | 454 ++++++++++------------------------------------ src/htsmsg_json.h | 10 +- src/htsp_server.c | 25 +-- src/misc/dbl.c | 295 ++++++++++++++++++++++++++++++ src/misc/dbl.h | 5 + src/misc/json.c | 432 +++++++++++++++++++++++++++++++++++++++++++ src/misc/json.h | 31 ++++ 12 files changed, 1255 insertions(+), 498 deletions(-) create mode 100644 src/misc/dbl.c create mode 100644 src/misc/dbl.h create mode 100644 src/misc/json.c create mode 100644 src/misc/json.h diff --git a/Makefile b/Makefile index b1ed4b52..01f372df 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,8 @@ SRCS = src/main.c \ src/htsmsg_binary.c \ src/htsmsg_json.c \ src/htsmsg_xml.c \ + src/misc/dbl.c \ + src/misc/json.c \ src/settings.c \ src/htsbuf.c \ src/trap.c \ diff --git a/src/htsbuf.c b/src/htsbuf.c index ff859c18..03f49a3a 100644 --- a/src/htsbuf.c +++ b/src/htsbuf.c @@ -1,6 +1,6 @@ /* * Buffer management functions - * Copyright (C) 2008 Andreas Öman + * Copyright (C) 2008 Andreas Öman * * 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 @@ -22,17 +22,9 @@ #include #include #include -#include "htsbuf.h" + #include "tvheadend.h" - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - +#include "htsbuf.h" /** * @@ -206,6 +198,7 @@ htsbuf_peek(htsbuf_queue_t *hq, void *buf, size_t len) c = MIN(hd->hd_data_len - hd->hd_data_off, len); memcpy(buf, hd->hd_data + hd->hd_data_off, c); + r += c; buf += c; len -= c; @@ -233,7 +226,7 @@ htsbuf_drop(htsbuf_queue_t *hq, size_t len) len -= c; hd->hd_data_off += c; hq->hq_size -= c; - + r += c; if(hd->hd_data_off == hd->hd_data_len) htsbuf_data_free(hq, hd); } @@ -314,19 +307,162 @@ htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src) } -/** - * - */ -uint32_t -htsbuf_crc32(htsbuf_queue_t *hq, uint32_t crc) +void +htsbuf_dump_raw_stderr(htsbuf_queue_t *hq) { htsbuf_data_t *hd; - - TAILQ_FOREACH(hd, &hq->hq_q, hd_link) - crc = tvh_crc32(hd->hd_data + hd->hd_data_off, - hd->hd_data_len - hd->hd_data_off, - crc); - return crc; + char n = '\n'; + + TAILQ_FOREACH(hd, &hq->hq_q, hd_link) { + if(write(2, hd->hd_data + hd->hd_data_off, + hd->hd_data_len - hd->hd_data_off) + != hd->hd_data_len - hd->hd_data_off) + break; + } + if(write(2, &n, 1) != 1) + return; } +void +htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix) +{ + void *r = malloc(hq->hq_size); + htsbuf_peek(hq, r, hq->hq_size); + hexdump(prefix, r, hq->hq_size); + free(r); +} + + +/** + * + */ +void +htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *s) +{ + const char *c = s; + const char *e = s + strlen(s); + if(e == s) + return; + + while(1) { + const char *esc; + switch(*c++) { + case '<': esc = "<"; break; + case '>': esc = ">"; break; + case '&': esc = "&"; break; + case '\'': esc = "'"; break; + case '"': esc = """; break; + default: esc = NULL; break; + } + + if(esc != NULL) { + htsbuf_append(hq, s, c - s - 1); + htsbuf_append(hq, esc, strlen(esc)); + s = c; + } + + if(c == e) { + htsbuf_append(hq, s, c - s); + break; + } + } +} + + +/** + * + */ +void +htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s) +{ + const char *c = s; + const char *e = s + strlen(s); + char C; + if(e == s) + return; + + while(1) { + const char *esc; + C = *c++; + + if((C >= '0' && C <= '9') || + (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z') || + C == '_' || + C == '~' || + C == '.' || + C == '-') { + esc = NULL; + } else { + static const char hexchars[16] = "0123456789ABCDEF"; + char buf[4]; + buf[0] = '%'; + buf[1] = hexchars[(C >> 4) & 0xf]; + buf[2] = hexchars[C & 0xf];; + buf[3] = 0; + esc = buf; + } + + if(esc != NULL) { + htsbuf_append(hq, s, c - s - 1); + htsbuf_append(hq, esc, strlen(esc)); + s = c; + } + + if(c == e) { + htsbuf_append(hq, s, c - s); + break; + } + } +} + + +/** + * + */ +void +htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *str) +{ + const char *s = str; + + htsbuf_append(hq, "\"", 1); + + while(*s != 0) { + if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\r' || *s == '\t') { + htsbuf_append(hq, str, s - str); + + if(*s == '"') + htsbuf_append(hq, "\\\"", 2); + else if(*s == '\n') + htsbuf_append(hq, "\\n", 2); + else if(*s == '\r') + htsbuf_append(hq, "\\r", 2); + else if(*s == '\t') + htsbuf_append(hq, "\\t", 2); + else + htsbuf_append(hq, "\\\\", 2); + s++; + str = s; + } else { + s++; + } + } + htsbuf_append(hq, str, s - str); + htsbuf_append(hq, "\"", 1); +} + + + +/** + * + */ +char * +htsbuf_to_string(htsbuf_queue_t *hq) +{ + char *r = malloc(hq->hq_size + 1); + r[hq->hq_size] = 0; + htsbuf_read(hq, r, hq->hq_size); + return r; +} + diff --git a/src/htsbuf.h b/src/htsbuf.h index 13aaedb5..ae9a1322 100644 --- a/src/htsbuf.h +++ b/src/htsbuf.h @@ -1,6 +1,6 @@ /* * Buffer management functions - * Copyright (C) 2008 Andreas Öman + * Copyright (C) 2008 Andreas Öman * * 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 @@ -20,10 +20,11 @@ #define HTSBUF_H__ #include -#include -#include "queue.h" #include +#include "queue.h" + + TAILQ_HEAD(htsbuf_data_queue, htsbuf_data); typedef struct htsbuf_data { @@ -48,15 +49,12 @@ void htsbuf_queue_flush(htsbuf_queue_t *hq); void htsbuf_vqprintf(htsbuf_queue_t *hq, const char *fmt, va_list ap); -void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...) - __attribute__((format(printf,2,3))); +void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...); void htsbuf_append(htsbuf_queue_t *hq, const void *buf, size_t len); void htsbuf_append_prealloc(htsbuf_queue_t *hq, const void *buf, size_t len); -void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src); - void htsbuf_data_free(htsbuf_queue_t *hq, htsbuf_data_t *hd); size_t htsbuf_read(htsbuf_queue_t *hq, void *buf, size_t len); @@ -67,6 +65,21 @@ size_t htsbuf_drop(htsbuf_queue_t *hq, size_t len); size_t htsbuf_find(htsbuf_queue_t *hq, uint8_t v); -uint32_t htsbuf_crc32(htsbuf_queue_t *q, uint32_t crc); +void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src); + +void htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *str); + +void htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s); + +void htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *s); + +void htsbuf_dump_raw_stderr(htsbuf_queue_t *hq); + +char *htsbuf_to_string(htsbuf_queue_t *hq); + +struct rstr; +struct rstr *htsbuf_to_rstr(htsbuf_queue_t *hq, const char *prefix); + +void htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix); #endif /* HTSBUF_H__ */ diff --git a/src/htsmsg.c b/src/htsmsg.c index 5dd67e3a..935c30b6 100644 --- a/src/htsmsg.c +++ b/src/htsmsg.c @@ -1,6 +1,6 @@ /* * Functions for manipulating HTS messages - * Copyright (C) 2007 Andreas Öman + * Copyright (C) 2007 Andreas Öman * * 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 @@ -27,10 +27,10 @@ static void htsmsg_clear(htsmsg_t *msg); -/* +/** * */ -static void +void htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f) { TAILQ_REMOVE(&msg->hm_fields, f, hmf_link); @@ -102,7 +102,7 @@ htsmsg_field_add(htsmsg_t *msg, const char *name, int type, int flags) /* * */ -static htsmsg_field_t * +htsmsg_field_t * htsmsg_field_find(htsmsg_t *msg, const char *name) { htsmsg_field_t *f; @@ -196,16 +196,6 @@ htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64) f->hmf_s64 = s64; } -/* - * - */ -void -htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64) -{ - htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED); - f->hmf_s64 = u64; -} - /* * */ @@ -217,6 +207,17 @@ htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32) } +/* + * + */ +void +htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl) +{ + htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_DBL, HMF_NAME_ALLOCED); + f->hmf_dbl = dbl; +} + + /* * @@ -267,8 +268,8 @@ htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub) HMF_NAME_ALLOCED); assert(sub->hm_data == NULL); - TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link); f->hmf_msg.hm_islist = sub->hm_islist; + TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link); free(sub); } @@ -312,10 +313,14 @@ htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p) case HMF_S64: *s64p = f->hmf_s64; break; + case HMF_DBL: + *s64p = f->hmf_dbl; + break; } return 0; } + /** * */ @@ -326,30 +331,6 @@ htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def) return htsmsg_get_s64(msg, name, &s64) ? def : s64; } -/** - * - */ -int -htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p) -{ - htsmsg_field_t *f; - - if((f = htsmsg_field_find(msg, name)) == NULL) - return HTSMSG_ERR_FIELD_NOT_FOUND; - - switch(f->hmf_type) { - default: - return HTSMSG_ERR_CONVERSION_IMPOSSIBLE; - case HMF_STR: - *u64p = strtoull(f->hmf_str, NULL, 0); - break; - case HMF_S64: - *u64p = f->hmf_s64; - break; - } - return 0; -} - /* * @@ -373,13 +354,26 @@ htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p) /** * */ -uint32_t +int htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def) { uint32_t u32; return htsmsg_get_u32(msg, name, &u32) ? def : u32; } + +/** + * + */ +int32_t +htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name, int32_t def) +{ + int32_t s32; + return htsmsg_get_s32(msg, name, &s32) ? def : s32; +} + + + /* * */ @@ -400,6 +394,25 @@ htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p) } +/* + * + */ +int +htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp) +{ + htsmsg_field_t *f; + + if((f = htsmsg_field_find(msg, name)) == NULL) + return HTSMSG_ERR_FIELD_NOT_FOUND; + + if(f->hmf_type != HMF_DBL) + return HTSMSG_ERR_CONVERSION_IMPOSSIBLE; + + *dblp = f->hmf_dbl; + return 0; +} + + /* * */ @@ -457,13 +470,6 @@ htsmsg_get_str(htsmsg_t *msg, const char *name) } -const char * -htsmsg_get_str_or_default(htsmsg_t *msg, const char *name, const char *def) -{ - const char *str = htsmsg_get_str(msg, name); - return str ?: def; -} - /* * */ @@ -493,6 +499,32 @@ htsmsg_get_map_multi(htsmsg_t *msg, ...) return msg; } +/** + * + */ +const char * +htsmsg_get_str_multi(htsmsg_t *msg, ...) +{ + va_list ap; + const char *n; + htsmsg_field_t *f; + va_start(ap, msg); + + while((n = va_arg(ap, char *)) != NULL) { + if((f = htsmsg_field_find(msg, n)) == NULL) + return NULL; + else if(f->hmf_type == HMF_STR) + return f->hmf_str; + else if(f->hmf_type == HMF_MAP) + msg = &f->hmf_msg; + else + return NULL; + } + return NULL; +} + + + /* * */ @@ -565,6 +597,10 @@ htsmsg_print0(htsmsg_t *msg, int indent) case HMF_S64: printf("S64) = %" PRId64 "\n", f->hmf_s64); break; + + case HMF_DBL: + printf("DBL) = %f\n", f->hmf_dbl); + break; } } } @@ -611,6 +647,10 @@ htsmsg_copy_i(htsmsg_t *src, htsmsg_t *dst) case HMF_BIN: htsmsg_add_bin(dst, f->hmf_name, f->hmf_bin, f->hmf_binsize); break; + + case HMF_DBL: + htsmsg_add_dbl(dst, f->hmf_name, f->hmf_dbl); + break; } } } @@ -623,6 +663,45 @@ htsmsg_copy(htsmsg_t *src) return dst; } +/** + * + */ +htsmsg_t * +htsmsg_get_map_in_list(htsmsg_t *m, int num) +{ + htsmsg_field_t *f; + + HTSMSG_FOREACH(f, m) { + if(!--num) + return htsmsg_get_map_by_field(f); + } + return NULL; +} + + +/** + * + */ +htsmsg_t * +htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name) +{ + if(f->hmf_type != HMF_MAP) + return NULL; + if(strcmp(f->hmf_name, name)) + return NULL; + return &f->hmf_msg; +} + + +/** + * + */ +const char * +htsmsg_get_cdata(htsmsg_t *m, const char *field) +{ + return htsmsg_get_str_multi(m, field, "cdata", NULL); +} + /** * @@ -633,3 +712,4 @@ htsmsg_dtor(htsmsg_t **mp) if(*mp != NULL) htsmsg_destroy(*mp); } + diff --git a/src/htsmsg.h b/src/htsmsg.h index d7d2bb05..82fb7fda 100644 --- a/src/htsmsg.h +++ b/src/htsmsg.h @@ -1,6 +1,6 @@ /* * Functions for manipulating HTS messages - * Copyright (C) 2007 Andreas Öman + * Copyright (C) 2007 Andreas Öman * * 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 @@ -16,9 +16,8 @@ * along with this program. If not, see . */ -#ifndef HTSMSG_H_ -#define HTSMSG_H_ - +#pragma once +#include #include #include "queue.h" @@ -50,6 +49,7 @@ typedef struct htsmsg { #define HMF_STR 3 #define HMF_BIN 4 #define HMF_LIST 5 +#define HMF_DBL 6 typedef struct htsmsg_field { TAILQ_ENTRY(htsmsg_field) hmf_link; @@ -68,6 +68,7 @@ typedef struct htsmsg_field { size_t len; } bin; htsmsg_t msg; + double dbl; } u; } htsmsg_field_t; @@ -76,11 +77,12 @@ typedef struct htsmsg_field { #define hmf_str u.str #define hmf_bin u.bin.data #define hmf_binsize u.bin.len +#define hmf_dbl u.dbl #define htsmsg_get_map_by_field(f) \ ((f)->hmf_type == HMF_MAP ? &(f)->hmf_msg : NULL) #define htsmsg_get_list_by_field(f) \ - ((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL) + ((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL) #define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link) @@ -94,6 +96,11 @@ htsmsg_t *htsmsg_create_map(void); */ htsmsg_t *htsmsg_create_list(void); +/** + * Remove a given field from a msg + */ +void htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f); + /** * Destroys a message (map or list) */ @@ -109,11 +116,6 @@ void htsmsg_add_u32(htsmsg_t *msg, const char *name, uint32_t u32); */ void htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32); -/** - * Add an integer field where source is unsigned 64 bit. - */ -void htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64); - /** * Add an integer field where source is signed 64 bit. */ @@ -129,6 +131,11 @@ void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str); */ void htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub); +/** + * Add an field where source is a double + */ +void htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl); + /** * Add an field where source is a list or map message. * @@ -160,15 +167,6 @@ void htsmsg_add_binptr(htsmsg_t *msg, const char *name, const void *bin, */ int htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p); -/** - * Return the field \p name as an u32. - * - * @return An unsigned 32 bit integer or def if the field can not be found - * or if conversion is not possible. - */ -uint32_t htsmsg_get_u32_or_default - (htsmsg_t *msg, const char *name, uint32_t def); - /** * Get an integer as an signed 32 bit integer. * @@ -187,24 +185,11 @@ int htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p); */ int htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p); - -/** +/* * Return the field \p name as an s64. - * - * @return A signed 64 bit integer or def if the field can not be found - * or if conversion is not possible. */ -int64_t htsmsg_get_s64_or_default - (htsmsg_t *msg, const char *name, int64_t def); +int64_t htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def); -/** - * Get an integer as an unsigned 64 bit integer. - * - * @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist - * HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or - * out of range for the requested storage. - */ -int htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p); /** * Get pointer to a binary field. No copying of data is performed. @@ -236,15 +221,6 @@ htsmsg_t *htsmsg_get_list(htsmsg_t *msg, const char *name); */ const char *htsmsg_get_str(htsmsg_t *msg, const char *name); -/** - * Get a field of type 'string'. No copying is done. - * - * @return def if the field can not be found or not of string type. - * Otherwise a pointer to the data is returned. - */ -const char *htsmsg_get_str_or_default - (htsmsg_t *msg, const char *name, const char *def); - /** * Get a field of type 'map'. No copying is done. * @@ -256,7 +232,23 @@ htsmsg_t *htsmsg_get_map(htsmsg_t *msg, const char *name); /** * Traverse a hierarchy of htsmsg's to find a specific child. */ -htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...); +htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...) + __attribute__((__sentinel__(0))); + +/** + * Traverse a hierarchy of htsmsg's to find a specific child. + */ +const char *htsmsg_get_str_multi(htsmsg_t *msg, ...) + __attribute__((__sentinel__(0))); + +/** + * Get a field of type 'double'. + * + * @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist + * HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or + * out of range for the requested storage. + */ +int htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp); /** * Given the field \p f, return a string if it is of type string, otherwise @@ -264,6 +256,23 @@ htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...); */ const char *htsmsg_field_get_string(htsmsg_field_t *f); +/** + * Return the field \p name as an u32. + * + * @return An unsigned 32 bit integer or NULL if the field can not be found + * or if conversion is not possible. + */ +int htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def); + +/** + * Return the field \p name as an s32. + * + * @return A signed 32 bit integer or NULL if the field can not be found + * or if conversion is not possible. + */ +int32_t htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name, + int32_t def); + /** * Remove the given field called \p name from the message \p msg. */ @@ -288,6 +297,12 @@ void htsmsg_print(htsmsg_t *msg); htsmsg_field_t *htsmsg_field_add(htsmsg_t *msg, const char *name, int type, int flags); +/** + * Get a field, return NULL if it does not exist + */ +htsmsg_field_t *htsmsg_field_find(htsmsg_t *msg, const char *name); + + /** * Clone a message. */ @@ -296,8 +311,15 @@ htsmsg_t *htsmsg_copy(htsmsg_t *src); #define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link) +/** + * Misc + */ +htsmsg_t *htsmsg_get_map_in_list(htsmsg_t *m, int num); + +htsmsg_t *htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name); + +const char *htsmsg_get_cdata(htsmsg_t *m, const char *field); + extern void htsmsg_dtor(htsmsg_t **mp); #define htsmsg_autodtor(n) htsmsg_t *n __attribute__((cleanup(htsmsg_dtor))) - -#endif /* HTSMSG_H_ */ diff --git a/src/htsmsg_json.c b/src/htsmsg_json.c index ff1aa6f7..3fda47c1 100644 --- a/src/htsmsg_json.c +++ b/src/htsmsg_json.c @@ -25,54 +25,19 @@ #include "htsmsg_json.h" #include "htsbuf.h" +#include "misc/json.h" +#include "misc/dbl.h" + /** * */ static void -htsmsg_json_encode_string(const char *str, htsbuf_queue_t *hq) -{ - const char *s = str; - - htsbuf_append(hq, "\"", 1); - - while(*s != 0) { - if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\t' || *s == '\r') { - htsbuf_append(hq, str, s - str); - - if(*s == '"') - htsbuf_append(hq, "\\\"", 2); - else if(*s == '\n') - htsbuf_append(hq, "\\n", 2); - else if(*s == '\t') - htsbuf_append(hq, "\\t", 2); - else if(*s == '\r') - htsbuf_append(hq, "\\r", 2); - else - htsbuf_append(hq, "\\\\", 2); - s++; - str = s; - } else { - s++; - } - } - htsbuf_append(hq, str, s - str); - htsbuf_append(hq, "\"", 1); -} - - -/* - * Note to future: - * If your about to add support for numbers with decimal point, - * remember to always serialize them with '.' as decimal point character - * no matter what current locale says. This is according to the JSON spec. - */ -static void htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray, int indent, int pretty) { htsmsg_field_t *f; - char buf[30]; + char buf[100]; static const char *indentor = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; htsbuf_append(hq, isarray ? "[" : "{", 1); @@ -83,7 +48,7 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray, htsbuf_append(hq, indentor, indent < 16 ? indent : 16); if(!isarray) { - htsmsg_json_encode_string(f->hmf_name ?: "noname", hq); + htsbuf_append_and_escape_jsonstr(hq, f->hmf_name ?: "noname"); htsbuf_append(hq, ": ", 2); } @@ -97,11 +62,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray, break; case HMF_STR: - htsmsg_json_encode_string(f->hmf_str, hq); + htsbuf_append_and_escape_jsonstr(hq, f->hmf_str); break; case HMF_BIN: - htsmsg_json_encode_string("binary", hq); + htsbuf_append_and_escape_jsonstr(hq, "binary"); break; case HMF_S64: @@ -109,6 +74,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray, htsbuf_append(hq, buf, strlen(buf)); break; + case HMF_DBL: + my_double2str(buf, sizeof(buf), f->hmf_dbl); + htsbuf_append(hq, buf, strlen(buf)); + break; + default: abort(); } @@ -125,343 +95,109 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray, /** * */ -int +void htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty) { htsmsg_json_write(msg, hq, msg->hm_islist, 2, pretty); if(pretty) htsbuf_append(hq, "\n", 1); - return 0; -} - - - -static const char *htsmsg_json_parse_value(const char *s, - htsmsg_t *parent, char *name); - -/** - * - */ -static char * -htsmsg_json_parse_string(const char *s, const char **endp) -{ - const char *start; - char *r, *a, *b; - int l, esc = 0; - - while(*s > 0 && *s < 33) - s++; - - if(*s != '"') - return NULL; - - s++; - start = s; - - while(1) { - if(*s == 0) - return NULL; - - if(*s == '\\') { - esc = 1; - /* skip the escape */ - s++; - if (*s == 'u') s += 4; - // Note: we could detect the lack of support here! - } else if(*s == '"') { - - *endp = s + 1; - - /* End */ - l = s - start; - r = malloc(l + 1); - memcpy(r, start, l); - r[l] = 0; - - if(esc) { - /* Do deescaping inplace */ - - a = b = r; - - while(*a) { - if(*a == '\\') { - a++; - if(*a == 'b') - *b++ = '\b'; - else if(*a == '\\') - *b++ = '\\'; - else if(*a == 'f') - *b++ = '\f'; - else if(*a == 'n') - *b++ = '\n'; - else if(*a == 'r') - *b++ = '\r'; - else if(*a == 't') - *b++ = '\t'; - else if(*a == 'u') { - /* 4 hexdigits: Not supported */ - free(r); - return NULL; - } else { - *b++ = *a; - } - a++; - } else { - *b++ = *a++; - } - } - *b = 0; - } - return r; - } - s++; - } } /** * */ -static htsmsg_t * -htsmsg_json_parse_object(const char *s, const char **endp) +char * +htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty) { - char *name; - const char *s2; - htsmsg_t *r; - - while(*s > 0 && *s < 33) - s++; - - if(*s != '{') - return NULL; - - s++; - - r = htsmsg_create_map(); - - while(*s > 0 && *s < 33) - s++; - - if(*s != '}') while(1) { - - if((name = htsmsg_json_parse_string(s, &s2)) == NULL) { - htsmsg_destroy(r); - return NULL; - } - - s = s2; - - while(*s > 0 && *s < 33) - s++; - - if(*s != ':') { - htsmsg_destroy(r); - free(name); - return NULL; - } - s++; - - s2 = htsmsg_json_parse_value(s, r, name); - free(name); - - if(s2 == NULL) { - htsmsg_destroy(r); - return NULL; - } - - s = s2; - - while(*s > 0 && *s < 33) - s++; - - if(*s == '}') - break; - - if(*s != ',') { - htsmsg_destroy(r); - return NULL; - } - s++; - } - - s++; - *endp = s; - return r; -} - - -/** - * - */ -static htsmsg_t * -htsmsg_json_parse_array(const char *s, const char **endp) -{ - const char *s2; - htsmsg_t *r; - - while(*s > 0 && *s < 33) - s++; - - if(*s != '[') - return NULL; - - s++; - - r = htsmsg_create_list(); - - while(*s > 0 && *s < 33) - s++; - - if(*s != ']') { - - while(1) { - - s2 = htsmsg_json_parse_value(s, r, NULL); - - if(s2 == NULL) { - htsmsg_destroy(r); - return NULL; - } - - s = s2; - - while(*s > 0 && *s < 33) - s++; - - if(*s == ']') - break; - - if(*s != ',') { - htsmsg_destroy(r); - return NULL; - } - s++; - } - } - s++; - *endp = s; - return r; -} - -/* - * locale independent strtod. - * does not support hex floats as the standard strtod - */ -static double -_strntod(const char *s, char decimal_point_char, char **ep) -{ - static char locale_decimal_point_char = 0; - char buf[64]; - const char *c; - double d; - - /* ugly but very portable way to get decimal point char */ - if(locale_decimal_point_char == 0) { - snprintf(buf, sizeof(buf), "%f", 0.0); - locale_decimal_point_char = buf[1]; - assert(locale_decimal_point_char != 0); - } - - for(c = s; - *c != '\0' && - ((*c > 0 && *c < 33) || /* skip whitespace */ - (*c == decimal_point_char || strchr("+-0123456789", *c) != NULL)); c++) - ; - - strncpy(buf, s, c - s); - buf[c - s] = '\0'; - - /* replace if specified char is not same as current locale */ - if(decimal_point_char != locale_decimal_point_char) { - char *r = strchr(buf, decimal_point_char); - if(r != NULL) - *r = locale_decimal_point_char; - } - - d = strtod(buf, ep); - - /* figure out offset in original string */ - if(ep != NULL) - *ep = (char *)s + (*ep - buf); - - return d; -} - -/** - * - */ -static char * -htsmsg_json_parse_number(const char *s, double *dp) -{ - char *ep; - double d = _strntod(s, '.', &ep); - - if(ep == s) - return NULL; - - *dp = d; - return ep; -} - -/** - * - */ -static const char * -htsmsg_json_parse_value(const char *s, htsmsg_t *parent, char *name) -{ - const char *s2; + htsbuf_queue_t hq; char *str; - double d = 0; - htsmsg_t *c; - - if((c = htsmsg_json_parse_object(s, &s2)) != NULL) { - htsmsg_add_msg(parent, name, c); - return s2; - } else if((c = htsmsg_json_parse_array(s, &s2)) != NULL) { - htsmsg_add_msg(parent, name, c); - return s2; - } else if((str = htsmsg_json_parse_string(s, &s2)) != NULL) { - htsmsg_add_str(parent, name, str); - free(str); - return s2; - } else if((s2 = htsmsg_json_parse_number(s, &d)) != NULL) { - htsmsg_add_s64(parent, name, d); - return s2; - } - - if(!strncmp(s, "true", 4)) { - htsmsg_add_u32(parent, name, 1); - return s + 4; - } - - if(!strncmp(s, "false", 5)) { - htsmsg_add_u32(parent, name, 0); - return s + 5; - } - - if(!strncmp(s, "null", 4)) { - /* Don't add anything */ - return s + 4; - } - return NULL; + htsbuf_queue_init(&hq, 0); + htsmsg_json_serialize(msg, &hq, pretty); + str = htsbuf_to_string(&hq); + htsbuf_queue_flush(&hq); + return str; } +/** + * + */ +static void * +create_map(void *opaque) +{ + return htsmsg_create_map(); +} + +static void * +create_list(void *opaque) +{ + return htsmsg_create_list(); +} + +static void +destroy_obj(void *opaque, void *obj) +{ + return htsmsg_destroy(obj); +} + +static void +add_obj(void *opaque, void *parent, const char *name, void *child) +{ + htsmsg_add_msg(parent, name, child); +} + +static void +add_string(void *opaque, void *parent, const char *name, char *str) +{ + htsmsg_add_str(parent, name, str); + free(str); +} + +static void +add_long(void *opaque, void *parent, const char *name, long v) +{ + htsmsg_add_s64(parent, name, v); +} + +static void +add_double(void *opaque, void *parent, const char *name, double v) +{ + htsmsg_add_dbl(parent, name, v); +} + +static void +add_bool(void *opaque, void *parent, const char *name, int v) +{ + htsmsg_add_u32(parent, name, v); +} + +static void +add_null(void *opaque, void *parent, const char *name) +{ +} + +/** + * + */ +static const json_deserializer_t json_to_htsmsg = { + .jd_create_map = create_map, + .jd_create_list = create_list, + .jd_destroy_obj = destroy_obj, + .jd_add_obj = add_obj, + .jd_add_string = add_string, + .jd_add_long = add_long, + .jd_add_double = add_double, + .jd_add_bool = add_bool, + .jd_add_null = add_null, +}; + + /** * */ htsmsg_t * htsmsg_json_deserialize(const char *src) { - const char *end; - htsmsg_t *c; - - if((c = htsmsg_json_parse_object(src, &end)) != NULL) - return c; - - if((c = htsmsg_json_parse_array(src, &end)) != NULL) { - c->hm_islist = 1; - return c; - } - return NULL; + return json_deserialize(src, &json_to_htsmsg, NULL, NULL, 0); } diff --git a/src/htsmsg_json.h b/src/htsmsg_json.h index da6b2504..fae3c457 100644 --- a/src/htsmsg_json.h +++ b/src/htsmsg_json.h @@ -1,6 +1,6 @@ /* * Functions converting HTSMSGs to/from JSON - * Copyright (C) 2008 Andreas Öman + * Copyright (C) 2008 Andreas Öman * * 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 @@ -21,12 +21,16 @@ #include "htsmsg.h" #include "htsbuf.h" - +struct rstr; /** * htsmsg_binary_deserialize */ htsmsg_t *htsmsg_json_deserialize(const char *src); -int htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty); +void htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty); + +char *htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty); + +struct rstr *htsmsg_json_serialize_to_rstr(htsmsg_t *msg, const char *prefix); #endif /* HTSMSG_JSON_H_ */ diff --git a/src/htsp_server.c b/src/htsp_server.c index 05ff068f..d13b59de 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -381,14 +381,14 @@ htsp_file_open(htsp_connection_t *htsp, const char *path) hf->hf_fd = fd; hf->hf_id = ++htsp->htsp_file_id; hf->hf_path = strdup(path); - LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link); + LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link); htsmsg_t *rep = htsmsg_create_map(); htsmsg_add_u32(rep, "id", hf->hf_id); if(!fstat(hf->hf_fd, &st)) { - htsmsg_add_u64(rep, "size", st.st_size); - htsmsg_add_u64(rep, "mtime", st.st_mtime); + htsmsg_add_s64(rep, "size", st.st_size); + htsmsg_add_s64(rep, "mtime", st.st_mtime); } return rep; @@ -837,7 +837,7 @@ htsp_method_getEvent(htsp_connection_t *htsp, htsmsg_t *in) if(htsmsg_get_u32(in, "eventId", &eventId)) return htsp_error("Missing argument 'eventId'"); - lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language); + lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language; if((e = epg_broadcast_find_by_id(eventId, NULL)) == NULL) return htsp_error("Event does not exist"); @@ -871,7 +871,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in) maxTime = htsmsg_get_s64_or_default(in, "maxTime", 0); lang - = htsmsg_get_str_or_default(in, "language", htsp->htsp_language); + = htsmsg_get_str(in, "language") ?: htsp->htsp_language; /* Use event as starting point */ if (e || ch) { @@ -940,7 +940,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in) genre.code = u32; eg = &genre; } - lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language); + lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language; full = htsmsg_get_u32_or_default(in, "full", 0); //do the query @@ -1324,6 +1324,7 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in) return NULL; } + /** * Open file */ @@ -1358,17 +1359,17 @@ static htsmsg_t * htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in) { htsp_file_t *hf = htsp_file_find(htsp, in); - uint64_t off; - uint64_t size; + int64_t off; + int64_t size; if(hf == NULL) return htsp_error("Unknown file id"); - if(htsmsg_get_u64(in, "size", &size)) + if(htsmsg_get_s64(in, "size", &size)) return htsp_error("Missing field 'size'"); /* Seek (optional) */ - if (!htsmsg_get_u64(in, "offset", &off)) + if (!htsmsg_get_s64(in, "offset", &off)) if(lseek(hf->hf_fd, off, SEEK_SET) != off) return htsp_error("Seek error"); @@ -1418,8 +1419,8 @@ htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in) htsmsg_t *rep = htsmsg_create_map(); if(!fstat(hf->hf_fd, &st)) { - htsmsg_add_u64(rep, "size", st.st_size); - htsmsg_add_u64(rep, "mtime", st.st_mtime); + htsmsg_add_s64(rep, "size", st.st_size); + htsmsg_add_s64(rep, "mtime", st.st_mtime); } return rep; } diff --git a/src/misc/dbl.c b/src/misc/dbl.c new file mode 100644 index 00000000..05e5a6dd --- /dev/null +++ b/src/misc/dbl.c @@ -0,0 +1,295 @@ +/* + * Floating point conversion functions. + * Not accurate but should be enough for Showtime's needs + * + * Copyright (C) 2011 Andreas Öman + * + * 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 . + */ + +#define _ISOC99_SOURCE + +#include +#include +#include +#include + +#include "dbl.h" + + +double +my_str2double(const char *str, const char **endp) +{ + double ret = 1.0f; + int n = 0, m = 0, o = 0, e = 0; + + if(*str == '-') { + ret = -1.0f; + str++; + } + + while(*str >= '0' && *str <= '9') + n = n * 10 + *str++ - '0'; + + if(*str != '.') { + ret *= n; + } else { + + str++; + + while(*str >= '0' && *str <= '9') { + o = o * 10 + *str++ - '0'; + m--; + } + + ret *= (n + pow(10, m) * o); + } + + if(*str == 'e' || *str == 'E') { + int esign = 1; + str++; + + if(*str == '+') + str++; + else if(*str == '-') { + str++; + esign = -1; + } + + while(*str >= '0' && *str <= '9') + e = e * 10 + *str++ - '0'; + ret *= pow(10, e * esign); + } + + if(endp != NULL) + *endp = str; + + return ret; +} + + + + +/* +** The code that follow is based on "printf" code that dates from the +** 1980s. It is in the public domain. The original comments are +** included here for completeness. They are very out-of-date but +** might be useful as an historical reference. +** +************************************************************************** +** +** The following modules is an enhanced replacement for the "printf" subroutines +** found in the standard C library. The following enhancements are +** supported: +** +** + Additional functions. The standard set of "printf" functions +** includes printf, fprintf, sprintf, vprintf, vfprintf, and +** vsprintf. This module adds the following: +** +** * snprintf -- Works like sprintf, but has an extra argument +** which is the size of the buffer written to. +** +** * mprintf -- Similar to sprintf. Writes output to memory +** obtained from malloc. +** +** * xprintf -- Calls a function to dispose of output. +** +** * nprintf -- No output, but returns the number of characters +** that would have been output by printf. +** +** * A v- version (ex: vsnprintf) of every function is also +** supplied. +** +** + A few extensions to the formatting notation are supported: +** +** * The "=" flag (similar to "-") causes the output to be +** be centered in the appropriately sized field. +** +** * The %b field outputs an integer in binary notation. +** +** * The %c field now accepts a precision. The character output +** is repeated by the number of times the precision specifies. +** +** * The %' field works like %c, but takes as its character the +** next character of the format string, instead of the next +** argument. For example, printf("%.78'-") prints 78 minus +** signs, the same as printf("%.78c",'-'). +** +** + When compiled using GCC on a SPARC, this version of printf is +** faster than the library printf for SUN OS 4.1. +** +** + All functions are fully reentrant. +** +*/ + + +static char +getdigit(double *val, int *cnt) +{ + int digit; + double d; + if( (*cnt)++ >= 16 ) return '0'; + digit = (int)*val; + d = digit; + digit += '0'; + *val = (*val - d)*10.0; + return (char)digit; +} + +#define xGENERIC 0 +#define xFLOAT 1 +#define xEXP 2 + + +int +my_double2str(char *buf, size_t bufsize, double realvalue) +{ + int precision = -1; + char *bufpt; + char prefix; + char xtype = xGENERIC; + int idx, exp, e2; + double rounder; + char flag_exp; + char flag_rtz; + char flag_dp; + char flag_alternateform = 0; + char flag_altform2 = 0; + int nsd; + + if(bufsize < 8) + return -1; + + if( precision<0 ) precision = 20; /* Set default precision */ + if( precision>bufsize/2-10 ) precision = bufsize/2-10; + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + prefix = 0; + } + if( xtype==xGENERIC && precision>0 ) precision--; + for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} + + if( xtype==xFLOAT ) realvalue += rounder; + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; + + if(isnan(realvalue)) { + strcpy(buf, "NaN"); + return 0; + } + + if( realvalue>0.0 ){ + while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; } + while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } + while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } + while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } + if( exp>350 ){ + if( prefix=='-' ){ + strcpy(buf, "-Inf"); + }else{ + strcpy(buf, "Inf"); + } + return 0; + } + } + bufpt = buf; + + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ + flag_exp = xtype==xEXP; + if( xtype != xFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==xGENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = xEXP; + }else{ + precision = precision - exp; + xtype = xFLOAT; + } + }else{ + flag_rtz = 0; + } + if( xtype==xEXP ){ + e2 = 0; + }else{ + e2 = exp; + } + nsd = 0; + flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; + /* The sign in front of the number */ + if( prefix ){ + *(bufpt++) = prefix; + } + /* Digits prior to the decimal point */ + if( e2<0 ){ + *(bufpt++) = '0'; + }else{ + for(; e2>=0; e2--){ + *(bufpt++) = getdigit(&realvalue,&nsd); + } + } + /* The decimal point */ + if( flag_dp ){ + *(bufpt++) = '.'; + } + /* "0" digits after the decimal point but before the first + ** significant digit of the number */ + for(e2++; e2<0; precision--, e2++){ + assert( precision>0 ); + *(bufpt++) = '0'; + } + /* Significant digits after the decimal point */ + while( (precision--)>0 ){ + *(bufpt++) = getdigit(&realvalue,&nsd); + } + + /* Remove trailing zeros and the "." if no digits follow the "." */ + if( flag_rtz && flag_dp ){ + while( bufpt[-1]=='0' ) *(--bufpt) = 0; + assert( bufpt>buf ); + if( bufpt[-1]=='.' ){ + if( flag_altform2 ){ + *(bufpt++) = '0'; + }else{ + *(--bufpt) = 0; + } + } + } + /* Add the "eNNN" suffix */ + if( flag_exp || xtype==xEXP ){ + *(bufpt++) = 'e'; + if( exp<0 ){ + *(bufpt++) = '-'; exp = -exp; + }else{ + *(bufpt++) = '+'; + } + if( exp>=100 ){ + *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ + exp %= 100; + } + *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ + *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ + } + *bufpt = 0; + return 0; +} + diff --git a/src/misc/dbl.h b/src/misc/dbl.h new file mode 100644 index 00000000..fc05259c --- /dev/null +++ b/src/misc/dbl.h @@ -0,0 +1,5 @@ +#pragma once + +double my_str2double(const char *str, const char **endp); + +int my_double2str(char *buf, size_t bufsize, double realvalue); diff --git a/src/misc/json.c b/src/misc/json.c new file mode 100644 index 00000000..59f40ebb --- /dev/null +++ b/src/misc/json.c @@ -0,0 +1,432 @@ +/* + * JSON helpers + * Copyright (C) 2011 Andreas Öman + * + * 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 . + */ + +#include +#include +#include +#include + +#include "json.h" +#include "dbl.h" +#include "tvheadend.h" + +#define NOT_THIS_TYPE ((void *)-1) + +static const char *json_parse_value(const char *s, void *parent, + const char *name, + const json_deserializer_t *jd, + void *opaque, + const char **failp, const char **failmsg); + +/** + * Returns a newly allocated string + */ +static char * +json_parse_string(const char *s, const char **endp, + const char **failp, const char **failmsg) +{ + const char *start; + char *r, *a, *b; + int l, esc = 0; + + while(*s > 0 && *s < 33) + s++; + + if(*s != '"') + return NOT_THIS_TYPE; + + s++; + start = s; + + while(1) { + if(*s == 0) { + *failmsg = "Unexpected end of JSON message"; + *failp = s; + return NULL; + } + + if(*s == '\\') { + esc = 1; + } else if(*s == '"' && s[-1] != '\\') { + + *endp = s + 1; + + /* End */ + l = s - start; + r = malloc(l + 1); + memcpy(r, start, l); + r[l] = 0; + + if(esc) { + /* Do deescaping inplace */ + + a = b = r; + + while(*a) { + if(*a == '\\') { + a++; + if(*a == 'b') + *b++ = '\b'; + else if(*a == 'f') + *b++ = '\f'; + else if(*a == 'n') + *b++ = '\n'; + else if(*a == 'r') + *b++ = '\r'; + else if(*a == 't') + *b++ = '\t'; + else if(*a == 'u') { + // Unicode character + int i, v = 0; + + a++; + for(i = 0; i < 4; i++) { + v = v << 4; + switch(a[i]) { + case '0' ... '9': + v |= a[i] - '0'; + break; + case 'a' ... 'f': + v |= a[i] - 'a' + 10; + break; + case 'A' ... 'F': + v |= a[i] - 'F' + 10; + break; + default: + free(r); + *failmsg = "Incorrect escape sequence"; + *failp = (a - r) + start; + return NULL; + } + } + a+=3; + b += put_utf8(b, v); + } else { + *b++ = *a; + } + a++; + } else { + *b++ = *a++; + } + } + *b = 0; + } + return r; + } + s++; + } +} + + +/** + * + */ +static void * +json_parse_map(const char *s, const char **endp, const json_deserializer_t *jd, + void *opaque, const char **failp, const char **failmsg) + +{ + char *name; + const char *s2; + void *r; + + while(*s > 0 && *s < 33) + s++; + + if(*s != '{') + return NOT_THIS_TYPE; + + s++; + + r = jd->jd_create_map(opaque); + + while(*s > 0 && *s < 33) + s++; + + if(*s != '}') { + + while(1) { + name = json_parse_string(s, &s2, failp, failmsg); + if(name == NOT_THIS_TYPE) { + *failmsg = "Expected string"; + *failp = s; + return NULL; + } + + if(name == NULL) + return NULL; + + s = s2; + + while(*s > 0 && *s < 33) + s++; + + if(*s != ':') { + jd->jd_destroy_obj(opaque, r); + free(name); + *failmsg = "Expected ':'"; + *failp = s; + return NULL; + } + s++; + + s2 = json_parse_value(s, r, name, jd, opaque, failp, failmsg); + free(name); + + if(s2 == NULL) { + jd->jd_destroy_obj(opaque, r); + return NULL; + } + + s = s2; + + while(*s > 0 && *s < 33) + s++; + + if(*s == '}') + break; + + if(*s != ',') { + jd->jd_destroy_obj(opaque, r); + *failmsg = "Expected ','"; + *failp = s; + return NULL; + } + s++; + } + } + + s++; + *endp = s; + return r; +} + + +/** + * + */ +static void * +json_parse_list(const char *s, const char **endp, const json_deserializer_t *jd, + void *opaque, const char **failp, const char **failmsg) +{ + const char *s2; + void *r; + + while(*s > 0 && *s < 33) + s++; + + if(*s != '[') + return NOT_THIS_TYPE; + + s++; + + r = jd->jd_create_list(opaque); + + while(*s > 0 && *s < 33) + s++; + + if(*s != ']') { + + while(1) { + + s2 = json_parse_value(s, r, NULL, jd, opaque, failp, failmsg); + + if(s2 == NULL) { + jd->jd_destroy_obj(opaque, r); + return NULL; + } + + s = s2; + + while(*s > 0 && *s < 33) + s++; + + if(*s == ']') + break; + + if(*s != ',') { + jd->jd_destroy_obj(opaque, r); + *failmsg = "Expected ','"; + *failp = s; + return NULL; + } + s++; + } + } + s++; + *endp = s; + return r; +} + +/** + * + */ +static const char * +json_parse_double(const char *s, double *dp) +{ + const char *ep; + while(*s > 0 && *s < 33) + s++; + + double d = my_str2double(s, &ep); + + if(ep == s) + return NULL; + + *dp = d; + return ep; +} + + +/** + * + */ +static char * +json_parse_integer(const char *s, long *lp) +{ + char *ep; + while(*s > 0 && *s < 33) + s++; + const char *s2 = s; + if(*s2 == '-') + s2++; + while(*s2 >= '0' && *s2 <= '9') + s2++; + + if(*s2 == 0) + return NULL; + if(s2[0] == '.' || s2[0] == 'e' || s2[0] == 'E') + return NULL; // Is floating point + + long v = strtol(s, &ep, 10); + if(v == LONG_MIN || v == LONG_MAX) + return NULL; + + if(ep == s) + return NULL; + + *lp = v; + return ep; +} + +/** + * + */ +static const char * +json_parse_value(const char *s, void *parent, const char *name, + const json_deserializer_t *jd, void *opaque, + const char **failp, const char **failmsg) +{ + const char *s2; + char *str; + double d = 0; + long l = 0; + void *c; + + if((c = json_parse_map(s, &s2, jd, opaque, failp, failmsg)) == NULL) + return NULL; + + if(c != NOT_THIS_TYPE) { + jd->jd_add_obj(opaque, parent, name, c); + return s2; + } + + if((c = json_parse_list(s, &s2, jd, opaque, failp, failmsg)) == NULL) + return NULL; + + if(c != NOT_THIS_TYPE) { + jd->jd_add_obj(opaque, parent, name, c); + return s2; + } + + if((str = json_parse_string(s, &s2, failp, failmsg)) == NULL) + return NULL; + + if(str != NOT_THIS_TYPE) { + jd->jd_add_string(opaque, parent, name, str); + return s2; + } + + if((s2 = json_parse_integer(s, &l)) != NULL) { + jd->jd_add_long(opaque, parent, name, l); + return s2; + } else if((s2 = json_parse_double(s, &d)) != NULL) { + jd->jd_add_double(opaque, parent, name, d); + return s2; + } + + while(*s > 0 && *s < 33) + s++; + + if(!strncmp(s, "true", 4)) { + jd->jd_add_bool(opaque, parent, name, 1); + return s + 4; + } + + if(!strncmp(s, "false", 5)) { + jd->jd_add_bool(opaque, parent, name, 0); + return s + 5; + } + + if(!strncmp(s, "null", 4)) { + jd->jd_add_null(opaque, parent, name); + return s + 4; + } + + *failmsg = "Unknown token"; + *failp = s; + return NULL; +} + + +/** + * + */ +void * +json_deserialize(const char *src, const json_deserializer_t *jd, void *opaque, + char *errbuf, size_t errlen) +{ + const char *end; + void *c; + const char *errmsg; + const char *errp; + + c = json_parse_map(src, &end, jd, opaque, &errp, &errmsg); + if(c == NOT_THIS_TYPE) + c = json_parse_list(src, &end, jd, opaque, &errp, &errmsg); + + if(c == NOT_THIS_TYPE) { + snprintf(errbuf, errlen, "Invalid JSON, expected '{' or '['"); + return NULL; + } + + if(c == NULL) { + size_t len = strlen(src); + ssize_t offset = errp - src; + if(offset > len || offset < 0) { + snprintf(errbuf, errlen, "%s at (bad) offset %d", errmsg, (int)offset); + } else { + offset -= 10; + if(offset < 0) + offset = 0; + snprintf(errbuf, errlen, "%s at offset %d : '%.20s'", errmsg, (int)offset, + src + offset); + } + } + return c; +} diff --git a/src/misc/json.h b/src/misc/json.h new file mode 100644 index 00000000..8fc1bf26 --- /dev/null +++ b/src/misc/json.h @@ -0,0 +1,31 @@ +#pragma once + +typedef struct json_deserializer { + void *(*jd_create_map)(void *jd_opaque); + void *(*jd_create_list)(void *jd_opaque); + + void (*jd_destroy_obj)(void *jd_opaque, void *obj); + + void (*jd_add_obj)(void *jd_opaque, void *parent, + const char *name, void *child); + + // str must be free'd by callee + void (*jd_add_string)(void *jd_opaque, void *parent, + const char *name, char *str); + + void (*jd_add_long)(void *jd_opaque, void *parent, + const char *name, long v); + + void (*jd_add_double)(void *jd_opaque, void *parent, + const char *name, double d); + + void (*jd_add_bool)(void *jd_opaque, void *parent, + const char *name, int v); + + void (*jd_add_null)(void *jd_opaque, void *parent, + const char *name); + +} json_deserializer_t; + +void *json_deserialize(const char *src, const json_deserializer_t *jd, + void *opaque, char *errbuf, size_t errlen); From 54bc22cd5ec5046582314444ed63ca9b8657075d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 14:45:54 +0100 Subject: [PATCH 111/503] Fix linking issue --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 01f372df..3da26556 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ CFLAGS += -Wmissing-prototypes -fms-extensions CFLAGS += -g -funsigned-char -O2 CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} -LDFLAGS += -lrt -ldl -lpthread +LDFLAGS += -lrt -ldl -lpthread -lm # # Other config From 44dd4042a970a606ada24d4e6078214034cddb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 14:46:09 +0100 Subject: [PATCH 112/503] dvb/fe: Use double for uncorrected bits average --- src/dvb/dvb_fe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index b90d5abb..2731e13a 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -229,7 +229,7 @@ dvb_fe_monitor(void *aux) htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); - htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg); + htsmsg_add_dbl(m, "uncavg", tdmi->tdmi_unc_avg); notify_by_msg("tvAdapter", m); } From e16707bef79f072ea068a39a7bcb508ea76807d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 19 Nov 2012 15:21:39 +0100 Subject: [PATCH 113/503] Display SNR (in dB) in status tab But only for adapters we know that support it --- src/dvb/dvb.h | 4 +++- src/dvb/dvb_adapter.c | 9 +++++++-- src/dvb/dvb_fe.c | 18 +++++++++++++----- src/webui/static/app/status.js | 11 +++++++++++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 92fcaf2c..5ae0d876 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -97,9 +97,10 @@ typedef struct th_dvb_mux_instance { struct th_dvb_adapter *tdmi_adapter; - uint16_t tdmi_snr, tdmi_signal; + uint16_t tdmi_signal; uint32_t tdmi_ber, tdmi_unc; float tdmi_unc_avg; + float tdmi_snr; #define TDMI_FEC_ERR_HISTOGRAM_SIZE 10 uint32_t tdmi_fec_err_histogram[TDMI_FEC_ERR_HISTOGRAM_SIZE]; @@ -219,6 +220,7 @@ typedef struct th_dvb_adapter { char *tda_fe_path; int tda_fe_fd; int tda_type; + int tda_snr_valid; struct dvb_frontend_info *tda_fe_info; int tda_adapter_num; diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 1847304a..8e35dfe7 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -510,9 +510,14 @@ tda_add(int adapter_num) dvb_adapter_checkspeed(tda); + + if(!strcmp(tda->tda_fe_info->name, "Sony CXD2820R (DVB-T/T2)")) + tda->tda_snr_valid = 1; + tvhlog(LOG_INFO, "dvb", - "Found adapter %s (%s) via %s", path, tda->tda_fe_info->name, - hostconnection2str(tda->tda_hostconnection)); + "Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name, + hostconnection2str(tda->tda_hostconnection), + tda->tda_snr_valid ? ", Reports valid SNR values" : ""); TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 2731e13a..7120ab5c 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -182,9 +182,14 @@ dvb_fe_monitor(void *aux) } /* signal/noise ratio */ - if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &v) == -1 && v != tdmi->tdmi_snr) { - tdmi->tdmi_snr = v; - notify = 1; + if(tda->tda_snr_valid) { + if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &v) != -1) { + float snr = v / 10.0; + if(tdmi->tdmi_snr != snr) { + tdmi->tdmi_snr = snr; + notify = 1; + } + } } } @@ -218,7 +223,9 @@ dvb_fe_monitor(void *aux) htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); htsmsg_add_u32(m, "signal", tdmi->tdmi_signal); - htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); + + if(tda->tda_snr_valid) + htsmsg_add_dbl(m, "snr", tdmi->tdmi_snr); htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); notify_by_msg("dvbMux", m); @@ -226,7 +233,8 @@ dvb_fe_monitor(void *aux) m = htsmsg_create_map(); htsmsg_add_str(m, "identifier", tda->tda_identifier); htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100)); - htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); + if(tda->tda_snr_valid) + htsmsg_add_dbl(m, "snr", tdmi->tdmi_snr); htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); htsmsg_add_dbl(m, "uncavg", tdmi->tdmi_unc_avg); diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index bf3c2cbb..3ecf1f17 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -168,6 +168,17 @@ tvheadend.status_adapters = function() { width : 50, header : "Uncorrected bit error rate", dataIndex : 'uncavg' + },{ + width : 50, + header : "SNR", + dataIndex : 'snr', + renderer: function(value) { + if(value > 0) { + return value.toFixed(1) + " dB"; + } else { + return 'Unknown'; + } + } }, signal]); var panel = new Ext.grid.GridPanel({ From e74f53574cf1449c8e94d22da0bb2fca50473b65 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 20 Nov 2012 22:02:00 +0000 Subject: [PATCH 114/503] Fix #1397 - only load adapter config if tab is enabled. --- src/webui/static/app/tvadapters.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index d6928ed8..dc14c198 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -8,7 +8,6 @@ tvheadend.tvAdapterStore = new Ext.data.JsonStore({ 'hostconnection', 'currentMux', 'services', 'muxes', 'initialMuxes', 'satConf', 'deliverySystem', 'freqMin', 'freqMax', 'freqStep', 'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg'], - autoLoad : true, url : 'tv/adapter' }); @@ -25,6 +24,8 @@ tvheadend.comet.on('tvAdapter', function(m) { }); tvheadend.tvadapters = function() { + tvheadend.tvAdapterStore.load(); + var adapterSelection = new Ext.form.ComboBox({ loadingText : 'Loading...', width : 300, From 459bf3fd50604206806b63c0ab3d26c35fad28bd Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 21 Nov 2012 21:34:31 +0000 Subject: [PATCH 115/503] Re-apply previous fix to JSON escape processing required by opentv config files. --- src/misc/json.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/misc/json.c b/src/misc/json.c index 59f40ebb..08f8a74d 100644 --- a/src/misc/json.c +++ b/src/misc/json.c @@ -62,7 +62,11 @@ json_parse_string(const char *s, const char **endp, if(*s == '\\') { esc = 1; - } else if(*s == '"' && s[-1] != '\\') { + /* skip the escape */ + s++; + if (*s == 'u') s += 4; + // Note: we could detect the lack of support here! + } else if(*s == '"') { *endp = s + 1; @@ -82,6 +86,8 @@ json_parse_string(const char *s, const char **endp, a++; if(*a == 'b') *b++ = '\b'; + else if(*a == '\\') + *b++ = '\\'; else if(*a == 'f') *b++ = '\f'; else if(*a == 'n') From bf59755681f87157447fe865f46f849348e3255d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 26 Nov 2012 11:34:59 +0100 Subject: [PATCH 116/503] make sure pvr muxer is freed --- src/dvr/dvr_rec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 1e279e7b..a44b15c1 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -544,6 +544,10 @@ dvr_thread(void *aux) pthread_mutex_lock(&sq->sq_mutex); } pthread_mutex_unlock(&sq->sq_mutex); + + if(de->de_mux) + dvr_thread_epilog(de); + return NULL; } From d9b9b26fd95b0cef9244936e42aa17503c504ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 26 Nov 2012 11:36:47 +0100 Subject: [PATCH 117/503] fix bug where recording stopped when the stream was reconfigured --- src/dvr/dvr_rec.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index a44b15c1..48696d42 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -472,9 +472,11 @@ dvr_thread(void *aux) break; case SMT_STOP: + if(sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) { + // Subscription is restarting, wait for SMT_START - if(sm->sm_code == 0) { - /* Completed */ + } else if(sm->sm_code == 0) { + // Recording is completed de->de_last_error = 0; @@ -482,18 +484,21 @@ dvr_thread(void *aux) "dvr", "Recording completed: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL)); - } else if(sm->sm_code != SM_CODE_SOURCE_RECONFIGURED) { - if(de->de_last_error != sm->sm_code) { - dvr_rec_set_state(de, DVR_RS_ERROR, sm->sm_code); + dvr_thread_epilog(de); + started = 0; - tvhlog(LOG_ERR, - "dvr", "Recording stopped: \"%s\": %s", - de->de_filename ?: lang_str_get(de->de_title, NULL), - streaming_code2txt(sm->sm_code)); - } + }else if(de->de_last_error != sm->sm_code) { + // Error during recording + + dvr_rec_set_state(de, DVR_RS_ERROR, sm->sm_code); + tvhlog(LOG_ERR, + "dvr", "Recording stopped: \"%s\": %s", + de->de_filename ?: lang_str_get(de->de_title, NULL), + streaming_code2txt(sm->sm_code)); + + dvr_thread_epilog(de); + started = 0; } - - dvr_thread_epilog(de); break; case SMT_SERVICE_STATUS: From 575f516ad50d29b051046bfcd6654a92269afd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 26 Nov 2012 11:37:27 +0100 Subject: [PATCH 118/503] if reconfiguration of the muxer fails, restart recording to a new file --- src/dvr/dvr_rec.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 48696d42..7401dd32 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -458,17 +458,25 @@ dvr_thread(void *aux) break; case SMT_START: + if(started && + muxer_reconfigure(de->de_mux, sm->sm_data) < 0) { + tvhlog(LOG_WARNING, + "dvr", "Unable to reconfigure \"%s\"", + de->de_filename ?: lang_str_get(de->de_title, NULL)); + + // Try to restart the recording if the muxer doesn't + // support reconfiguration of the streams. + dvr_thread_epilog(de); + started = 0; + } + if(!started) { pthread_mutex_lock(&global_lock); dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0); if(dvr_rec_start(de, sm->sm_data) == 0) started = 1; pthread_mutex_unlock(&global_lock); - } else if(muxer_reconfigure(de->de_mux, sm->sm_data) < 0) { - tvhlog(LOG_WARNING, - "dvr", "Unable to reconfigure the recording \"%s\"", - de->de_filename ?: lang_str_get(de->de_title, NULL)); - } + } break; case SMT_STOP: @@ -479,7 +487,6 @@ dvr_thread(void *aux) // Recording is completed de->de_last_error = 0; - tvhlog(LOG_INFO, "dvr", "Recording completed: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL)); From 95134ddbb51d40b5534a4aa534bd7b92d8752a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 26 Nov 2012 11:38:20 +0100 Subject: [PATCH 119/503] log aspect ratio of recordings --- src/dvr/dvr_rec.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 7401dd32..925d721d 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -371,25 +371,32 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) tvhlog(LOG_INFO, "dvr", - " # %-20s %-4s %-16s %-10s %-10s", + " # %-16s %-4s %-10s %-12s %-11s %-8s", "type", "lang", "resolution", - "samplerate", + "aspect ratio", + "sample rate", "channels"); for(i = 0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; - char res[16]; + char res[11]; + char asp[6]; char sr[6]; char ch[7]; if(SCT_ISAUDIO(ssc->ssc_type)) { - snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri)); + if(ssc->ssc_sri) + snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri)); + else + strcpy(sr, "?"); if(ssc->ssc_channels == 6) snprintf(ch, sizeof(ch), "5.1"); + else if(ssc->ssc_channels == 0) + strcpy(ch, "?"); else snprintf(ch, sizeof(ch), "%d", ssc->ssc_channels); } else { @@ -397,20 +404,33 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) ch[0] = 0; } - if(SCT_ISVIDEO(ssc->ssc_type)) { - snprintf(res, sizeof(res), "%d x %d", - ssc->ssc_width, ssc->ssc_height); + if(ssc->ssc_width && ssc->ssc_height) + snprintf(res, sizeof(res), "%dx%d", + ssc->ssc_width, ssc->ssc_height); + else + strcpy(res, "?"); } else { res[0] = 0; } + if(SCT_ISVIDEO(ssc->ssc_type)) { + if(ssc->ssc_aspect_num && ssc->ssc_aspect_den) + snprintf(asp, sizeof(asp), "%d:%d", + ssc->ssc_aspect_num, ssc->ssc_aspect_den); + else + strcpy(asp, "?"); + } else { + asp[0] = 0; + } + tvhlog(LOG_INFO, "dvr", - "%2d %-20s %-4s %-16s %-10s %-10s %s", + "%2d %-16s %-4s %-10s %-12s %-11s %-8s %s", ssc->ssc_index, streaming_component_type2txt(ssc->ssc_type), ssc->ssc_lang, res, + asp, sr, ch, ssc->ssc_disabled ? "" : ""); From 8ff12894aa32493e0044189654366472f5bd14ff Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 26 Nov 2012 14:54:18 +0000 Subject: [PATCH 120/503] xmltv: was incorrectly blocking symlinked scripts. --- src/epggrab/module/xmltv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 55f0bc62..0352e4bb 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -677,7 +677,7 @@ static void _xmltv_load_grabbers ( void ) while ((de = readdir(dir))) { if (strstr(de->d_name, XMLTV_GRAB) != de->d_name) continue; snprintf(bin, sizeof(bin), "%s/%s", tmp, de->d_name); - if (lstat(bin, &st)) continue; + if (stat(bin, &st)) continue; if (!(st.st_mode & S_IEXEC)) continue; if (!S_ISREG(st.st_mode)) continue; if ((outlen = spawn_and_store_stdout(bin, argv, &outbuf)) > 0) { From 6254fb56225ff4501f70d7844cbfee9feb5e0ff6 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 2 Nov 2012 15:37:11 +0000 Subject: [PATCH 121/503] util: Added a generic makedirs replacement for the 2 diff implementations. --- src/dvr/dvr_rec.c | 51 +---------------------------------------------- src/settings.c | 19 +++++++----------- src/tvheadend.h | 2 ++ src/utils.c | 36 +++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 925d721d..6dc560dd 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -117,55 +117,6 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode) } -/** - * - */ -static int -makedirs(const char *path) -{ - struct stat st; - char *p; - int l, r; - - if(stat(path, &st) == 0 && S_ISDIR(st.st_mode)) - return 0; /* Dir already there */ - - if(mkdir(path, 0777) == 0) - return 0; /* Dir created ok */ - - if(errno == ENOENT) { - - /* Parent does not exist, try to create it */ - /* Allocate new path buffer and strip off last directory component */ - - l = strlen(path); - p = alloca(l + 1); - memcpy(p, path, l); - p[l--] = 0; - - for(; l >= 0; l--) - if(p[l] == '/') - break; - if(l == 0) { - return ENOENT; - } - p[l] = 0; - - if((r = makedirs(p)) != 0) - return r; - - /* Try again */ - if(mkdir(path, 0777) == 0) - return 0; /* Dir created ok */ - } - r = errno; - - tvhlog(LOG_ERR, "dvr", "Unable to create directory \"%s\" -- %s", - path, strerror(r)); - return r; -} - - /** * Replace various chars with a dash */ @@ -247,7 +198,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) /* */ - if(makedirs(path) != 0) { + if(makedirs(path, 0777) != 0) { return -1; } diff --git a/src/settings.c b/src/settings.c index 8f0de39b..3857a383 100644 --- a/src/settings.c +++ b/src/settings.c @@ -83,23 +83,18 @@ hts_settings_init(const char *confpath) int hts_settings_makedirs ( const char *inpath ) { - size_t x; - struct stat st; + size_t x = strlen(inpath) - 1; char path[512]; - size_t l = strlen(inpath); strcpy(path, inpath); - for(x = 0; x < l; x++) { - if(path[x] == '/' && x != 0) { + + while (x) { + if (path[x] == '/') { path[x] = 0; - if(stat(path, &st) && mkdir(path, 0700)) { - tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s", - path, strerror(errno)); - return -1; - } - path[x] = '/'; + break; } + x--; } - return 0; + return makedirs(path, 0700); } /** diff --git a/src/tvheadend.h b/src/tvheadend.h index e117f6ab..64a3318d 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -480,6 +480,8 @@ void sbuf_put_byte(sbuf_t *sb, uint8_t u8); char *md5sum ( const char *str ); +int makedirs ( const char *path, int mode ); + /* printing */ #if __SIZEOF_LONG__ == 8 #define PRItime_t PRId64 diff --git a/src/utils.c b/src/utils.c index 89ae56d4..7aaca13a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "tvheadend.h" /** @@ -337,3 +338,38 @@ md5sum ( const char *str ) ret[MD5_DIGEST_LENGTH*2] = '\0'; return ret; } + +int +makedirs ( const char *inpath, int mode ) +{ + int err, ok; + size_t x; + struct stat st; + char path[512]; + + if (!inpath || !*inpath) return -1; + + x = 1; + ok = 1; + strcpy(path, inpath); + while(ok) { + ok = path[x]; + if (path[x] == '/' || !path[x]) { + path[x] = 0; + if (stat(path, &st)) { + err = mkdir(path, mode); + } else { + err = S_ISDIR(st.st_mode) ? 0 : 1; + errno = ENOTDIR; + } + if (err) { + tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s", + path, strerror(errno)); + return -1; + } + path[x] = '/'; + } + x++; + } + return 0; +} From 41f1c671f07af57e590c23ea2d4ab7607e8c4df7 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 2 Nov 2012 16:32:28 +0000 Subject: [PATCH 122/503] util: Add util function to remove an entire directory tree (dangerous?). --- src/tvheadend.h | 2 ++ src/utils.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/tvheadend.h b/src/tvheadend.h index 64a3318d..f03ae96d 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -482,6 +482,8 @@ char *md5sum ( const char *str ); int makedirs ( const char *path, int mode ); +int rmtree ( const char *path ); + /* printing */ #if __SIZEOF_LONG__ == 8 #define PRItime_t PRId64 diff --git a/src/utils.c b/src/utils.c index 7aaca13a..0ed2181d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include "tvheadend.h" /** @@ -373,3 +376,30 @@ makedirs ( const char *inpath, int mode ) } return 0; } + +int +rmtree ( const char *path ) +{ + int err; + struct dirent de, *der; + struct stat st; + char buf[512]; + DIR *dir = opendir(path); + if (!dir) return -1; + while (!readdir_r(dir, &de, &der) && der) { + if (!strcmp("..", de.d_name) || !strcmp(".", de.d_name)) + continue; + snprintf(buf, sizeof(buf), "%s/%s", path, de.d_name); + err = stat(buf, &st); + if (err) break; + if (S_ISDIR(st.st_mode)) + err = rmtree(buf); + else + err = unlink(buf); + if (err) break; + } + closedir(dir); + if (!err) + err = rmdir(path); + return err; +} From fa6d71a719438d717279b5c8d02dc5b5036f1451 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 2 Nov 2012 22:21:57 +0000 Subject: [PATCH 123/503] rawts: Remove debug. --- src/rawtsinput.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rawtsinput.c b/src/rawtsinput.c index bd4a5098..315e1651 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -72,13 +72,12 @@ static void rawts_service_save(service_t *t) { htsmsg_t *m = htsmsg_create_map(); - printf("SAVE %s\n", service_nicename(t)); pthread_mutex_lock(&t->s_stream_mutex); psi_save_service_settings(m, t); pthread_mutex_unlock(&t->s_stream_mutex); - htsmsg_print(m); + //htsmsg_print(m); htsmsg_destroy(m); } From 517d01378a45cdab3a9aa9fe1afe8e740d89a7f9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 27 Nov 2012 13:28:34 +0000 Subject: [PATCH 124/503] util: Added new tvh_pipe() helper routine. --- src/tvheadend.h | 8 ++++++++ src/wrappers.c | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/tvheadend.h b/src/tvheadend.h index f03ae96d..0ceb242c 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -440,10 +440,18 @@ static inline const char *tvh_strbegins(const char *s1, const char *s2) return s1; } +typedef struct th_pipe +{ + int rd; + int wr; +} th_pipe_t; + int tvh_open(const char *pathname, int flags, mode_t mode); int tvh_socket(int domain, int type, int protocol); +int tvh_pipe(int flags, th_pipe_t *pipe); + void hexdump(const char *pfx, const uint8_t *data, int len); uint32_t tvh_crc32(uint8_t *data, size_t datalen, uint32_t crc); diff --git a/src/wrappers.c b/src/wrappers.c index ce38af94..fd374ec8 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -1,6 +1,7 @@ #include #include /* See NOTES */ #include +#include #include "tvheadend.h" int @@ -29,3 +30,21 @@ tvh_socket(int domain, int type, int protocol) pthread_mutex_unlock(&fork_lock); return fd; } + +int +tvh_pipe(int flags, th_pipe_t *p) +{ + int fd[2], err; + pthread_mutex_lock(&fork_lock); + err = pipe(fd); + if (err != -1) { + fcntl(fd[0], F_SETFD, fcntl(fd[0], F_GETFD) | FD_CLOEXEC); + fcntl(fd[1], F_SETFD, fcntl(fd[1], F_GETFD) | FD_CLOEXEC); + fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | flags); + fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | flags); + p->rd = fd[0]; + p->wr = fd[1]; + } + pthread_mutex_unlock(&fork_lock); + return err; +} From c3d3b3d8078646f8c46289bd0031b34008dead2f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 28 Nov 2012 09:30:58 +0000 Subject: [PATCH 125/503] dvb: updated adapter code to use th_pipe_t --- src/dvb/dvb.h | 2 +- src/dvb/dvb_adapter.c | 26 +++++++++++--------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 5ae0d876..80d8c666 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -228,7 +228,7 @@ typedef struct th_dvb_adapter { char *tda_demux_path; pthread_t tda_dvr_thread; - int tda_dvr_pipe[2]; + th_pipe_t tda_dvr_pipe; int tda_hostconnection; diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8e35dfe7..67b88f10 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -470,7 +470,7 @@ tda_add(int adapter_num) snprintf(tda->tda_demux_path, 256, "%s/demux0", path); tda->tda_fe_path = strdup(fname); tda->tda_fe_fd = -1; - tda->tda_dvr_pipe[0] = -1; + tda->tda_dvr_pipe.rd = -1; tda->tda_full_mux_rx = -1; tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); @@ -540,7 +540,7 @@ tda_add_from_file(const char *filename) tda->tda_adapter_num = -1; tda->tda_fe_fd = -1; - tda->tda_dvr_pipe[0] = -1; + tda->tda_dvr_pipe.rd = -1; tda->tda_type = -1; @@ -598,13 +598,9 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } /* Start DVR thread */ - if (tda->tda_dvr_pipe[0] == -1) { - int err = pipe(tda->tda_dvr_pipe); + if (tda->tda_dvr_pipe.rd == -1) { + int err = tvh_pipe(O_NONBLOCK, &tda->tda_dvr_pipe); assert(err != -1); - - fcntl(tda->tda_dvr_pipe[0], F_SETFD, fcntl(tda->tda_dvr_pipe[0], F_GETFD) | FD_CLOEXEC); - fcntl(tda->tda_dvr_pipe[0], F_SETFL, fcntl(tda->tda_dvr_pipe[0], F_GETFL) | O_NONBLOCK); - fcntl(tda->tda_dvr_pipe[1], F_SETFD, fcntl(tda->tda_dvr_pipe[1], F_GETFD) | FD_CLOEXEC); pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); tvhlog(LOG_DEBUG, "dvb", "%s started dvr thread", tda->tda_rootpath); } @@ -627,14 +623,14 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) } /* Stop DVR thread */ - if (tda->tda_dvr_pipe[0] != -1) { + if (tda->tda_dvr_pipe.rd != -1) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - int err = write(tda->tda_dvr_pipe[1], "", 1); + int err = write(tda->tda_dvr_pipe.wr, "", 1); assert(err != -1); pthread_join(tda->tda_dvr_thread, NULL); - close(tda->tda_dvr_pipe[0]); - close(tda->tda_dvr_pipe[1]); - tda->tda_dvr_pipe[0] = -1; + close(tda->tda_dvr_pipe.rd); + close(tda->tda_dvr_pipe.wr); + tda->tda_dvr_pipe.rd = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } } @@ -921,8 +917,8 @@ dvb_adapter_input_dvr(void *aux) ev.events = EPOLLIN; ev.data.fd = fd; epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); - ev.data.fd = tda->tda_dvr_pipe[0]; - epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe[0], &ev); + ev.data.fd = tda->tda_dvr_pipe.rd; + epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev); r = i = 0; while(1) { From 88067f869e33572b6d0c683c5c63f72edd139076 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Mon, 26 Nov 2012 02:11:51 +0400 Subject: [PATCH 126/503] [PR-177] Added preferred CA pid support Added prefrerred CA pid field in user interface and use field value to store ECM only from stream which is answer ok. Field could be refreshed by program or manually. --- src/cwc.c | 65 ++++++++++++++++++++++++++++++------- src/dvb/dvb_service.c | 6 ++++ src/service.c | 9 +++++ src/service.h | 3 ++ src/webui/extjs.c | 6 ++++ src/webui/static/app/dvb.js | 7 +++- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index 51b8f2e7..52a4489e 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -27,7 +27,6 @@ #include #include #include - #include "tvheadend.h" #include "tcp.h" #include "psi.h" @@ -38,7 +37,7 @@ #include "atomic.h" #include "dtable.h" #include "subscriptions.h" - +#include "service.h" #include /** @@ -754,9 +753,9 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, int len, int seq) { service_t *t = ct->cs_service; + ecm_pid_t *ep, *epn; cwc_service_t *ct2; cwc_t *cwc2; - ecm_pid_t *ep; char chaninfo[32]; int i; int64_t delay = (getmonoclock() - es->es_time) / 1000LL; // in ms @@ -773,9 +772,6 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, /* ERROR */ - if(ct->cs_okchannel == es->es_channel) - ct->cs_okchannel = -1; - if (es->es_nok < 3) es->es_nok++; @@ -829,6 +825,11 @@ forbid: } else { ct->cs_okchannel = es->es_channel; + tvhlog(LOG_DEBUG, "cwc", "es->es_nok %d t->tht_prefcapid %d", es->es_nok, t->s_prefcapid); + if(es->es_nok == 1 || t->s_prefcapid == 0) { + t->s_prefcapid = ct->cs_okchannel; + service_request_save(t, 0); + } es->es_nok = 0; tvhlog(LOG_DEBUG, "cwc", @@ -865,6 +866,22 @@ forbid: ct->cs_keystate = CS_RESOLVED; memcpy(ct->cs_cw, msg + 3, 16); ct->cs_pending_cw_update = 1; + + ep = LIST_FIRST(&ct->cs_pids); + while(ep != NULL) { + if (ct->cs_okchannel == ep->ep_pid) { + ep = LIST_NEXT(ep, ep_link); + } + else { + epn = LIST_NEXT(ep, ep_link); + for(i = 0; i < 256; i++) + free(ep->ep_sections[i]); + LIST_REMOVE(ep, ep_link); + tvhlog(LOG_WARNING, "cwc", "Delete ECMpid %d", ep->ep_pid); + free(ep); + ep = epn; + } + } } } @@ -902,6 +919,10 @@ cwc_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len) } } tvhlog(LOG_WARNING, "cwc", "Got unexpected ECM reply (seqno: %d)", seq); + LIST_FOREACH(ct, &cwc->cwc_services, cs_link) { + tvhlog(LOG_DEBUG, "cwc", "After got unexpected (ct->cs_okchannel: %d)", ct->cs_okchannel); + if (ct->cs_okchannel == -3) ct->cs_okchannel = -2; + } break; } return 0; @@ -1592,9 +1613,28 @@ cwc_table_input(struct th_descrambler *td, struct service *t, } if(ep == NULL) { - ep = calloc(1, sizeof(ecm_pid_t)); - ep->ep_pid = st->es_pid; - LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); + if (ct->cs_okchannel == -2) { + t->s_prefcapid = 0; + ct->cs_okchannel = -1; + tvhlog(LOG_DEBUG, "cwc", "Insert after unexpected reply"); + } + + if (ct->cs_okchannel == -3 && t->s_prefcapid == st->es_pid) { + ep = calloc(1, sizeof(ecm_pid_t)); + ep->ep_pid = t->s_prefcapid; + LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); + tvhlog(LOG_DEBUG, "cwc", "Insert only one new ECM channel %d for service id %d", t->s_prefcapid, sid); + } + + if (ct->cs_okchannel == -1 || (ct->cs_okchannel == -3 && t->s_prefcapid == 0)) { + ep = calloc(1, sizeof(ecm_pid_t)); + ep->ep_pid = st->es_pid; + LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); + tvhlog(LOG_DEBUG, "cwc", "Insert new ECM channel %d", st->es_pid); + } + else { + return; + } } @@ -1626,6 +1666,9 @@ cwc_table_input(struct th_descrambler *td, struct service *t, section = 0; } + channel = st->es_pid; + snprintf(chaninfo, sizeof(chaninfo), " (channel %d)", channel); + if(ep->ep_sections[section] == NULL) ep->ep_sections[section] = calloc(1, sizeof(ecm_section_t)); @@ -1650,7 +1693,7 @@ cwc_table_input(struct th_descrambler *td, struct service *t, memcpy(es->es_ecm, data, len); es->es_ecmsize = len; - if(ct->cs_okchannel != -1 && channel != -1 && + if(ct->cs_okchannel >= 0 && channel != -1 && ct->cs_okchannel != channel) { tvhlog(LOG_DEBUG, "cwc", "Filtering ECM channel %d", channel); return; @@ -1988,7 +2031,7 @@ cwc_service_start(service_t *t) ct->cs_keys = get_key_struct(); ct->cs_cwc = cwc; ct->cs_service = t; - ct->cs_okchannel = -1; + ct->cs_okchannel = -3; td = &ct->cs_head; td->td_stop = cwc_service_destroy; diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 82621227..65b7fbf5 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -259,6 +259,10 @@ dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) if(s && u32) service_map_channel(t, channel_find_by_name(s, 1, 0), 0); + if(htsmsg_get_u32(c, "prefcapid", &u32)) + u32 = 0; + t->s_prefcapid = u32; + /* HACK - force save for old config */ if(old) dvb_service_save(t); @@ -471,6 +475,8 @@ dvb_service_build_msg(service_t *t) htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable); + htsmsg_add_u32(m, "prefcapid", t->s_prefcapid); + return m; } diff --git a/src/service.c b/src/service.c index 6548f104..e189ae1d 100644 --- a/src/service.c +++ b/src/service.c @@ -933,6 +933,15 @@ service_set_enable(service_t *t, int enabled) subscription_reschedule(); } +void +service_set_prefcapid(service_t *t, uint32_t prefcapid) +{ + if(t->s_prefcapid == prefcapid) + return; + + t->s_prefcapid = prefcapid; + t->s_config_save(t); +} static pthread_mutex_t pending_save_mutex; static pthread_cond_t pending_save_cond; diff --git a/src/service.h b/src/service.h index 11e39b5d..b6463fc0 100644 --- a/src/service.h +++ b/src/service.h @@ -474,6 +474,7 @@ typedef struct service { int s_scrambled; int s_scrambled_seen; int s_caid; + uint16_t s_prefcapid; /** * PCR drift compensation. This should really be per-packet. @@ -597,6 +598,8 @@ void service_set_dvb_charset(service_t *t, const char *dvb_charset); void service_set_dvb_eit_enable(service_t *t, int dvb_eit_enable); +void service_set_prefcapid(service_t *t, uint32_t prefcapid); + int service_is_primary_epg (service_t *t); htsmsg_t *servicetype_list (void); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 10829e2e..6355e236 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1469,6 +1469,9 @@ service_update(htsmsg_t *in) if((chname = htsmsg_get_str(c, "channelname")) != NULL) service_map_channel(t, channel_find_by_name(chname, 1, 0), 1); + if(!htsmsg_get_u32(c, "prefcapid", &u32)) + service_set_prefcapid(t, u32); + if((dvb_charset = htsmsg_get_str(c, "dvb_charset")) != NULL) service_set_dvb_charset(t, dvb_charset); @@ -1800,6 +1803,9 @@ extjs_service_update(htsmsg_t *in) if(!htsmsg_get_u32(c, "enabled", &u32)) service_set_enable(t, u32); + if(!htsmsg_get_u32(c, "prefcapid", &u32)) + service_set_prefcapid(t, u32); + if((chname = htsmsg_get_str(c, "channelname")) != NULL) service_map_channel(t, channel_find_by_name(chname, 1, 0), 1); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 746ac7d6..0f1b836f 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -506,6 +506,11 @@ tvheadend.dvb_services = function(adapterId) { dataIndex : 'sid', width : 50, hidden : true + }, { + header: "Preffered CA pid", + dataIndex: 'prefcapid', + width: 50, + editor: new fm.TextField({allowBlank: true}) }, { header : "PMT PID", dataIndex : 'pmt', @@ -521,7 +526,7 @@ tvheadend.dvb_services = function(adapterId) { var store = new Ext.data.JsonStore({ root : 'entries', fields : Ext.data.Record.create([ 'id', 'enabled', 'type', 'sid', 'pmt', - 'pcr', 'svcname', 'network', 'provider', 'mux', 'channelname', + 'pcr', 'svcname', 'network', 'provider', 'mux', 'channelname', 'prefcapid', 'dvb_charset', 'dvb_eit_enable' ]), url : "dvb/services/" + adapterId, autoLoad : true, From 303313a82fe24aa918c5a1fda042e4c8695f08f5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 28 Nov 2012 11:19:05 +0000 Subject: [PATCH 127/503] support: minor addition to the configure.inc script for cc lib checking. --- support/configure.inc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/support/configure.inc b/support/configure.inc index db5ad178..0130880c 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -255,7 +255,7 @@ int main() { return 0; } EOF - $CC $TMPDIR/$$.c -o $TMPDIR/$$.bin $opt &> /dev/null + $CC $CFLAGS $LDFLAGS $TMPDIR/$$.c -o $TMPDIR/$$.bin $opt &> /dev/null RET=$? rm -f $TMPDIR/$$.{c,bin} return $RET @@ -318,6 +318,25 @@ function check_cc_option fi } +# Check compiler library +function check_cc_lib +{ + local opt=$1 + local nam=$2 + [ -z "$nam" ] && nam=$opt + + echo -ne "checking for cc -l$opt ...${TAB}" + + # Enable if supported + if check_cc "" -l${opt}; then + echo "ok" + enable $nam + else + echo "fail" + return 1 + fi +} + # ########################################################################### # Python tests # ########################################################################### From 7c1819ba92d4fd3dec896d408625a85083a37ba1 Mon Sep 17 00:00:00 2001 From: Alain Kalker Date: Sat, 11 Feb 2012 17:35:03 +0100 Subject: [PATCH 128/503] [PR-54] Switch to libdvbcsa: update code word client and campt As libdvbcsa works on packet payloads instead of full packets, I borrowed the packet inspection code from FFdecsa. Tested and found working with Irdeto2 CA system and OSCam's NewCamd emulation. As the capmt code doesn't use key change notification, there might be a race between key updates and decryption, when there is undecrypted data batched up. This has not been tested yet, as I don't have a capmt cardserver. --- src/capmt.c | 110 +++++++++++++++++++++++++++++++++++++++------------ src/cwc.c | 112 +++++++++++++++++++++++++++++++++++++--------------- src/main.c | 2 - 3 files changed, 164 insertions(+), 60 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index 1bfc0300..0f779ed0 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -42,7 +42,7 @@ #include "tcp.h" #include "psi.h" #include "tsdemux.h" -#include "ffdecsa/FFdecsa.h" +#include #include "capmt.h" #include "notify.h" #include "subscriptions.h" @@ -155,13 +155,18 @@ typedef struct capmt_service { CT_FORBIDDEN } ct_keystate; - /* buffer for keystruct */ - void *ct_keys; + /* buffers for keystructs */ + struct dvbcsa_bs_key_s *ct_key_even; + struct dvbcsa_bs_key_s *ct_key_odd; /* CSA */ int ct_cluster_size; uint8_t *ct_tsbcluster; + struct dvbcsa_bs_batch_s *ct_tsbbatch_even; + struct dvbcsa_bs_batch_s *ct_tsbbatch_odd; int ct_fill; + int ct_fill_even; + int ct_fill_odd; /* current sequence number */ uint16_t ct_seq; @@ -359,7 +364,10 @@ capmt_service_destroy(th_descrambler_t *td) LIST_REMOVE(ct, ct_link); - free_key_struct(ct->ct_keys); + dvbcsa_bs_key_free(ct->ct_key_odd); + dvbcsa_bs_key_free(ct->ct_key_even); + free(ct->ct_tsbbatch_odd); + free(ct->ct_tsbbatch_even); free(ct->ct_tsbcluster); free(ct); } @@ -498,9 +506,9 @@ handle_ca0(capmt_t* capmt) { continue; if (memcmp(even, invalid, 8)) - set_even_control_word(ct->ct_keys, even); + dvbcsa_bs_key_set(even, ct->ct_key_even); if (memcmp(odd, invalid, 8)) - set_odd_control_word(ct->ct_keys, odd); + dvbcsa_bs_key_set(odd, ct->ct_key_odd); if(ct->ct_keystate != CT_RESOLVED) tvhlog(LOG_INFO, "capmt", "Obtained key for service \"%s\"",t->s_svcname); @@ -839,8 +847,14 @@ capmt_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *s const uint8_t *tsb) { capmt_service_t *ct = (capmt_service_t *)td; - int r, i; - unsigned char *vec[3]; + uint8_t *pkt; + int xc0; + int ev_od; + int len; + int offset; + int n; + // FIXME: //int residue; + int i; uint8_t *t0; if(ct->ct_keystate == CT_FORBIDDEN) @@ -849,28 +863,67 @@ capmt_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *s if(ct->ct_keystate != CT_RESOLVED) return -1; - memcpy(ct->ct_tsbcluster + ct->ct_fill * 188, tsb, 188); + pkt = ct->ct_tsbcluster + ct->ct_fill * 188; + memcpy(pkt, tsb, 188); ct->ct_fill++; + do { // handle this packet + xc0 = pkt[3] & 0xc0; + if(xc0 == 0x00) { // clear + break; + } + if(xc0 == 0x40) { // reserved + break; + } + if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted + ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd + pkt[3] &= 0x3f; // consider it decrypted now + if(pkt[3] & 0x20) { // incomplete packet + offset = 4 + pkt[4] + 1; + len = 188 - offset; + n = len >> 3; + // FIXME: //residue = len - (n << 3); + if(n == 0) { // decrypted==encrypted! + break; // this doesn't need more processing + } + } else { + len = 184; + offset = 4; + // FIXME: //n = 23; + // FIXME: //residue = 0; + } + if(ev_od == 0) { + ct->ct_tsbbatch_even[ct->ct_fill_even].data = pkt + offset; + ct->ct_tsbbatch_even[ct->ct_fill_even].len = len; + ct->ct_fill_even++; + } else { + ct->ct_tsbbatch_odd[ct->ct_fill_odd].data = pkt + offset; + ct->ct_tsbbatch_odd[ct->ct_fill_odd].len = len; + ct->ct_fill_odd++; + } + } + } while(0); + if(ct->ct_fill != ct->ct_cluster_size) return 0; - ct->ct_fill = 0; + if(ct->ct_fill_even) { + ct->ct_tsbbatch_even[ct->ct_fill_even].data = NULL; + dvbcsa_bs_decrypt(ct->ct_key_even, ct->ct_tsbbatch_even, 184); + ct->ct_fill_even = 0; + } + if(ct->ct_fill_odd) { + ct->ct_tsbbatch_odd[ct->ct_fill_odd].data = NULL; + dvbcsa_bs_decrypt(ct->ct_key_odd, ct->ct_tsbbatch_odd, 184); + ct->ct_fill_odd = 0; + } - vec[0] = ct->ct_tsbcluster; - vec[1] = ct->ct_tsbcluster + ct->ct_cluster_size * 188; - vec[2] = NULL; - - while(1) { - t0 = vec[0]; - r = decrypt_packets(ct->ct_keys, vec); - if(r == 0) - break; - for(i = 0; i < r; i++) { + t0 = ct->ct_tsbcluster; + for(i = 0; i < ct->ct_fill; i++) { ts_recv_packet2(t, t0); t0 += 188; } - } + ct->ct_fill = 0; return 0; } @@ -898,10 +951,14 @@ capmt_service_start(service_t *t) elementary_stream_t *st; /* create new capmt service */ - ct = calloc(1, sizeof(capmt_service_t)); - ct->ct_cluster_size = get_suggested_cluster_size(); - ct->ct_tsbcluster = malloc(ct->ct_cluster_size * 188); - ct->ct_seq = capmt->capmt_seq++; + ct = calloc(1, sizeof(capmt_service_t)); + ct->ct_cluster_size = dvbcsa_bs_batch_size(); + ct->ct_tsbcluster = malloc(ct->ct_cluster_size * 188); + ct->ct_tsbbatch_even = malloc((ct->ct_cluster_size + 1) * + sizeof(struct dvbcsa_bs_batch_s)); + ct->ct_tsbbatch_odd = malloc((ct->ct_cluster_size + 1) * + sizeof(struct dvbcsa_bs_batch_s)); + ct->ct_seq = capmt->capmt_seq++; TAILQ_FOREACH(st, &t->s_components, es_link) { caid_t *c; @@ -924,7 +981,8 @@ capmt_service_start(service_t *t) } } - ct->ct_keys = get_key_struct(); + ct->ct_key_even = dvbcsa_bs_key_alloc(); + ct->ct_key_odd = dvbcsa_bs_key_alloc(); ct->ct_capmt = capmt; ct->ct_service = t; diff --git a/src/cwc.c b/src/cwc.c index 52a4489e..242478ca 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -31,7 +31,7 @@ #include "tcp.h" #include "psi.h" #include "tsdemux.h" -#include "ffdecsa/FFdecsa.h" +#include #include "cwc.h" #include "notify.h" #include "atomic.h" @@ -155,7 +155,8 @@ typedef struct cwc_service { CS_IDLE } cs_keystate; - void *cs_keys; + struct dvbcsa_bs_key_s *cs_key_even; + struct dvbcsa_bs_key_s *cs_key_odd; uint8_t cs_cw[16]; @@ -166,7 +167,11 @@ typedef struct cwc_service { */ int cs_cluster_size; uint8_t *cs_tsbcluster; + struct dvbcsa_bs_batch_s *cs_tsbbatch_even; + struct dvbcsa_bs_batch_s *cs_tsbbatch_odd; int cs_fill; + int cs_fill_even; + int cs_fill_odd; LIST_HEAD(, ecm_pid) cs_pids; @@ -1884,13 +1889,13 @@ update_keys(cwc_service_t *ct) ct->cs_pending_cw_update = 0; for(i = 0; i < 8; i++) if(ct->cs_cw[i]) { - set_even_control_word(ct->cs_keys, ct->cs_cw); + dvbcsa_bs_key_set(ct->cs_cw, ct->cs_key_even); break; } for(i = 0; i < 8; i++) if(ct->cs_cw[8 + i]) { - set_odd_control_word(ct->cs_keys, ct->cs_cw + 8); + dvbcsa_bs_key_set(ct->cs_cw + 8, ct->cs_key_odd); break; } } @@ -1904,8 +1909,13 @@ cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, const uint8_t *tsb) { cwc_service_t *ct = (cwc_service_t *)td; - int r; - unsigned char *vec[3]; + uint8_t *pkt; + int xc0; + int ev_od; + int len; + int offset; + int n; + // FIXME: //int residue; if(ct->cs_keystate == CS_FORBIDDEN) return 1; @@ -1916,42 +1926,72 @@ cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, if(ct->cs_fill == 0 && ct->cs_pending_cw_update) update_keys(ct); - memcpy(ct->cs_tsbcluster + ct->cs_fill * 188, tsb, 188); + pkt = ct->cs_tsbcluster + ct->cs_fill * 188; + memcpy(pkt, tsb, 188); ct->cs_fill++; + do { // handle this packet + xc0 = pkt[3] & 0xc0; + if(xc0 == 0x00) { // clear + break; + } + if(xc0 == 0x40) { // reserved + break; + } + if(xc0 == 0x80 || xc0 == 0xc0) { // encrypted + ev_od = (xc0 & 0x40) >> 6; // 0 even, 1 odd + pkt[3] &= 0x3f; // consider it decrypted now + if(pkt[3] & 0x20) { // incomplete packet + offset = 4 + pkt[4] + 1; + len = 188 - offset; + n = len >> 3; + // FIXME: //residue = len - (n << 3); + if(n == 0) { // decrypted==encrypted! + break; // this doesn't need more processing + } + } else { + len = 184; + offset = 4; + // FIXME: //n = 23; + // FIXME: //residue = 0; + } + if(ev_od == 0) { + ct->cs_tsbbatch_even[ct->cs_fill_even].data = pkt + offset; + ct->cs_tsbbatch_even[ct->cs_fill_even].len = len; + ct->cs_fill_even++; + } else { + ct->cs_tsbbatch_odd[ct->cs_fill_odd].data = pkt + offset; + ct->cs_tsbbatch_odd[ct->cs_fill_odd].len = len; + ct->cs_fill_odd++; + } + } + } while(0); + if(ct->cs_fill != ct->cs_cluster_size) return 0; - while(1) { + if(ct->cs_fill_even) { + ct->cs_tsbbatch_even[ct->cs_fill_even].data = NULL; + dvbcsa_bs_decrypt(ct->cs_key_even, ct->cs_tsbbatch_even, 184); + ct->cs_fill_even = 0; + } + if(ct->cs_fill_odd) { + ct->cs_tsbbatch_odd[ct->cs_fill_odd].data = NULL; + dvbcsa_bs_decrypt(ct->cs_key_odd, ct->cs_tsbbatch_odd, 184); + ct->cs_fill_odd = 0; + } - vec[0] = ct->cs_tsbcluster; - vec[1] = ct->cs_tsbcluster + ct->cs_fill * 188; - vec[2] = NULL; - - r = decrypt_packets(ct->cs_keys, vec); - if(r > 0) { + { int i; const uint8_t *t0 = ct->cs_tsbcluster; - for(i = 0; i < r; i++) { + for(i = 0; i < ct->cs_fill; i++) { ts_recv_packet2(t, t0); t0 += 188; } - - r = ct->cs_fill - r; - assert(r >= 0); - - if(r > 0) - memmove(ct->cs_tsbcluster, t0, r * 188); - ct->cs_fill = r; - - if(ct->cs_pending_cw_update && r > 0) - continue; - } else { - ct->cs_fill = 0; - } - break; } + ct->cs_fill = 0; + if(ct->cs_pending_cw_update) update_keys(ct); @@ -1980,7 +2020,10 @@ cwc_service_destroy(th_descrambler_t *td) LIST_REMOVE(ct, cs_link); - free_key_struct(ct->cs_keys); + dvbcsa_bs_key_free(ct->cs_key_odd); + dvbcsa_bs_key_free(ct->cs_key_even); + free(ct->cs_tsbbatch_odd); + free(ct->cs_tsbbatch_even); free(ct->cs_tsbcluster); free(ct); } @@ -2025,10 +2068,15 @@ cwc_service_start(service_t *t) continue; ct = calloc(1, sizeof(cwc_service_t)); - ct->cs_cluster_size = get_suggested_cluster_size(); + ct->cs_cluster_size = dvbcsa_bs_batch_size(); ct->cs_tsbcluster = malloc(ct->cs_cluster_size * 188); + ct->cs_tsbbatch_even = malloc((ct->cs_cluster_size + 1) * + sizeof(struct dvbcsa_bs_batch_s)); + ct->cs_tsbbatch_odd = malloc((ct->cs_cluster_size + 1) * + sizeof(struct dvbcsa_bs_batch_s)); - ct->cs_keys = get_key_struct(); + ct->cs_key_even = dvbcsa_bs_key_alloc(); + ct->cs_key_odd = dvbcsa_bs_key_alloc(); ct->cs_cwc = cwc; ct->cs_service = t; ct->cs_okchannel = -3; diff --git a/src/main.c b/src/main.c index 580a151c..7ee0e70f 100644 --- a/src/main.c +++ b/src/main.c @@ -468,8 +468,6 @@ main(int argc, char **argv) htsp_init(); - ffdecsa_init(); - if(rawts_input != NULL) rawts_init(rawts_input); From 592a38f9f2ed1e86ae6dc39d5e699318b311a0c5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 28 Nov 2012 11:19:44 +0000 Subject: [PATCH 129/503] [PR-54] updated code from the PR to be optional. I have decided to include this as there is some suggestion the performance will be better on ARM (non-x86) processors where we currently have no optimised code. --- Makefile | 16 ++++--- configure | 12 +++++ src/capmt.c | 81 ++++++++++++++++++++++++++++++++-- src/cwc.c | 123 +++++++++++++++++++++++++++++++++++++++++++++------- src/main.c | 4 ++ 5 files changed, 210 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 3da26556..8c6b2936 100644 --- a/Makefile +++ b/Makefile @@ -166,20 +166,22 @@ SRCS-${CONFIG_V4L} += \ src/v4l.c \ src/webui/extjs_v4l.c \ -# CWC -SRCS-${CONFIG_CWC} += src/cwc.c \ - src/capmt.c \ - src/ffdecsa/ffdecsa_interface.c \ - src/ffdecsa/ffdecsa_int.c - # Avahi SRCS-$(CONFIG_AVAHI) += src/avahi.c -# Optimised code +# CWC +SRCS-${CONFIG_CWC} += src/cwc.c \ + src/capmt.c + +# FFdecsa +ifneq ($(CONFIG_DVBCSA),yes) +SRCS-${CONFIG_CWC} += src/ffdecsa/ffdecsa_interface.c \ + src/ffdecsa/ffdecsa_int.c SRCS-${CONFIG_MMX} += src/ffdecsa/ffdecsa_mmx.c SRCS-${CONFIG_SSE2} += src/ffdecsa/ffdecsa_sse2.c ${BUILDDIR}/src/ffdecsa/ffdecsa_mmx.o : CFLAGS += -mmmx ${BUILDDIR}/src/ffdecsa/ffdecsa_sse2.o : CFLAGS += -msse2 +endif # File bundles SRCS-${CONFIG_BUNDLE} += bundle.c diff --git a/configure b/configure index cd493750..8e02c1b4 100755 --- a/configure +++ b/configure @@ -23,6 +23,7 @@ OPTIONS=( "avahi:auto" "zlib:auto" "bundle:no" + "dvbcsa:no" ) # @@ -116,6 +117,17 @@ if enabled linuxdvb && enabled dvbscan; then fi fi +# +# libdvbcsa +# +if enabled cwc && enabled dvbcsa; then + (check_cc_header "dvbcsa/dvbcsa" dvbcsa_h &&\ + check_cc_lib dvbcsa dvbcsa_l) ||\ + die "Failed to find dvbcsa support (use --disable-dvbcsa)" + LDFLAGS="$LDFLAGS -ldvbcsa" +fi + + # ########################################################################### # Write config # ########################################################################### diff --git a/src/capmt.c b/src/capmt.c index 0f779ed0..f10778b3 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -42,12 +42,17 @@ #include "tcp.h" #include "psi.h" #include "tsdemux.h" -#include #include "capmt.h" #include "notify.h" #include "subscriptions.h" #include "dtable.h" +#if ENABLE_DVBCSA +#include +#else +#include "ffdecsa/FFdecsa.h" +#endif + // ca_pmt_list_management values: #define CAPMT_LIST_MORE 0x00 // append a 'MORE' CAPMT object the list and start receiving the next object #define CAPMT_LIST_FIRST 0x01 // clear the list when a 'FIRST' CAPMT object is received, and start receiving the next object @@ -156,17 +161,23 @@ typedef struct capmt_service { } ct_keystate; /* buffers for keystructs */ +#if ENABLE_DVBCSA struct dvbcsa_bs_key_s *ct_key_even; struct dvbcsa_bs_key_s *ct_key_odd; +#else + void *ct_keys; +#endif /* CSA */ int ct_cluster_size; uint8_t *ct_tsbcluster; + int ct_fill; +#if ENABLE_DVBCSA struct dvbcsa_bs_batch_s *ct_tsbbatch_even; struct dvbcsa_bs_batch_s *ct_tsbbatch_odd; - int ct_fill; int ct_fill_even; int ct_fill_odd; +#endif /* current sequence number */ uint16_t ct_seq; @@ -364,10 +375,14 @@ capmt_service_destroy(th_descrambler_t *td) LIST_REMOVE(ct, ct_link); +#if ENABLE_DVBCSA dvbcsa_bs_key_free(ct->ct_key_odd); dvbcsa_bs_key_free(ct->ct_key_even); free(ct->ct_tsbbatch_odd); free(ct->ct_tsbbatch_even); +#else + free_key_struct(ct->ct_keys); +#endif free(ct->ct_tsbcluster); free(ct); } @@ -506,9 +521,17 @@ handle_ca0(capmt_t* capmt) { continue; if (memcmp(even, invalid, 8)) +#if ENABLE_DVBCSA dvbcsa_bs_key_set(even, ct->ct_key_even); +#else + set_even_control_word(ct->ct_keys, even); +#endif if (memcmp(odd, invalid, 8)) +#if ENABLE_DVBCSA dvbcsa_bs_key_set(odd, ct->ct_key_odd); +#else + set_odd_control_word(ct->ct_keys, odd); +#endif if(ct->ct_keystate != CT_RESOLVED) tvhlog(LOG_INFO, "capmt", "Obtained key for service \"%s\"",t->s_svcname); @@ -842,6 +865,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, /** * */ +#if ENABLE_DVBCSA static int capmt_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, const uint8_t *tsb) @@ -926,6 +950,47 @@ capmt_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *s ct->ct_fill = 0; return 0; } +#else +static int +capmt_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, + const uint8_t *tsb) +{ + capmt_service_t *ct = (capmt_service_t *)td; + int r, i; + unsigned char *vec[3]; + uint8_t *t0; + + if(ct->ct_keystate == CT_FORBIDDEN) + return 1; + + if(ct->ct_keystate != CT_RESOLVED) + return -1; + + memcpy(ct->ct_tsbcluster + ct->ct_fill * 188, tsb, 188); + ct->ct_fill++; + + if(ct->ct_fill != ct->ct_cluster_size) + return 0; + + ct->ct_fill = 0; + + vec[0] = ct->ct_tsbcluster; + vec[1] = ct->ct_tsbcluster + ct->ct_cluster_size * 188; + vec[2] = NULL; + + while(1) { + t0 = vec[0]; + r = decrypt_packets(ct->ct_keys, vec); + if(r == 0) + break; + for(i = 0; i < r; i++) { + ts_recv_packet2(t, t0); + t0 += 188; + } + } + return 0; +} +#endif /** * Check if our CAID's matches, and if so, link @@ -952,13 +1017,19 @@ capmt_service_start(service_t *t) /* create new capmt service */ ct = calloc(1, sizeof(capmt_service_t)); +#if ENABLE_DVBCSA ct->ct_cluster_size = dvbcsa_bs_batch_size(); +#else + ct->ct_cluster_size = get_suggested_cluster_size(); +#endif ct->ct_tsbcluster = malloc(ct->ct_cluster_size * 188); + ct->ct_seq = capmt->capmt_seq++; +#if ENABLE_DVBCSA ct->ct_tsbbatch_even = malloc((ct->ct_cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s)); ct->ct_tsbbatch_odd = malloc((ct->ct_cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s)); - ct->ct_seq = capmt->capmt_seq++; +#endif TAILQ_FOREACH(st, &t->s_components, es_link) { caid_t *c; @@ -981,8 +1052,12 @@ capmt_service_start(service_t *t) } } +#if ENABLE_DVBCSA ct->ct_key_even = dvbcsa_bs_key_alloc(); ct->ct_key_odd = dvbcsa_bs_key_alloc(); +#else + ct->ct_keys = get_key_struct(); +#endif ct->ct_capmt = capmt; ct->ct_service = t; diff --git a/src/cwc.c b/src/cwc.c index 242478ca..b90cc8fe 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -27,18 +27,24 @@ #include #include #include +#include + #include "tvheadend.h" #include "tcp.h" #include "psi.h" #include "tsdemux.h" -#include #include "cwc.h" #include "notify.h" #include "atomic.h" #include "dtable.h" #include "subscriptions.h" #include "service.h" -#include + +#if ENABLE_DVBCSA +#include +#else +#include "ffdecsa/FFdecsa.h" +#endif /** * @@ -155,9 +161,12 @@ typedef struct cwc_service { CS_IDLE } cs_keystate; +#if ENABLE_DVBCSA struct dvbcsa_bs_key_s *cs_key_even; struct dvbcsa_bs_key_s *cs_key_odd; - +#else + void *cs_keys; +#endif uint8_t cs_cw[16]; int cs_pending_cw_update; @@ -167,11 +176,13 @@ typedef struct cwc_service { */ int cs_cluster_size; uint8_t *cs_tsbcluster; + int cs_fill; +#if ENABLE_DVBCSA struct dvbcsa_bs_batch_s *cs_tsbbatch_even; struct dvbcsa_bs_batch_s *cs_tsbbatch_odd; - int cs_fill; int cs_fill_even; int cs_fill_odd; +#endif LIST_HEAD(, ecm_pid) cs_pids; @@ -1889,13 +1900,21 @@ update_keys(cwc_service_t *ct) ct->cs_pending_cw_update = 0; for(i = 0; i < 8; i++) if(ct->cs_cw[i]) { +#if ENABLE_DVBCSA dvbcsa_bs_key_set(ct->cs_cw, ct->cs_key_even); +#else + set_even_control_word(ct->cs_keys, ct->cs_cw); +#endif break; } for(i = 0; i < 8; i++) if(ct->cs_cw[8 + i]) { +#if ENABLE_DVBCSA dvbcsa_bs_key_set(ct->cs_cw + 8, ct->cs_key_odd); +#else + set_odd_control_word(ct->cs_keys, ct->cs_cw + 8); +#endif break; } } @@ -1904,6 +1923,7 @@ update_keys(cwc_service_t *ct) /** * */ +#if ENABLE_DVBCSA static int cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, const uint8_t *tsb) @@ -1997,6 +2017,66 @@ cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, return 0; } +#else +static int +cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st, + const uint8_t *tsb) +{ + cwc_service_t *ct = (cwc_service_t *)td; + int r; + unsigned char *vec[3]; + + if(ct->cs_keystate == CS_FORBIDDEN) + return 1; + + if(ct->cs_keystate != CS_RESOLVED) + return -1; + + if(ct->cs_fill == 0 && ct->cs_pending_cw_update) + update_keys(ct); + + memcpy(ct->cs_tsbcluster + ct->cs_fill * 188, tsb, 188); + ct->cs_fill++; + + if(ct->cs_fill != ct->cs_cluster_size) + return 0; + + while(1) { + + vec[0] = ct->cs_tsbcluster; + vec[1] = ct->cs_tsbcluster + ct->cs_fill * 188; + vec[2] = NULL; + + r = decrypt_packets(ct->cs_keys, vec); + if(r > 0) { + int i; + const uint8_t *t0 = ct->cs_tsbcluster; + + for(i = 0; i < r; i++) { + ts_recv_packet2(t, t0); + t0 += 188; + } + + r = ct->cs_fill - r; + assert(r >= 0); + + if(r > 0) + memmove(ct->cs_tsbcluster, t0, r * 188); + ct->cs_fill = r; + + if(ct->cs_pending_cw_update && r > 0) + continue; + } else { + ct->cs_fill = 0; + } + break; + } + if(ct->cs_pending_cw_update) + update_keys(ct); + + return 0; +} +#endif /** * cwc_mutex is held @@ -2020,10 +2100,14 @@ cwc_service_destroy(th_descrambler_t *td) LIST_REMOVE(ct, cs_link); +#if ENABLE_DVBCSA dvbcsa_bs_key_free(ct->cs_key_odd); dvbcsa_bs_key_free(ct->cs_key_even); free(ct->cs_tsbbatch_odd); free(ct->cs_tsbbatch_even); +#else + free_key_struct(ct->cs_keys); +#endif free(ct->cs_tsbcluster); free(ct); } @@ -2067,19 +2151,26 @@ cwc_service_start(service_t *t) if(cwc_find_stream_by_caid(t, cwc->cwc_caid) == NULL) continue; - ct = calloc(1, sizeof(cwc_service_t)); - ct->cs_cluster_size = dvbcsa_bs_batch_size(); - ct->cs_tsbcluster = malloc(ct->cs_cluster_size * 188); + ct = calloc(1, sizeof(cwc_service_t)); +#if ENABLE_DVBCSA + ct->cs_cluster_size = dvbcsa_bs_batch_size(); +#else + ct->cs_cluster_size = get_suggested_cluster_size(); +#endif + ct->cs_tsbcluster = malloc(ct->cs_cluster_size * 188); +#if ENABLE_DVBCSA ct->cs_tsbbatch_even = malloc((ct->cs_cluster_size + 1) * - sizeof(struct dvbcsa_bs_batch_s)); - ct->cs_tsbbatch_odd = malloc((ct->cs_cluster_size + 1) * - sizeof(struct dvbcsa_bs_batch_s)); - - ct->cs_key_even = dvbcsa_bs_key_alloc(); - ct->cs_key_odd = dvbcsa_bs_key_alloc(); - ct->cs_cwc = cwc; - ct->cs_service = t; - ct->cs_okchannel = -3; + sizeof(struct dvbcsa_bs_batch_s)); + ct->cs_tsbbatch_odd = malloc((ct->cs_cluster_size + 1) * + sizeof(struct dvbcsa_bs_batch_s)); + ct->cs_key_even = dvbcsa_bs_key_alloc(); + ct->cs_key_odd = dvbcsa_bs_key_alloc(); +#else + ct->cs_keys = get_key_struct(); +#endif + ct->cs_cwc = cwc; + ct->cs_service = t; + ct->cs_okchannel = -1; td = &ct->cs_head; td->td_stop = cwc_service_destroy; diff --git a/src/main.c b/src/main.c index 7ee0e70f..acc757d8 100644 --- a/src/main.c +++ b/src/main.c @@ -468,6 +468,10 @@ main(int argc, char **argv) htsp_init(); +#if (!ENABLE_DVBCSA) + ffdecsa_init(); +#endif + if(rawts_input != NULL) rawts_init(rawts_input); From c1cb274c09a8d0c55e9263fc93abf1c45e35aa31 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 28 Nov 2012 19:46:27 +0000 Subject: [PATCH 130/503] [PR-54] fix mistake in merging of the old PR code. --- src/cwc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cwc.c b/src/cwc.c index b90cc8fe..79c07452 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -2170,7 +2170,7 @@ cwc_service_start(service_t *t) #endif ct->cs_cwc = cwc; ct->cs_service = t; - ct->cs_okchannel = -1; + ct->cs_okchannel = -3; td = &ct->cs_head; td->td_stop = cwc_service_destroy; From 8a48fe8214df98fc7cd7f23fb210971921d6282c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 29 Nov 2012 09:57:20 +0000 Subject: [PATCH 131/503] Issue #1407 - skip unused ISO 8859 control codes. This had the effect of placing unwanted spaces into various channel names where the broadcaster was sending these control chars. Thanks to Rene Herbrich for diagnosing this. --- src/dvb/dvb_support.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/dvb/dvb_support.c b/src/dvb/dvb_support.c index 64b35b2a..b4d5e3b0 100644 --- a/src/dvb/dvb_support.c +++ b/src/dvb/dvb_support.c @@ -95,10 +95,7 @@ static inline size_t conv_8859(int conv, (*dstlen)--; dst++; } else if (c <= 0x9f) { - // codes 0x80 - 0x9f (control codes) are mapped to ' ' - *dst = ' '; - (*dstlen)--; - dst++; + // codes 0x80 - 0x9f (control codes) are ignored } else { // map according to character table, skipping // unmapped chars (value 0 in the table) From 44628138d45b52a6bae7a973ba5fe3f81b5b0d40 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 29 Nov 2012 10:51:25 +0000 Subject: [PATCH 132/503] Issue #1407 - forgot to strip unused escapes from ISO 6937. --- src/dvb/dvb_support.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/dvb/dvb_support.c b/src/dvb/dvb_support.c index b4d5e3b0..6d883e00 100644 --- a/src/dvb/dvb_support.c +++ b/src/dvb/dvb_support.c @@ -132,10 +132,7 @@ static inline size_t conv_6937(const uint8_t *src, size_t srclen, (*dstlen)--; dst++; } else if (c <= 0x9f) { - // codes 0x80 - 0x9f (control codes) are mapped to ' ' - *dst = ' '; - (*dstlen)--; - dst++; + // codes 0x80 - 0x9f (control codes) are ignored } else { uint16_t uc; if (c >= 0xc0 && c <= 0xcf) { From 5d2197c30fe4774f25ba9404287436d51a215578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Thu, 29 Nov 2012 13:15:21 +0100 Subject: [PATCH 133/503] [PR-178] Close file pointer after the call to fb_size(fp) --- src/settings.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/settings.c b/src/settings.c index 3857a383..2f1ab077 100644 --- a/src/settings.c +++ b/src/settings.c @@ -195,11 +195,13 @@ hts_settings_load_one(const char *filename) mem = malloc(fb_size(fp)+1); n = fb_read(fp, mem, fb_size(fp)); if (n >= 0) mem[n] = 0; - fb_close(fp); /* Decode */ if(n == fb_size(fp)) r = htsmsg_json_deserialize(mem); + + /* Close */ + fb_close(fp); free(mem); return r; From aafd353d7af4f58215bec5ce507eb6a76fb34fc7 Mon Sep 17 00:00:00 2001 From: Vuolter Date: Fri, 30 Nov 2012 19:02:09 +0100 Subject: [PATCH 134/503] [PR-180] Fixed Sony CXD2820R identification for SNR enabling --- src/dvb/dvb_adapter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 67b88f10..145b5441 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -511,7 +511,7 @@ tda_add(int adapter_num) dvb_adapter_checkspeed(tda); - if(!strcmp(tda->tda_fe_info->name, "Sony CXD2820R (DVB-T/T2)")) + if(!strcmp(tda->tda_fe_info->name, "Sony CXD2820R")) tda->tda_snr_valid = 1; tvhlog(LOG_INFO, "dvb", From 563b8f8c51a4fa0ee33c0ae8786f3fec84f70b4d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 4 Dec 2012 20:13:16 +0000 Subject: [PATCH 135/503] Issue #1423 - ensure that XMLTV grabber search does not crash. It appears that if paths are duplicated in the PATH env variable the internal search algorithm failed to detect this and could crash due to an assert in the internal modlue registration code. --- src/epggrab/module/xmltv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 0352e4bb..9ac90bb3 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -677,6 +677,7 @@ static void _xmltv_load_grabbers ( void ) while ((de = readdir(dir))) { if (strstr(de->d_name, XMLTV_GRAB) != de->d_name) continue; snprintf(bin, sizeof(bin), "%s/%s", tmp, de->d_name); + if (epggrab_module_find_by_id(bin)) continue; if (stat(bin, &st)) continue; if (!(st.st_mode & S_IEXEC)) continue; if (!S_ISREG(st.st_mode)) continue; From 7f394752c0f0b0c9e886ddad72ed4fa79413fd71 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 4 Dec 2012 23:18:01 +0000 Subject: [PATCH 136/503] build: use AUTOBUILD_CONFIGURE_EXTRA in debian build rules. --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 9b6d1783..20a70098 100755 --- a/debian/rules +++ b/debian/rules @@ -5,7 +5,7 @@ export DH_VERBOSE=1 dh $@ override_dh_auto_configure: - dh_auto_configure -- ${JOBSARGS} + dh_auto_configure -- ${AUTOBUILD_CONFIGURE_EXTRA} ${JOBSARGS} override_dh_auto_build: make ${JARGS} From cf54890d14290011905ceb329344444eb38dbfe2 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Wed, 5 Dec 2012 08:51:53 +0100 Subject: [PATCH 137/503] Split recorder schedule view Now there are two separate categories: finished (and missed) recordings and upcoming recordings. --- src/dvr/dvr.h | 3 ++ src/dvr/dvr_db.c | 24 ++++++++++----- src/webui/extjs.c | 33 +++++++++++++++++++-- src/webui/static/app/dvr.js | 58 +++++++++++++++++++++++++------------ 4 files changed, 90 insertions(+), 28 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 327b7b02..e1935fbf 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -331,7 +331,10 @@ typedef struct dvr_query_result { int dqr_alloced; } dvr_query_result_t; +typedef int (dvr_entry_filter)(dvr_entry_t *entry); + void dvr_query(dvr_query_result_t *dqr); +void dvr_query_filter(dvr_query_result_t *dqr, dvr_entry_filter filter); void dvr_query_free(dvr_query_result_t *dqr); void dvr_query_sort(dvr_query_result_t *dqr); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index fe53120e..91165f55 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1299,6 +1299,22 @@ dvr_query_add_entry(dvr_query_result_t *dqr, dvr_entry_t *de) dqr->dqr_array[dqr->dqr_entries++] = de; } +void +dvr_query_filter(dvr_query_result_t *dqr, dvr_entry_filter filter) +{ + dvr_entry_t *de; + + memset(dqr, 0, sizeof(dvr_query_result_t)); + + LIST_FOREACH(de, &dvrentries, de_global_link) + if (filter(de)) + dvr_query_add_entry(dqr, de); +} + +static int all_filter(dvr_entry_t *entry) +{ + return 1; +} /** * @@ -1306,15 +1322,9 @@ dvr_query_add_entry(dvr_query_result_t *dqr, dvr_entry_t *de) void dvr_query(dvr_query_result_t *dqr) { - dvr_entry_t *de; - - memset(dqr, 0, sizeof(dvr_query_result_t)); - - LIST_FOREACH(de, &dvrentries, de_global_link) - dvr_query_add_entry(dqr, de); + return dvr_query_filter(dqr, all_filter); } - /** * */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 6355e236..85b70c74 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1287,7 +1287,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) * */ static int -extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque) +extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, dvr_entry_filter filter) { htsbuf_queue_t *hq = &hc->hc_reply; htsmsg_t *out, *array, *m; @@ -1317,7 +1317,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque) array = htsmsg_create_list(); - dvr_query(&dqr); + dvr_query_filter(&dqr, filter); dvr_query_sort(&dqr); @@ -1389,6 +1389,32 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque) return 0; } +static int is_dvr_entry_old(dvr_entry_t *entry) +{ + dvr_entry_sched_state_t state = entry->de_sched_state; + switch (state) { + case DVR_COMPLETED: return 1; + case DVR_MISSED_TIME: return 1; + default: return 0; + } +} + +static int is_dvr_entry_new(dvr_entry_t *entry) +{ + return !is_dvr_entry_old(entry); +} + +static int +extjs_dvrlist_old(http_connection_t *hc, const char *remain, void *opaque) +{ + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_old); +} + +static int +extjs_dvrlist_new(http_connection_t *hc, const char *remain, void *opaque) +{ + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_new); +} /** * @@ -1921,7 +1947,8 @@ extjs_start(void) http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE); http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE); http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE); - http_path_add("/dvrlist", NULL, extjs_dvrlist, ACCESS_WEB_INTERFACE); + http_path_add("/dvrlist_new", NULL, extjs_dvrlist_new, ACCESS_WEB_INTERFACE); + http_path_add("/dvrlist_old", NULL, extjs_dvrlist_old, ACCESS_WEB_INTERFACE); http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE); http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE); http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 72a5ed9e..6610ad1b 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -143,7 +143,7 @@ tvheadend.dvrDetails = function(entry) { /** * */ -tvheadend.dvrschedule = function() { +tvheadend.dvrschedule = function(title, dvrStore) { var actions = new Ext.ux.grid.RowActions({ header : '', @@ -358,9 +358,9 @@ tvheadend.dvrschedule = function() { loadMask : true, stripeRows : true, disableSelection : true, - title : 'Recorder schedule', + title : title, iconCls : 'clock', - store : tvheadend.dvrStore, + store : dvrStore, cm : dvrCm, plugins : [ actions ], viewConfig : { @@ -378,7 +378,7 @@ tvheadend.dvrschedule = function() { } } ], bbar : new Ext.PagingToolbar({ - store : tvheadend.dvrStore, + store : dvrStore, pageSize : 20, displayInfo : true, displayMsg : 'Programs {0} - {1} of {2}', @@ -570,7 +570,8 @@ tvheadend.autoreceditor = function() { */ tvheadend.dvr = function() { - tvheadend.dvrStore = new Ext.data.JsonStore({ + function datastoreBuilder(url) { + return new Ext.data.JsonStore({ root : 'entries', totalProperty : 'totalCount', fields : [ { @@ -610,29 +611,47 @@ tvheadend.dvr = function() { }, { name : 'url' } ], - url : 'dvrlist', + url : url, autoLoad : true, id : 'id', remoteSort : true - }); + }); + } + tvheadend.dvrStoreNew = datastoreBuilder('dvrlist_new'); + tvheadend.dvrStoreOld = datastoreBuilder('dvrlist_old'); + + + function updateDvrStore(store, r, m) { + r.data.status = m.status; + r.data.schedstate = m.schedstate; + + store.afterEdit(r); + store.fireEvent('updated', store, r, + Ext.data.Record.COMMIT); + } tvheadend.comet.on('dvrdb', function(m) { - if (m.reload != null) tvheadend.dvrStore.reload(); + if (m.reload != null) { + tvheadend.dvrStoreOld.reload(); + tvheadend.dvrStoreNew.reload(); + } if (m.updateEntry != null) { - r = tvheadend.dvrStore.getById(m.id) - if (typeof r === 'undefined') { - tvheadend.dvrStore.reload(); + r = tvheadend.dvrStoreNew.getById(m.id); + if (typeof r !== 'undefined') { + updateDvrStore(tvheadend.dvrStoreNew, r, m); return; } - r.data.status = m.status; - r.data.schedstate = m.schedstate; - - tvheadend.dvrStore.afterEdit(r); - tvheadend.dvrStore.fireEvent('updated', tvheadend.dvrStore, r, - Ext.data.Record.COMMIT); + r = tvheadend.dvrStoreOld.getById(m.id); + if (typeof r === 'undefined') { + updateDvrStore(tvheadend.dvrStoreOld, r, m); + return; + } + + tvheadend.dvrStoreNew.reload(); + tvheadend.dvrStoreOld.reload(); } }); @@ -661,7 +680,10 @@ tvheadend.dvr = function() { autoScroll : true, title : 'Digital Video Recorder', iconCls : 'drive', - items : [ new tvheadend.dvrschedule, new tvheadend.autoreceditor ] + items : [ new tvheadend.dvrschedule('Finished recordings', tvheadend.dvrStoreOld), + new tvheadend.dvrschedule('Recorder schedule', tvheadend.dvrStoreNew), + new tvheadend.autoreceditor + ] }); return panel; } From 982a8798e1bcb9e986fc329f90df1388ce3db458 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Wed, 5 Dec 2012 09:24:44 +0100 Subject: [PATCH 138/503] Add proper sort order for recording schedule --- src/dvr/dvr.h | 6 ++++++ src/dvr/dvr_db.c | 18 +++++++++++++----- src/webui/extjs.c | 9 +++++---- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index e1935fbf..604400a2 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -332,12 +332,18 @@ typedef struct dvr_query_result { } dvr_query_result_t; typedef int (dvr_entry_filter)(dvr_entry_t *entry); +typedef int (dvr_entry_comparator)(const void *a, const void *b); void dvr_query(dvr_query_result_t *dqr); void dvr_query_filter(dvr_query_result_t *dqr, dvr_entry_filter filter); void dvr_query_free(dvr_query_result_t *dqr); + +void dvr_query_sort_cmp(dvr_query_result_t *dqr, dvr_entry_comparator cmp); void dvr_query_sort(dvr_query_result_t *dqr); +int dvr_sort_start_descending(const void *A, const void *B); +int dvr_sort_start_ascending(const void *A, const void *B); + /** * */ diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 91165f55..88e871e5 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1337,7 +1337,7 @@ dvr_query_free(dvr_query_result_t *dqr) /** * Sorting functions */ -static int +int dvr_sort_start_descending(const void *A, const void *B) { dvr_entry_t *a = *(dvr_entry_t **)A; @@ -1345,22 +1345,30 @@ dvr_sort_start_descending(const void *A, const void *B) return b->de_start - a->de_start; } +int +dvr_sort_start_ascending(const void *A, const void *B) +{ + return -dvr_sort_start_descending(A, B); +} + /** * */ void -dvr_query_sort(dvr_query_result_t *dqr) +dvr_query_sort_cmp(dvr_query_result_t *dqr, dvr_entry_comparator sf) { - int (*sf)(const void *a, const void *b); - if(dqr->dqr_array == NULL) return; - sf = dvr_sort_start_descending; qsort(dqr->dqr_array, dqr->dqr_entries, sizeof(dvr_entry_t *), sf); } +void +dvr_query_sort(dvr_query_result_t *dqr) +{ + dvr_query_sort_cmp(dqr, dvr_sort_start_descending); +} /** * diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 85b70c74..63381eb5 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1287,7 +1287,8 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) * */ static int -extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, dvr_entry_filter filter) +extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, + dvr_entry_filter filter, dvr_entry_comparator cmp) { htsbuf_queue_t *hq = &hc->hc_reply; htsmsg_t *out, *array, *m; @@ -1319,7 +1320,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, dvr_entry dvr_query_filter(&dqr, filter); - dvr_query_sort(&dqr); + dvr_query_sort_cmp(&dqr, cmp); htsmsg_add_u32(out, "totalCount", dqr.dqr_entries); @@ -1407,13 +1408,13 @@ static int is_dvr_entry_new(dvr_entry_t *entry) static int extjs_dvrlist_old(http_connection_t *hc, const char *remain, void *opaque) { - return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_old); + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_old, dvr_sort_start_descending); } static int extjs_dvrlist_new(http_connection_t *hc, const char *remain, void *opaque) { - return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_new); + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_new, dvr_sort_start_ascending); } /** From d8d7a2f583b29925954b40c7f587d6e5f0e9262a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 1 Dec 2012 12:50:00 +0100 Subject: [PATCH 139/503] increment the pmt version on eatch reconfiguration of the passthrough muxer --- src/muxer_pass.c | 5 ++++- src/psi.c | 9 ++++++--- src/psi.h | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/muxer_pass.c b/src/muxer_pass.c index f6658ad5..933be24b 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -51,6 +51,7 @@ typedef struct pass_muxer { /* TS muxing */ uint8_t *pm_pat; uint8_t *pm_pmt; + uint16_t pm_pmt_version; uint32_t pm_ic; // Injection counter uint32_t pm_pc; // Packet counter } pass_muxer_t; @@ -128,11 +129,13 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) pm->pm_pmt[2] = 0x00 | (ss->ss_pmt_pid >> 0); pm->pm_pmt[3] = 0x10; pm->pm_pmt[4] = 0x00; - if(psi_build_pmt(ss, pm->pm_pmt+5, 183, ss->ss_pcr_pid) < 0) { + if(psi_build_pmt(ss, pm->pm_pmt+5, 183, pm->pm_pmt_version, + ss->ss_pcr_pid) < 0) { pm->m_errors++; tvhlog(LOG_ERR, "pass", "%s: Unable to build pmt", pm->pm_filename); return -1; } + pm->pm_pmt_version++; } return 0; diff --git a/src/psi.c b/src/psi.c index ff298fad..4d49ca71 100644 --- a/src/psi.c +++ b/src/psi.c @@ -710,7 +710,8 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, * PMT generator */ int -psi_build_pmt(const streaming_start_t *ss, uint8_t *buf0, int maxlen, int pcrpid) +psi_build_pmt(const streaming_start_t *ss, uint8_t *buf0, int maxlen, + int version, int pcrpid) { int c, tlen, dlen, l, i; uint8_t *buf, *buf1; @@ -726,8 +727,10 @@ psi_build_pmt(const streaming_start_t *ss, uint8_t *buf0, int maxlen, int pcrpid buf[4] = 0x01; buf[5] = 0xc1; /* current_next_indicator + version */ - buf[6] = 0; - buf[7] = 0; + buf[5] |= (version & 0x1F) << 1; + + buf[6] = 0; /* section number */ + buf[7] = 0; /* last section number */ buf[8] = 0xe0 | (pcrpid >> 8); buf[9] = pcrpid; diff --git a/src/psi.h b/src/psi.h index 270c4335..a3b099cf 100644 --- a/src/psi.h +++ b/src/psi.h @@ -45,7 +45,8 @@ int psi_parse_pmt(struct service *t, const uint8_t *ptr, int len, int chksvcid, int psi_build_pat(struct service *t, uint8_t *buf, int maxlen, int pmtpid); -int psi_build_pmt(const streaming_start_t *ss, uint8_t *buf, int maxlen, int pcrpid); +int psi_build_pmt(const streaming_start_t *ss, uint8_t *buf, int maxlen, + int version, int pcrpid); const char *psi_caid2name(uint16_t caid); From f22683e87061d015fde8a0651cb821e9558d5426 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 5 Dec 2012 20:46:18 +0000 Subject: [PATCH 140/503] htsp: add client version info in welcome message. --- src/htsp_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index d13b59de..c1ccc3b9 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -681,8 +681,8 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) tvh_str_update(&htsp->htsp_clientname, htsmsg_get_str(in, "clientname")); - tvhlog(LOG_INFO, "htsp", "%s: Welcomed client software: %s", - htsp->htsp_logname, name); + tvhlog(LOG_INFO, "htsp", "%s: Welcomed client software: %s (HTSPv%d)", + htsp->htsp_logname, name, v); htsmsg_add_u32(r, "htspversion", HTSP_PROTO_VERSION); htsmsg_add_str(r, "servername", "HTS Tvheadend"); From cf159f8322bf21e7fb1fc4974bfc79ab5ee6b576 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 6 Dec 2012 20:25:38 +0100 Subject: [PATCH 141/503] Split recorder schedule in three parts: upcoming, finished, failed --- src/webui/extjs.c | 77 +++++++++++++++++++++---------------- src/webui/static/app/dvr.js | 44 ++++++++++++--------- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 63381eb5..fd12e188 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1390,31 +1390,41 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, return 0; } -static int is_dvr_entry_old(dvr_entry_t *entry) +static int is_dvr_entry_finished(dvr_entry_t *entry) { dvr_entry_sched_state_t state = entry->de_sched_state; - switch (state) { - case DVR_COMPLETED: return 1; - case DVR_MISSED_TIME: return 1; - default: return 0; - } + return state == DVR_COMPLETED; } -static int is_dvr_entry_new(dvr_entry_t *entry) +static int is_dvr_entry_upcoming(dvr_entry_t *entry) { - return !is_dvr_entry_old(entry); + dvr_entry_sched_state_t state = entry->de_sched_state; + return state == DVR_RECORDING || state == DVR_SCHEDULED; +} + + +static int is_dvr_entry_failed(dvr_entry_t *entry) +{ + dvr_entry_sched_state_t state = entry->de_sched_state; + return state == DVR_MISSED_TIME || state == DVR_NOSTATE; } static int -extjs_dvrlist_old(http_connection_t *hc, const char *remain, void *opaque) +extjs_dvrlist_finished(http_connection_t *hc, const char *remain, void *opaque) { - return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_old, dvr_sort_start_descending); + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_finished, dvr_sort_start_descending); } static int -extjs_dvrlist_new(http_connection_t *hc, const char *remain, void *opaque) +extjs_dvrlist_upcoming(http_connection_t *hc, const char *remain, void *opaque) { - return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_new, dvr_sort_start_ascending); + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_upcoming, dvr_sort_start_ascending); +} + +static int +extjs_dvrlist_failed(http_connection_t *hc, const char *remain, void *opaque) +{ + return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_failed, dvr_sort_start_descending); } /** @@ -1937,27 +1947,28 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) void extjs_start(void) { - http_path_add("/about.html", NULL, page_about, ACCESS_WEB_INTERFACE); - http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE); - http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE); - http_path_add("/channels", NULL, extjs_channels, ACCESS_WEB_INTERFACE); - http_path_add("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE); - http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE); - http_path_add("/confignames", NULL, extjs_confignames, ACCESS_WEB_INTERFACE); - http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE); - http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE); - http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE); - http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE); - http_path_add("/dvrlist_new", NULL, extjs_dvrlist_new, ACCESS_WEB_INTERFACE); - http_path_add("/dvrlist_old", NULL, extjs_dvrlist_old, ACCESS_WEB_INTERFACE); - http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE); - http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE); - http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE); - http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE); - http_path_add("/mergechannel", NULL, extjs_mergechannel, ACCESS_ADMIN); - http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN); - http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN); - http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN); + http_path_add("/about.html", NULL, page_about, ACCESS_WEB_INTERFACE); + http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE); + http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE); + http_path_add("/channels", NULL, extjs_channels, ACCESS_WEB_INTERFACE); + http_path_add("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE); + http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE); + http_path_add("/confignames", NULL, extjs_confignames, ACCESS_WEB_INTERFACE); + http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE); + http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE); + http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE); + http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE); + http_path_add("/dvrlist_upcoming", NULL, extjs_dvrlist_upcoming, ACCESS_WEB_INTERFACE); + http_path_add("/dvrlist_finished", NULL, extjs_dvrlist_finished, ACCESS_WEB_INTERFACE); + http_path_add("/dvrlist_failed", NULL, extjs_dvrlist_failed, ACCESS_WEB_INTERFACE); + http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE); + http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE); + http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE); + http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE); + http_path_add("/mergechannel", NULL, extjs_mergechannel, ACCESS_ADMIN); + http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN); + http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN); + http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN); #if ENABLE_LINUXDVB extjs_start_dvb(); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 6610ad1b..61cea984 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -617,8 +617,12 @@ tvheadend.dvr = function() { remoteSort : true }); } - tvheadend.dvrStoreNew = datastoreBuilder('dvrlist_new'); - tvheadend.dvrStoreOld = datastoreBuilder('dvrlist_old'); + tvheadend.dvrStoreUpcoming = datastoreBuilder('dvrlist_upcoming'); + tvheadend.dvrStoreFinished = datastoreBuilder('dvrlist_finished'); + tvheadend.dvrStoreFailed = datastoreBuilder('dvrlist_failed'); + tvheadend.dvrStores = [tvheadend.dvrStoreUpcoming, + tvheadend.dvrStoreFinished, + tvheadend.dvrStoreFailed]; function updateDvrStore(store, r, m) { @@ -630,28 +634,28 @@ tvheadend.dvr = function() { Ext.data.Record.COMMIT); } + function reloadStores() { + for (var i = 0; i < tvheadend.dvrStores.length; i++) { + tvheadend.dvrStores[i].reload(); + } + } + tvheadend.comet.on('dvrdb', function(m) { if (m.reload != null) { - tvheadend.dvrStoreOld.reload(); - tvheadend.dvrStoreNew.reload(); + reloadStores(); } if (m.updateEntry != null) { - r = tvheadend.dvrStoreNew.getById(m.id); - if (typeof r !== 'undefined') { - updateDvrStore(tvheadend.dvrStoreNew, r, m); - return; + for (var i = 0; i < tvheadend.dvrStores.length; i++) { + var store = tvheadend.dvrStores[i]; + r = tvheadend.dvrStoreUpcoming.getById(m.id); + if (typeof r !== 'undefined') { + updateDvrStore(store, r, m); + return; + } } - - r = tvheadend.dvrStoreOld.getById(m.id); - if (typeof r === 'undefined') { - updateDvrStore(tvheadend.dvrStoreOld, r, m); - return; - } - - tvheadend.dvrStoreNew.reload(); - tvheadend.dvrStoreOld.reload(); + reloadStores(); } }); @@ -680,8 +684,10 @@ tvheadend.dvr = function() { autoScroll : true, title : 'Digital Video Recorder', iconCls : 'drive', - items : [ new tvheadend.dvrschedule('Finished recordings', tvheadend.dvrStoreOld), - new tvheadend.dvrschedule('Recorder schedule', tvheadend.dvrStoreNew), + items : [ + new tvheadend.dvrschedule('Upcoming recordings', tvheadend.dvrStoreUpcoming), + new tvheadend.dvrschedule('Finished recordings', tvheadend.dvrStoreFinished), + new tvheadend.dvrschedule('Failed recordings', tvheadend.dvrStoreFailed), new tvheadend.autoreceditor ] }); From 663e1d618dbe59bebb0be139a9df818a6a86a510 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 6 Dec 2012 20:30:23 +0100 Subject: [PATCH 142/503] Differentiate recordings by icon --- src/webui/static/app/dvr.js | 10 +++++----- src/webui/static/app/ext.css | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 61cea984..80be21a6 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -143,7 +143,7 @@ tvheadend.dvrDetails = function(entry) { /** * */ -tvheadend.dvrschedule = function(title, dvrStore) { +tvheadend.dvrschedule = function(title, iconCls, dvrStore) { var actions = new Ext.ux.grid.RowActions({ header : '', @@ -359,7 +359,7 @@ tvheadend.dvrschedule = function(title, dvrStore) { stripeRows : true, disableSelection : true, title : title, - iconCls : 'clock', + iconCls : iconCls, store : dvrStore, cm : dvrCm, plugins : [ actions ], @@ -685,9 +685,9 @@ tvheadend.dvr = function() { title : 'Digital Video Recorder', iconCls : 'drive', items : [ - new tvheadend.dvrschedule('Upcoming recordings', tvheadend.dvrStoreUpcoming), - new tvheadend.dvrschedule('Finished recordings', tvheadend.dvrStoreFinished), - new tvheadend.dvrschedule('Failed recordings', tvheadend.dvrStoreFailed), + new tvheadend.dvrschedule('Upcoming recordings', 'clock', tvheadend.dvrStoreUpcoming), + new tvheadend.dvrschedule('Finished recordings', 'television', tvheadend.dvrStoreFinished), + new tvheadend.dvrschedule('Failed recordings', 'exclamation', tvheadend.dvrStoreFailed), new tvheadend.autoreceditor ] }); diff --git a/src/webui/static/app/ext.css b/src/webui/static/app/ext.css index a256b6aa..8296e46c 100644 --- a/src/webui/static/app/ext.css +++ b/src/webui/static/app/ext.css @@ -224,6 +224,10 @@ background-image: url(../icons/clock.png) !important; } +.exclamation { + background-image: url(../icons/exclamation.png) !important; +} + .wrench { background-image: url(../icons/wrench.png) !important; } From 5da648f44773701528f86d2a507b6d8dec698e96 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 6 Dec 2012 19:47:32 +0000 Subject: [PATCH 143/503] Fix mistake in CXD2820R check. --- src/dvb/dvb_adapter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 145b5441..ca8729e2 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#define _GNU_SOURCE #include #include @@ -511,7 +512,7 @@ tda_add(int adapter_num) dvb_adapter_checkspeed(tda); - if(!strcmp(tda->tda_fe_info->name, "Sony CXD2820R")) + if(strcasestr(tda->tda_fe_info->name, "Sony CXD2820R")) tda->tda_snr_valid = 1; tvhlog(LOG_INFO, "dvb", From 3a54bc7f16ab1b9dc4b690c5f49da93db53b845f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 9 Dec 2012 22:47:41 +0000 Subject: [PATCH 144/503] dvr: ensure scheduled recordings are linked to autorec rule. --- src/dvr/dvr_db.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 88e871e5..8560fbc0 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1034,9 +1034,8 @@ dvr_init(void) } } - dvr_db_load(); - dvr_autorec_init(); + dvr_db_load(); } /** From 86fac417c20734383e904e38169f1c88b42a6da9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 13 Dec 2012 20:57:09 +0000 Subject: [PATCH 145/503] Issue #1454 - Remove usage of non re-entrant strtok() Thanks to Jaroslav Kysela for providing the initial fix. --- src/epggrab/module/xmltv.c | 6 +++--- src/filebundle.c | 7 ++++--- src/spawn.c | 6 +++--- src/webui/extjs.c | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 9ac90bb3..c6f1006e 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -634,7 +634,7 @@ static void _xmltv_load_grabbers ( void ) size_t i, p, n; char *outbuf; char name[1000]; - char *tmp, *path; + char *tmp, *tmp2, *path; /* Load data */ outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf); @@ -668,7 +668,7 @@ static void _xmltv_load_grabbers ( void ) NULL }; path = strdup(tmp); - tmp = strtok(path, ":"); + tmp = strtok_r(path, ":", &tmp2); while (tmp) { DIR *dir; struct dirent *de; @@ -691,7 +691,7 @@ static void _xmltv_load_grabbers ( void ) } closedir(dir); } - tmp = strtok(NULL, ":"); + tmp = strtok_r(NULL, ":", &tmp2); } free(path); } diff --git a/src/filebundle.c b/src/filebundle.c index 82450a37..15a4d4c5 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -214,12 +214,13 @@ fb_dir *fb_opendir ( const char *path ) /* Bundle */ #if ENABLE_BUNDLE - char *tmp1 = strdup(path); - char *tmp2 = strtok(tmp1, "/"); + char *tmp1, *tmp2, *tmp3; + *tmp1 = strdup(path); + *tmp2 = strtok_r(tmp1, "/", &tmp3); filebundle_entry_t *fb = filebundle_root; while (fb && tmp2) { if (fb->type == FB_DIR && !strcmp(fb->name, tmp2)) { - tmp2 = strtok(NULL, "/"); + tmp2 = strtok_r(NULL, "/", &tmp3); if (tmp2) fb = fb->d.child; } else { fb = fb->next; diff --git a/src/spawn.c b/src/spawn.c index 1ae74891..0cee4bdd 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -53,13 +53,13 @@ find_exec ( const char *name, char *out, size_t len ) { int ret = 0; char bin[512]; - char *path, *tmp; + char *path, *tmp, *tmp2; DIR *dir; struct dirent *de; struct stat st; if (!(path = getenv("PATH"))) return 0; path = strdup(path); - tmp = strtok(path, ":"); + tmp = strtok_r(path, ":", &tmp2); while (tmp && !ret) { if ((dir = opendir(tmp))) { while ((de = readdir(dir))) { @@ -73,7 +73,7 @@ find_exec ( const char *name, char *out, size_t len ) } closedir(dir); } - tmp = strtok(NULL, ":"); + tmp = strtok_r(NULL, ":", &tmp2); } free(path); return ret; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index fd12e188..e27935af 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -352,7 +352,7 @@ extjs_channels_update(htsmsg_t *in) if((s = htsmsg_get_str(c, "epggrabsrc")) != NULL) { char *tmp = strdup(s); - char *sptr = NULL; + char *sptr, *sptr2; char *modecid = strtok_r(tmp, ",", &sptr); char *modid, *ecid; epggrab_module_t *mod; @@ -377,8 +377,8 @@ extjs_channels_update(htsmsg_t *in) /* Add new */ while (modecid) { - modid = strtok(modecid, "|"); - ecid = strtok(NULL, "|"); + modid = strtok_r(modecid, "|", &sptr2); + ecid = strtok_r(NULL, "|", &sptr2); modecid = strtok_r(NULL, ",", &sptr); if (!(mod = epggrab_module_find_by_id(modid))) From 4419dde14b4b98a9ee39d5b47b7fc9ca2f6a79d7 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 12 Dec 2012 14:45:27 +0100 Subject: [PATCH 146/503] Issue #1453 - Fix autorec init issue (duplicate entries) With previous init change, the scheduled events may be duplicated. Split the dvr_autorec_init() to two phases: 1) load the autorec list 2) update the scheduled events after dvr db is initialized --- src/dvr/dvr.h | 2 ++ src/dvr/dvr_autorec.c | 16 +++++++++++++++- src/dvr/dvr_db.c | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 604400a2..7046362b 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -280,6 +280,8 @@ void dvr_init(void); void dvr_autorec_init(void); +void dvr_autorec_update(void); + void dvr_destroy_by_channel(channel_t *ch); void dvr_rec_subscribe(dvr_entry_t *de); diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 4eff0435..defb0e39 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -38,6 +38,8 @@ dtable_t *autorec_dt; TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry); +static int dvr_autorec_in_init = 0; + struct dvr_autorec_entry_queue autorec_entries; static void dvr_autorec_changed(dvr_autorec_entry_t *dae); @@ -422,7 +424,8 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values, if (dae->dae_serieslink) dae->dae_serieslink->getref(dae->dae_serieslink); } - dvr_autorec_changed(dae); + if (!dvr_autorec_in_init) + dvr_autorec_changed(dae); return autorec_record_build(dae); } @@ -465,7 +468,18 @@ dvr_autorec_init(void) { TAILQ_INIT(&autorec_entries); autorec_dt = dtable_create(&autorec_dtc, "autorec", NULL); + dvr_autorec_in_init = 1; dtable_load(autorec_dt); + dvr_autorec_in_init = 0; +} + +void +dvr_autorec_update(void) +{ + dvr_autorec_entry_t *dae; + TAILQ_FOREACH(dae, &autorec_entries, dae_link) { + dvr_autorec_changed(dae); + } } static void diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 8560fbc0..70bdabcc 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1036,6 +1036,7 @@ dvr_init(void) dvr_autorec_init(); dvr_db_load(); + dvr_autorec_update(); } /** From 7d97d1f648ed720785eac367481d8e56ea8f1ff8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 13 Dec 2012 21:29:22 +0000 Subject: [PATCH 147/503] Issue #1454 - init strtok_r() state ptrs, older gcc whinges. --- src/epggrab/module/xmltv.c | 2 +- src/filebundle.c | 2 +- src/spawn.c | 2 +- src/webui/extjs.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index c6f1006e..d8616f9a 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -634,7 +634,7 @@ static void _xmltv_load_grabbers ( void ) size_t i, p, n; char *outbuf; char name[1000]; - char *tmp, *tmp2, *path; + char *tmp, *tmp2 = NULL, *path; /* Load data */ outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf); diff --git a/src/filebundle.c b/src/filebundle.c index 15a4d4c5..a7db77b0 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -214,7 +214,7 @@ fb_dir *fb_opendir ( const char *path ) /* Bundle */ #if ENABLE_BUNDLE - char *tmp1, *tmp2, *tmp3; + char *tmp1, *tmp2, *tmp3 = NULL; *tmp1 = strdup(path); *tmp2 = strtok_r(tmp1, "/", &tmp3); filebundle_entry_t *fb = filebundle_root; diff --git a/src/spawn.c b/src/spawn.c index 0cee4bdd..534e5af7 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -53,7 +53,7 @@ find_exec ( const char *name, char *out, size_t len ) { int ret = 0; char bin[512]; - char *path, *tmp, *tmp2; + char *path, *tmp, *tmp2 = NULL; DIR *dir; struct dirent *de; struct stat st; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index e27935af..efb79c80 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -352,7 +352,7 @@ extjs_channels_update(htsmsg_t *in) if((s = htsmsg_get_str(c, "epggrabsrc")) != NULL) { char *tmp = strdup(s); - char *sptr, *sptr2; + char *sptr = NULL, *sptr2 = NULL; char *modecid = strtok_r(tmp, ",", &sptr); char *modid, *ecid; epggrab_module_t *mod; From a420c83a0e0d2c31c2c15d0fec6fedc3f5a36dfe Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 14 Dec 2012 09:37:29 +0000 Subject: [PATCH 148/503] Issue #1454 - correct stupid typo when changing code layout in filebundle. --- src/filebundle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filebundle.c b/src/filebundle.c index a7db77b0..77e335e7 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -215,8 +215,8 @@ fb_dir *fb_opendir ( const char *path ) /* Bundle */ #if ENABLE_BUNDLE char *tmp1, *tmp2, *tmp3 = NULL; - *tmp1 = strdup(path); - *tmp2 = strtok_r(tmp1, "/", &tmp3); + tmp1 = strdup(path); + tmp2 = strtok_r(tmp1, "/", &tmp3); filebundle_entry_t *fb = filebundle_root; while (fb && tmp2) { if (fb->type == FB_DIR && !strcmp(fb->name, tmp2)) { From b1d20d3255e8a2e386487d17f55e25d3c5ce1a76 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 20:04:56 +0000 Subject: [PATCH 149/503] Issue #1458 - fix unint variable. --- src/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index 0ed2181d..47b2cff2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -380,7 +380,7 @@ makedirs ( const char *inpath, int mode ) int rmtree ( const char *path ) { - int err; + int err = 0; struct dirent de, *der; struct stat st; char buf[512]; From 87f6f582157e25ff0a32b7aca9364acd483ce771 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 20:46:35 +0000 Subject: [PATCH 150/503] Issue #1403 - ensure adapter config is properly loaded. --- src/dvb/dvb_adapter.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index ca8729e2..21b9cab6 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -482,10 +482,7 @@ tda_add(int adapter_num) free(tda); return; } - if (tda->tda_idlescan || !tda->tda_idleclose) - tda->tda_fe_fd = fe; - else - close(fe); + tda->tda_fe_fd = fe; tda->tda_type = tda->tda_fe_info->type; @@ -709,6 +706,10 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { tda_init_input(tda); + if (tda->tda_idlescan || !tda->tda_idleclose) { + close(tda->tda_fe_fd); + tda->tda_fe_fd = -1; + } if(tda->tda_sat) dvb_satconf_init(tda); From 99b69cb86059ffb8d3f243aed8ed677b65034b5a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 21:21:05 +0000 Subject: [PATCH 151/503] Add new capabilities field to determine what optional features are included. --- src/htsp_server.c | 5 +++++ src/main.c | 13 +++++++++++++ src/tvheadend.h | 1 + 3 files changed, 19 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index c1ccc3b9..b84768e0 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -672,6 +672,7 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) htsmsg_t *l, *r = htsmsg_create_map(); uint32_t v; const char *name; + int i = 0; if(htsmsg_get_u32(in, "htspversion", &v)) return htsp_error("Missing argument 'htspversion'"); @@ -691,6 +692,10 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) /* Capabilities */ l = htsmsg_create_list(); + while (tvheadend_capabilities[i]) { + htsmsg_add_str(l, NULL, tvheadend_capabilities[i]); + i++; + } htsmsg_add_msg(r, "servercapability", l); /* Set version to lowest num */ diff --git a/src/main.c b/src/main.c index acc757d8..43459ad7 100644 --- a/src/main.c +++ b/src/main.c @@ -78,6 +78,19 @@ int htsp_port; int htsp_port_extra; char *tvheadend_cwd; +const char *tvheadend_capabilities[] = { +#if ENABLE_CWC + "cwc", +#endif +#if ENABLE_V4L + "v4l", +#endif +#if ENABLE_LINUXDVB + "linuxdvb", +#endif + NULL +}; + static void handle_sigpipe(int x) { diff --git a/src/tvheadend.h b/src/tvheadend.h index 0ceb242c..eb317b9c 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -36,6 +36,7 @@ extern const char *tvheadend_version; extern char *tvheadend_cwd; +extern const char *tvheadend_capabilities[]; #define PTS_UNSET INT64_C(0x8000000000000000) From 496808bbd496cd013f69e53bb9131f7d312dbb02 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 22:17:18 +0000 Subject: [PATCH 152/503] webui: add some processing of capabilities for dynamic tab inclusion. --- src/webui/extjs.c | 21 +++++++++++++++++++ src/webui/static/app/tvheadend.js | 35 ++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index efb79c80..059417c0 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1941,6 +1941,26 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) return 0; } +/** + * Capability check + */ +static int +extjs_capabilities(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + htsmsg_t *l; + int i = 0; + l = htsmsg_create_list(); + while (tvheadend_capabilities[i]) { + htsmsg_add_str(l, NULL, tvheadend_capabilities[i]); + i++; + } + htsmsg_json_serialize(l, hq, 0); + htsmsg_destroy(l); + http_output_content(hc, "text/x-json; charset=UTF-8"); + return 0; +} + /** * WEB user interface */ @@ -1949,6 +1969,7 @@ extjs_start(void) { http_path_add("/about.html", NULL, page_about, ACCESS_WEB_INTERFACE); http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE); + http_path_add("/capabilities", NULL, extjs_capabilities, ACCESS_WEB_INTERFACE); http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE); http_path_add("/channels", NULL, extjs_channels, ACCESS_WEB_INTERFACE); http_path_add("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE); diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 3b44f6ca..2a1af2b7 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -1,3 +1,6 @@ +tvheadend.accessupdate = null; +tvheadend.capabilties = null; + /** * Displays a help popup window */ @@ -27,6 +30,21 @@ tvheadend.help = function(title, pagename) { }); } +/* + * General capabilities + */ +Ext.Ajax.request({ + url: '/capabilities', + success: function(d) + { + if (d && d.responseText) + tvheadend.capabilities = Ext.util.JSON.decode(d.responseText); + if (tvheadend.capabilities && tvheadend.accessupdate) + accessUpdate(tvheadend.accessUpdate); + + } +}); + /** * Displays a mediaplayer using VLC plugin */ @@ -223,6 +241,7 @@ tvheadend.VLC = function(url) { * Obviosuly, access is verified in the server too. */ function accessUpdate(o) { + tvheadend.accessUpdate = o; if (o.dvr == true && tvheadend.dvrpanel == null) { tvheadend.dvrpanel = new tvheadend.dvr; @@ -237,12 +256,22 @@ function accessUpdate(o) { iconCls : 'wrench', items : [ new tvheadend.miscconf, new tvheadend.chconf, new tvheadend.epggrab, new tvheadend.cteditor, - new tvheadend.dvrsettings, new tvheadend.tvadapters, - new tvheadend.iptv, new tvheadend.acleditor, - new tvheadend.cwceditor, new tvheadend.capmteditor ] + new tvheadend.dvrsettings, + new tvheadend.iptv, new tvheadend.acleditor ] }); tvheadend.rootTabPanel.add(tvheadend.confpanel); } + if (tvheadend.capabilities && tvheadend.confpanel) { + if (tvheadend.capabilities.indexOf('linuxdvb') != -1 || + tvheadend.capabilities.indexOf('v4l') != -1) { + tvheadend.confpanel.add(new tvheadend.tvadapters); + } + if (tvheadend.capabilities.indexOf('cwc') != -1) { + tvheadend.confpanel.add(new tvheadend.cwceditor); + tvheadend.confpanel.add(new tvheadend.capmteditor); + } + tvheadend.confpanel.doLayout(); + } if (o.admin == true && tvheadend.statuspanel == null) { tvheadend.statuspanel = new tvheadend.status; From d9dae65bbb77534bf283d4c4487ae051343d3caa Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 21:32:55 +0000 Subject: [PATCH 153/503] Issue #1446 - fix --disable-cwc checks. --- Makefile | 2 ++ src/dvb/dvb_tables.c | 2 ++ src/main.c | 10 +++++----- src/service.c | 2 ++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 8c6b2936..6e4edae0 100644 --- a/Makefile +++ b/Makefile @@ -177,8 +177,10 @@ SRCS-${CONFIG_CWC} += src/cwc.c \ ifneq ($(CONFIG_DVBCSA),yes) SRCS-${CONFIG_CWC} += src/ffdecsa/ffdecsa_interface.c \ src/ffdecsa/ffdecsa_int.c +ifeq ($(CONFIG_CWC),yes) SRCS-${CONFIG_MMX} += src/ffdecsa/ffdecsa_mmx.c SRCS-${CONFIG_SSE2} += src/ffdecsa/ffdecsa_sse2.c +endif ${BUILDDIR}/src/ffdecsa/ffdecsa_mmx.o : CFLAGS += -mmmx ${BUILDDIR}/src/ffdecsa/ffdecsa_sse2.o : CFLAGS += -msse2 endif diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 529f5c50..7396e6e3 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -534,7 +534,9 @@ static int dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { +#if ENABLE_CWC cwc_emm(ptr, len, (uintptr_t)opaque, (void *)tdmi); +#endif return 0; } diff --git a/src/main.c b/src/main.c index 43459ad7..0a6a199c 100644 --- a/src/main.c +++ b/src/main.c @@ -470,9 +470,13 @@ main(int argc, char **argv) serviceprobe_init(); +#if ENABLE_CWC cwc_init(); - capmt_init(); +#if (!ENABLE_DVBCSA) + ffdecsa_init(); +#endif +#endif epggrab_init(); epg_init(); @@ -481,10 +485,6 @@ main(int argc, char **argv) htsp_init(); -#if (!ENABLE_DVBCSA) - ffdecsa_init(); -#endif - if(rawts_input != NULL) rawts_init(rawts_input); diff --git a/src/service.c b/src/service.c index e189ae1d..c745527a 100644 --- a/src/service.c +++ b/src/service.c @@ -208,8 +208,10 @@ service_start(service_t *t, unsigned int weight, int force_start) if((r = t->s_start_feed(t, weight, force_start))) return r; +#if ENABLE_CWC cwc_service_start(t); capmt_service_start(t); +#endif pthread_mutex_lock(&t->s_stream_mutex); From b624851dde9bdc4b78ff3d72cf4e172ba203f5ce Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 19 Dec 2012 22:27:04 +0000 Subject: [PATCH 154/503] Issue #1446 - fix --disable-linuxdvb. --- Makefile | 5 +++-- src/capmt.c | 4 +++- src/epggrab.c | 8 ++++++++ src/main.c | 13 +++++++++---- src/webui/statedump.c | 1 + src/webui/webui.c | 8 ++++++++ 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 6e4edae0..3d8fab24 100644 --- a/Makefile +++ b/Makefile @@ -106,16 +106,16 @@ SRCS = src/main.c \ src/avc.c \ src/huffman.c \ src/filebundle.c \ - src/muxes.c \ src/config2.c \ src/lang_codes.c \ src/lang_str.c \ SRCS += src/epggrab/module.c\ src/epggrab/channel.c\ - src/epggrab/otamux.c\ src/epggrab/module/pyepg.c\ src/epggrab/module/xmltv.c\ + +SRCS-$(CONFIG_LINUXDVB) += src/epggrab/otamux.c\ src/epggrab/module/eit.c \ src/epggrab/module/opentv.c \ src/epggrab/support/freesat_huffman.c \ @@ -160,6 +160,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/dvb/dvb_input_filtered.c \ src/dvb/dvb_input_raw.c \ src/webui/extjs_dvb.c \ + src/muxes.c \ # V4L SRCS-${CONFIG_V4L} += \ diff --git a/src/capmt.c b/src/capmt.c index f10778b3..6da676cd 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -572,7 +572,6 @@ capmt_thread(void *aux) capmt_t *capmt = aux; struct timespec ts; int d, i, bind_ok = 0; - th_dvb_adapter_t *tda; while (capmt->capmt_running) { for (i = 0; i < MAX_CA; i++) @@ -605,6 +604,8 @@ capmt_thread(void *aux) if (!capmt->capmt_oscam) { bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[0], capmt->capmt_port); } else { +#if ENABLE_LINUXDVB + th_dvb_adapter_t *tda; TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { if (tda->tda_rootpath) { //if rootpath is NULL then can't rely on tda_adapter_num because it is always 0 if (tda->tda_adapter_num > MAX_CA) { @@ -615,6 +616,7 @@ capmt_thread(void *aux) bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[tda->tda_adapter_num], 9000 + tda->tda_adapter_num); } } +#endif } if (bind_ok) handle_ca0(capmt); diff --git a/src/epggrab.c b/src/epggrab.c index 9d0dcb0f..aa085a07 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -213,8 +213,10 @@ static void _epggrab_load ( void ) } /* Load module config (channels) */ +#if ENABLE_LINUXDVB eit_load(); opentv_load(); +#endif pyepg_load(); xmltv_load(); } @@ -343,21 +345,27 @@ void epggrab_resched ( void ) void epggrab_init ( void ) { /* Lists */ +#if ENABLE_LINUXDVB extern TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all; TAILQ_INIT(&ota_mux_all); +#endif pthread_mutex_init(&epggrab_mutex, NULL); pthread_cond_init(&epggrab_cond, NULL); /* Initialise modules */ +#if ENABLE_LINUXDVB eit_init(); opentv_init(); +#endif pyepg_init(); xmltv_init(); /* Load config */ _epggrab_load(); +#if ENABLE_LINUXDVB epggrab_ota_load(); +#endif /* Start internal grab thread */ pthread_t tid; diff --git a/src/main.c b/src/main.c index 0a6a199c..564e592c 100644 --- a/src/main.c +++ b/src/main.c @@ -275,7 +275,9 @@ main(int argc, char **argv) sigset_t set; const char *homedir; const char *rawts_input = NULL; +#if ENABLE_LINUXDVB const char *dvb_rawts_input = NULL; +#endif const char *join_transport = NULL; const char *confpath = NULL; char *p, *endp; @@ -356,9 +358,11 @@ main(int argc, char **argv) case 'r': rawts_input = optarg; break; +#if ENABLE_LINUXDVB case 'R': dvb_rawts_input = optarg; break; +#endif case 'j': join_transport = optarg; break; @@ -446,8 +450,6 @@ main(int argc, char **argv) config_init(); - muxes_init(); - service_init(); channels_init(); @@ -456,16 +458,19 @@ main(int argc, char **argv) access_init(createdefault); - tcp_server_init(); #if ENABLE_LINUXDVB + muxes_init(); dvb_init(adapter_mask, dvb_rawts_input); #endif + iptv_input_init(); + #if ENABLE_V4L v4l_init(); #endif - http_server_init(); + tcp_server_init(); + http_server_init(); webui_init(); serviceprobe_init(); diff --git a/src/webui/statedump.c b/src/webui/statedump.c index a9758e6f..603fd158 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -29,6 +29,7 @@ #include "access.h" #include "epg.h" #include "psi.h" +#include "channels.h" #if ENABLE_LINUXDVB #include "dvr/dvr.h" #include "dvb/dvb.h" diff --git a/src/webui/webui.c b/src/webui/webui.c index 6e5ab39c..170127cf 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -597,6 +597,7 @@ http_stream_service(http_connection_t *hc, service_t *service) /** * Subscribes to a service and starts the streaming loop */ +#if ENABLE_LINUXDVB static int http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) { @@ -616,6 +617,7 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) return 0; } +#endif /** @@ -698,7 +700,9 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) char *components[2]; channel_t *ch = NULL; service_t *service = NULL; +#if ENABLE_LINUXDVB th_dvb_mux_instance_t *tdmi = NULL; +#endif hc->hc_keep_alive = 0; @@ -722,16 +726,20 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) ch = channel_find_by_name(components[1], 0, 0); } else if(!strcmp(components[0], "service")) { service = service_find_by_identifier(components[1]); +#if ENABLE_LINUXDVB } else if(!strcmp(components[0], "mux")) { tdmi = dvb_mux_find_by_identifier(components[1]); +#endif } if(ch != NULL) { return http_stream_channel(hc, ch); } else if(service != NULL) { return http_stream_service(hc, service); +#if ENABLE_LINUXDVB } else if(tdmi != NULL) { return http_stream_tdmi(hc, tdmi); +#endif } else { http_error(hc, HTTP_STATUS_BAD_REQUEST); return HTTP_STATUS_BAD_REQUEST; From dd379084c02367ed4171476ac9d98262b4eb85b8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 20 Dec 2012 21:38:20 +0000 Subject: [PATCH 155/503] Fix #1427 - webui: Added reverse proxy support. This includes a next webroot command line argument (using -W) and some minor mods to the core HTTP/WebUI code to support this. Most of the mods are pretty trivial and hopefully nothing will break too badly. --- src/http.c | 14 ++++++++++++-- src/main.c | 7 ++++++- src/tvheadend.h | 1 + src/webui/static/app/tvheadend.js | 2 +- src/webui/webui.c | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/http.c b/src/http.c index 07c88fc6..7610adf1 100644 --- a/src/http.c +++ b/src/http.c @@ -608,8 +608,18 @@ http_path_add(const char *path, void *opaque, http_callback_t *callback, { http_path_t *hp = malloc(sizeof(http_path_t)); - hp->hp_len = strlen(path); - hp->hp_path = strdup(path); + if (tvheadend_webroot) { + char *tmp; const char *pre = ""; + size_t len = strlen(tvheadend_webroot) + strlen(path) + 1; + if (*tvheadend_webroot != '/') { + len++; + pre = "/"; + } + hp->hp_path = tmp = malloc(len); + sprintf(tmp, "%s%s%s", pre, tvheadend_webroot, path); + } else + hp->hp_path = strdup(path); + hp->hp_len = strlen(hp->hp_path); hp->hp_opaque = opaque; hp->hp_callback = callback; hp->hp_accessmask = accessmask; diff --git a/src/main.c b/src/main.c index 564e592c..baae957c 100644 --- a/src/main.c +++ b/src/main.c @@ -77,6 +77,7 @@ int webui_port; int htsp_port; int htsp_port_extra; char *tvheadend_cwd; +const char *tvheadend_webroot; const char *tvheadend_capabilities[] = { #if ENABLE_CWC @@ -202,6 +203,7 @@ usage(const char *argv0) printf(" -s Log debug to syslog\n"); printf(" -w WebUI access port [default 9981]\n"); printf(" -e HTSP access port [default 9982]\n"); + printf(" -W WebUI context path [default /]\n"); printf("\n"); printf("Development options\n"); printf("\n"); @@ -297,7 +299,7 @@ main(int argc, char **argv) // make sure the timezone is set tzset(); - while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:R:")) != -1) { + while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:R:W:")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; @@ -366,6 +368,9 @@ main(int argc, char **argv) case 'j': join_transport = optarg; break; + case 'W': + tvheadend_webroot = optarg; + break; default: usage(argv[0]); } diff --git a/src/tvheadend.h b/src/tvheadend.h index eb317b9c..8ad66cd0 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -37,6 +37,7 @@ extern const char *tvheadend_version; extern char *tvheadend_cwd; extern const char *tvheadend_capabilities[]; +extern const char *tvheadend_webroot; #define PTS_UNSET INT64_C(0x8000000000000000) diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 2a1af2b7..d1754f49 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -34,7 +34,7 @@ tvheadend.help = function(title, pagename) { * General capabilities */ Ext.Ajax.request({ - url: '/capabilities', + url: 'capabilities', success: function(d) { if (d && d.responseText) diff --git a/src/webui/webui.c b/src/webui/webui.c index 170127cf..fbd0416f 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -74,13 +74,24 @@ static int page_root(http_connection_t *hc, const char *remain, void *opaque) { if(is_client_simple(hc)) { - http_redirect(hc, "/simple.html"); + http_redirect(hc, "simple.html"); } else { - http_redirect(hc, "/extjs.html"); + http_redirect(hc, "extjs.html"); } return 0; } +static int +page_root2(http_connection_t *hc, const char *remain, void *opaque) +{ + if (!tvheadend_webroot) return 1; + char *tmp = malloc(strlen(tvheadend_webroot) + 2); + sprintf(tmp, "%s/", tvheadend_webroot); + http_redirect(hc, tmp); + free(tmp); + return 0; +} + /** * Static download of a file from the filesystem */ @@ -922,6 +933,7 @@ int page_statedump(http_connection_t *hc, const char *remain, void *opaque); void webui_init(void) { + http_path_add("", NULL, page_root2, ACCESS_WEB_INTERFACE); http_path_add("/", NULL, page_root, ACCESS_WEB_INTERFACE); http_path_add("/dvrfile", NULL, page_dvrfile, ACCESS_WEB_INTERFACE); From 616dcda277171a0a17cbd83ba0a5ff16ef3ee6aa Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sun, 23 Dec 2012 19:09:25 +0100 Subject: [PATCH 156/503] fixed - don't mark unscrambled streams with TSS_NO_DESCRAMBLER --- src/tsdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tsdemux.c b/src/tsdemux.c index 796cc087..7a2ecd3f 100644 --- a/src/tsdemux.c +++ b/src/tsdemux.c @@ -248,7 +248,7 @@ ts_recv_packet1(service_t *t, const uint8_t *tsb, int64_t *pcrp) m++; } - if(!error) { + if(!error && t->s_scrambled != 0) { if(n == 0) { service_set_streaming_status_flags(t, TSS_NO_DESCRAMBLER); } else if(m == n) { From a822ed13b7b90d3817e30e9d598ec0c479263d05 Mon Sep 17 00:00:00 2001 From: KillerOPS Date: Wed, 26 Dec 2012 12:26:15 +0200 Subject: [PATCH 157/503] webui: add initial support for nStreamPlayer on Samsung SmartTv's --- src/webui/webui.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/webui/webui.c b/src/webui/webui.c index fbd0416f..b4790379 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -527,6 +527,8 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque) r = http_tag_list_playlist(hc); else if(!strcmp(components[0], "channels")) r = http_channel_list_playlist(hc); + else if(!strcmp(components[0], "channels.m3u")) + r = http_channel_list_playlist(hc); else if(!strcmp(components[0], "recordings")) r = http_dvr_list_playlist(hc); else { From d23f60b0311abc3d0b516bbd7ab5163e7cdfdc43 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 27 Dec 2012 00:48:14 +0000 Subject: [PATCH 158/503] Issue #1459 - fix removal of all autorec recordings on startup There were 2 issues here: - recent changes meant that recordings were purged on every startup - long standing issue meant complete recordings were removed on removal of an autorec rule (which I don't think was intended). --- src/dvr/dvr.h | 2 ++ src/dvr/dvr_autorec.c | 20 ++++++++++++-------- src/dvr/dvr_db.c | 4 +--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 7046362b..9cbfacf6 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -247,6 +247,8 @@ void dvr_config_delete(const char *name); void dvr_entry_notify(dvr_entry_t *de); +void dvr_entry_save(dvr_entry_t *de); + const char *dvr_entry_status(dvr_entry_t *de); const char *dvr_entry_schedstatus(dvr_entry_t *de); diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index defb0e39..0b6a5c91 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -42,10 +42,10 @@ static int dvr_autorec_in_init = 0; struct dvr_autorec_entry_queue autorec_entries; -static void dvr_autorec_changed(dvr_autorec_entry_t *dae); +static void dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge); /** - * + * Unlink - and remove any unstarted */ static void dvr_autorec_purge_spawns(dvr_autorec_entry_t *dae) @@ -55,7 +55,10 @@ dvr_autorec_purge_spawns(dvr_autorec_entry_t *dae) while((de = LIST_FIRST(&dae->dae_spawns)) != NULL) { LIST_REMOVE(de, de_autorec_link); de->de_autorec = NULL; - dvr_entry_cancel(de); + if (de->de_sched_state == DVR_SCHEDULED) + dvr_entry_cancel(de); + else + dvr_entry_save(de); } } @@ -425,7 +428,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values, dae->dae_serieslink->getref(dae->dae_serieslink); } if (!dvr_autorec_in_init) - dvr_autorec_changed(dae); + dvr_autorec_changed(dae, 1); return autorec_record_build(dae); } @@ -478,7 +481,7 @@ dvr_autorec_update(void) { dvr_autorec_entry_t *dae; TAILQ_FOREACH(dae, &autorec_entries, dae_link) { - dvr_autorec_changed(dae); + dvr_autorec_changed(dae, 0); } } @@ -537,7 +540,7 @@ _dvr_autorec_add(const char *config_name, notify_reload("autorec"); - dvr_autorec_changed(dae); + dvr_autorec_changed(dae, 1); } void @@ -606,12 +609,13 @@ void dvr_autorec_check_serieslink(epg_serieslink_t *s) * */ static void -dvr_autorec_changed(dvr_autorec_entry_t *dae) +dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge) { channel_t *ch; epg_broadcast_t *e; - dvr_autorec_purge_spawns(dae); + if (purge) + dvr_autorec_purge_spawns(dae); RB_FOREACH(ch, &channel_name_tree, ch_name_link) { RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) { diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 70bdabcc..627ef426 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -37,8 +37,6 @@ int dvr_iov_max; struct dvr_config_list dvrconfigs; struct dvr_entry_list dvrentries; -static void dvr_entry_save(dvr_entry_t *de); - static void dvr_timer_expire(void *aux); static void dvr_timer_start_recording(void *aux); @@ -573,7 +571,7 @@ dvr_db_load(void) /** * */ -static void +void dvr_entry_save(dvr_entry_t *de) { htsmsg_t *m = htsmsg_create_map(); From a2cec9743280ec189db5f3eb663720bc21c0fe46 Mon Sep 17 00:00:00 2001 From: Dimitris Kazakos Date: Thu, 27 Dec 2012 20:12:07 +0200 Subject: [PATCH 159/503] add greek language to language codes list --- src/lang_codes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lang_codes.c b/src/lang_codes.c index abde7c2a..0e92599b 100644 --- a/src/lang_codes.c +++ b/src/lang_codes.c @@ -180,6 +180,7 @@ const lang_code_t lang_codes[] = { { "gor", NULL, NULL , "Gorontalo" }, { "got", NULL, NULL , "Gothic" }, { "grb", NULL, NULL , "Grebo" }, + { "gre", "el", NULL , "Greek" }, { "grn", "gn", NULL , "Guarani" }, { "gsw", NULL, NULL , "Swiss German; Alemannic; Alsatian" }, { "guj", "gu", NULL , "Gujarati" }, From 426117f006e3c44226533372e85f168eeefb4112 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 29 Dec 2012 20:32:59 +0100 Subject: [PATCH 160/503] filebundle: always open files as binary If files have wrong file endings the stated lenghts won't match total data read from file. --- src/filebundle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filebundle.c b/src/filebundle.c index 77e335e7..81993d03 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -384,7 +384,7 @@ fb_file *fb_open2 } else { char path[512]; snprintf(path, sizeof(path), "%s/%s", dir->d.root, name); - FILE *fp = fopen(path, "r"); + FILE *fp = fopen(path, "rb"); if (fp) { struct stat st; lstat(path, &st); From 19fb12798b35c0d93510cb2c8fc998108c3eadff Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 29 Dec 2012 20:34:11 +0100 Subject: [PATCH 161/503] filebundle: fb_read must return real len in FB_DIRECT mode --- src/filebundle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/filebundle.c b/src/filebundle.c index 81993d03..295ed00f 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -493,7 +493,8 @@ ssize_t fb_read ( fb_file *fp, void *buf, size_t count ) memcpy(buf, fp->buf + fp->pos, count); fp->pos += count; } else if (fp->type == FB_DIRECT) { - fp->pos += fread(buf, 1, count, fp->d.cur); + count = fread(buf, 1, count, fp->d.cur); + fp->pos += count; } else { count = MIN(count, fp->b.root->f.size - fp->pos); memcpy(buf, fp->b.root->f.data + fp->pos, count); From f980a3abe31077ffa110285f86afe5b2df27e066 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 29 Dec 2012 20:37:30 +0100 Subject: [PATCH 162/503] tcp: socket writes get interrupted so must be retried --- src/tcp.c | 24 ++++++++++++++++++++++-- src/webui/webui.c | 20 +++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index cab3c5ff..88f37a02 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -181,13 +181,33 @@ int tcp_write_queue(int fd, htsbuf_queue_t *q) { htsbuf_data_t *hd; - int l, r = 0; + int l, r = 0, l2; + uint8_t* p; while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) { TAILQ_REMOVE(&q->hq_q, hd, hd_link); l = hd->hd_data_len - hd->hd_data_off; - r |= !!write(fd, hd->hd_data + hd->hd_data_off, l); + p = hd->hd_data + hd->hd_data_off; + + while(l > 0) { + l2 = write(fd, p, l); + if(l2 < 0) { + perror("tcp_write_queue"); + if(errno == EINTR) { + continue; + } else { + break; + } + } + l -= l2; + p += l2; + } + + if(l == 0) { + r = 1; + } + free(hd->hd_data); free(hd); } diff --git a/src/webui/webui.c b/src/webui/webui.c index fbd0416f..c6f2b8d5 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -98,12 +98,12 @@ page_root2(http_connection_t *hc, const char *remain, void *opaque) static int page_static_file(http_connection_t *hc, const char *remain, void *opaque) { - int ret = 0; + int ret = 0, r; const char *base = opaque; char path[500]; ssize_t size; const char *content = NULL, *postfix; - char buf[4096]; + char buf[4096], *p; const char *gzip; if(remain == NULL) @@ -139,7 +139,21 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) ret = 500; break; } - if (write(hc->hc_fd, buf, c) != c) { + p = buf; + while(c > 0) { + r = write(hc->hc_fd, p, c); + if(r < 0) { + perror("page_static_file"); + if(errno == EINTR) { + continue; + } else { + break; + } + } + c -= r; + p += r; + } + if (c != 0) { ret = 500; break; } From 1418041e14e91242a7e820a7c740e3e40cb375f9 Mon Sep 17 00:00:00 2001 From: Jacek Tomasiak Date: Sun, 30 Sep 2012 19:11:06 +0200 Subject: [PATCH 163/503] fixed content type translation --- src/webui/static/app/epg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui/static/app/epg.js b/src/webui/static/app/epg.js index 1b09184b..9fc74aa8 100644 --- a/src/webui/static/app/epg.js +++ b/src/webui/static/app/epg.js @@ -20,7 +20,7 @@ tvheadend.contentGroupLookupName = function(code) { ret = ""; tvheadend.ContentGroupStore.each(function(r) { if (r.data.code == code) ret = r.data.name; - else if (ret == "" && r.data.code == code & 0xF0) ret = r.data.name; + else if (ret == "" && r.data.code == (code & 0xF0)) ret = r.data.name; }); return ret; } From 0dcbb359a5da1f64285a8a25e9ac5429ebd12e0d Mon Sep 17 00:00:00 2001 From: Jacek Tomasiak Date: Sat, 29 Dec 2012 10:30:19 +0100 Subject: [PATCH 164/503] added separator before 'onscreen' style of episode number --- src/epg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/epg.c b/src/epg.c index eb6a6a88..fafb65dd 100644 --- a/src/epg.c +++ b/src/epg.c @@ -1078,8 +1078,8 @@ size_t epg_episode_number_format if ( cfmt && num.e_cnt ) i+= snprintf(&buf[i], len-i, cfmt, num.e_cnt); } else if ( num.text ) { - strncpy(buf, num.text, len); - i = strlen(buf); + if (pre) i += snprintf(&buf[i], len-i, "%s", pre); + i += snprintf(&buf[i], len-i, "%s", num.text); } return i; } From 16db6f132f3896fad4782c750230adf4ad1e8b86 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 30 Dec 2012 12:12:24 +0000 Subject: [PATCH 165/503] support: ensure git tree properly cleaned. --- support/launchpad-ppa | 5 ++++- support/tarball | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/support/launchpad-ppa b/support/launchpad-ppa index c3f5936e..ff367d04 100755 --- a/support/launchpad-ppa +++ b/support/launchpad-ppa @@ -26,11 +26,14 @@ TVH_ROOT=$(cd $(dirname $0)/..; pwd) # Setup cd $TVH_ROOT || exit 1 -git checkout $1 && git checkout . || exit 1 NOW=`date -R` CHANGELOG=$TVH_ROOT/debian/changelog VERFILE=$TVH_ROOT/src/version.c +# Checkout +git checkout $REL || die "could not checkout $REL" +git clean -dfx || die "could not clean git tree" + # Create version file VER=$($TVH_ROOT/support/version $VERFILE) diff --git a/support/tarball b/support/tarball index ca65b664..2d5b809e 100755 --- a/support/tarball +++ b/support/tarball @@ -16,12 +16,12 @@ cd $SRCDIR # Arguments REL=$1 + +# Checkout if [ ! -z "$REL" ]; then git checkout $REL || die "could not checkout $REL" fi - -# Clean -git checkout . || die "could not clean git tree" +git clean -dfx || die "could not clean git tree" # Version VER=$(./support/version) From 051e404da5e1ba3876cce179b1463fe6bac6149b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 30 Dec 2012 12:17:34 +0000 Subject: [PATCH 166/503] [PR-194] - tidy up code, create a generic write wrapper to simplify code. Fix #1177 --- src/tcp.c | 27 ++++++--------------------- src/tvheadend.h | 2 ++ src/webui/webui.c | 20 +++----------------- src/wrappers.c | 21 +++++++++++++++++++++ 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index 88f37a02..001c4709 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -181,31 +181,16 @@ int tcp_write_queue(int fd, htsbuf_queue_t *q) { htsbuf_data_t *hd; - int l, r = 0, l2; - uint8_t* p; + int l, r = 0; + void *p; while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) { TAILQ_REMOVE(&q->hq_q, hd, hd_link); - l = hd->hd_data_len - hd->hd_data_off; - p = hd->hd_data + hd->hd_data_off; - - while(l > 0) { - l2 = write(fd, p, l); - if(l2 < 0) { - perror("tcp_write_queue"); - if(errno == EINTR) { - continue; - } else { - break; - } - } - l -= l2; - p += l2; - } - - if(l == 0) { - r = 1; + if (!r) { + l = hd->hd_data_len - hd->hd_data_off; + p = hd->hd_data + hd->hd_data_off; + r = tvh_write(fd, p, l); } free(hd->hd_data); diff --git a/src/tvheadend.h b/src/tvheadend.h index 8ad66cd0..23018afe 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -454,6 +454,8 @@ int tvh_socket(int domain, int type, int protocol); int tvh_pipe(int flags, th_pipe_t *pipe); +int tvh_write(int fd, void *buf, size_t len); + void hexdump(const char *pfx, const uint8_t *data, int len); uint32_t tvh_crc32(uint8_t *data, size_t datalen, uint32_t crc); diff --git a/src/webui/webui.c b/src/webui/webui.c index c6f2b8d5..8fbb31db 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -98,12 +98,12 @@ page_root2(http_connection_t *hc, const char *remain, void *opaque) static int page_static_file(http_connection_t *hc, const char *remain, void *opaque) { - int ret = 0, r; + int ret = 0; const char *base = opaque; char path[500]; ssize_t size; const char *content = NULL, *postfix; - char buf[4096], *p; + char buf[4096]; const char *gzip; if(remain == NULL) @@ -139,21 +139,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) ret = 500; break; } - p = buf; - while(c > 0) { - r = write(hc->hc_fd, p, c); - if(r < 0) { - perror("page_static_file"); - if(errno == EINTR) { - continue; - } else { - break; - } - } - c -= r; - p += r; - } - if (c != 0) { + if (tvh_write(hc->hc_fd, buf, c)) { ret = 500; break; } diff --git a/src/wrappers.c b/src/wrappers.c index fd374ec8..febe6a8c 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -48,3 +48,24 @@ tvh_pipe(int flags, th_pipe_t *p) pthread_mutex_unlock(&fork_lock); return err; } + +int +tvh_write(int fd, void *buf, size_t len) +{ + ssize_t c; + + while (len) { + c = write(fd, buf, len); + if (c < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + usleep(100); + continue; + } + break; + } + len -= c; + buf += c; + } + + return len ? 1 : 0; +} From c98d8626b1b890fd43f6955d478da15c27db1339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 30 Dec 2012 20:52:55 +0100 Subject: [PATCH 167/503] changed the tvh_write() wrapper take const pointer as agument --- src/tvheadend.h | 2 +- src/wrappers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tvheadend.h b/src/tvheadend.h index 23018afe..a601f80e 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -454,7 +454,7 @@ int tvh_socket(int domain, int type, int protocol); int tvh_pipe(int flags, th_pipe_t *pipe); -int tvh_write(int fd, void *buf, size_t len); +int tvh_write(int fd, const void *buf, size_t len); void hexdump(const char *pfx, const uint8_t *data, int len); diff --git a/src/wrappers.c b/src/wrappers.c index febe6a8c..5b08d3ce 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -50,7 +50,7 @@ tvh_pipe(int flags, th_pipe_t *p) } int -tvh_write(int fd, void *buf, size_t len) +tvh_write(int fd, const void *buf, size_t len) { ssize_t c; From b42cbe593cc4e4e38dc68b42075a4385ef89bd64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 30 Dec 2012 21:53:18 +0100 Subject: [PATCH 168/503] make use of tvh_write wrapper in passthrough muxer --- src/muxer_pass.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/muxer_pass.c b/src/muxer_pass.c index 933be24b..ca21c8dd 100644 --- a/src/muxer_pass.c +++ b/src/muxer_pass.c @@ -194,16 +194,16 @@ pass_muxer_open_file(muxer_t *m, const char *filename) /** - * Write TS packets to the file descriptor + * Write data to the file descriptor */ static void -pass_muxer_write(muxer_t *m, const void *ts, size_t len) +pass_muxer_write(muxer_t *m, const void *data, size_t size) { pass_muxer_t *pm = (pass_muxer_t*)m; if(pm->pm_error) { pm->m_errors++; - } else if(write(pm->pm_fd, ts, len) != len) { + } else if(tvh_write(pm->pm_fd, data, size)) { pm->pm_error = errno; tvhlog(LOG_ERR, "pass", "%s: Write failed -- %s", pm->pm_filename, strerror(errno)); From d4be257b80f25e331d4097107765e7d6daf150d0 Mon Sep 17 00:00:00 2001 From: Jason Millard Date: Sun, 30 Dec 2012 19:10:14 -0500 Subject: [PATCH 169/503] Added encryption column to configuration->TV Adapters->Services. Added additional caidnametab entry. --- src/dvb/dvb_service.c | 4 ++++ src/psi.c | 1 + src/webui/static/app/dvb.js | 8 ++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 65b7fbf5..31f1b9e7 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -447,6 +447,7 @@ dvb_service_build_msg(service_t *t) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; htsmsg_t *m = htsmsg_create_map(); + uint16_t caid; char buf[100]; htsmsg_add_str(m, "id", t->s_identifier); @@ -464,6 +465,9 @@ dvb_service_build_msg(service_t *t) htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); + if((caid = service_get_encryption(t)) != 0) + htsmsg_add_str(m, "encryption", psi_caid2name(caid)); + dvb_mux_nicefreq(buf, sizeof(buf), tdmi); htsmsg_add_str(m, "mux", buf); diff --git a/src/psi.c b/src/psi.c index 4d49ca71..07f7d3cd 100644 --- a/src/psi.c +++ b/src/psi.c @@ -874,6 +874,7 @@ static struct strtab caidnametab[] = { { "CryptoWorks ICE", 0x0D96 }, { "CryptoWorks ICE", 0x0D97 }, { "PowerVu", 0x0E00 }, + { "PowerVu", 0x0E11 }, { "Sony", 0x0F00 }, { "Tandberg", 0x1000 }, { "Thompson", 0x1100 }, diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 0f1b836f..c3a3f314 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -497,6 +497,10 @@ tvheadend.dvb_services = function(adapterId) { header : "Network", dataIndex : 'network', width : 100 + }, { + header : "Encryption", + dataIndex : 'encryption', + width : 100 }, { header : "Multiplex", dataIndex : 'mux', @@ -526,8 +530,8 @@ tvheadend.dvb_services = function(adapterId) { var store = new Ext.data.JsonStore({ root : 'entries', fields : Ext.data.Record.create([ 'id', 'enabled', 'type', 'sid', 'pmt', - 'pcr', 'svcname', 'network', 'provider', 'mux', 'channelname', 'prefcapid', - 'dvb_charset', 'dvb_eit_enable' ]), + 'pcr', 'svcname', 'network', 'provider', 'encryption', 'mux', 'channelname', + 'prefcapid', 'dvb_charset', 'dvb_eit_enable' ]), url : "dvb/services/" + adapterId, autoLoad : true, id : 'id', From c341b12f904fefd37c16121465e59e6a4dcccae4 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Mon, 31 Dec 2012 13:39:18 +0100 Subject: [PATCH 170/503] Fix memory leak on add channel --- src/webui/extjs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 059417c0..ca987e94 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -487,6 +487,7 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_msg(out, "entries", array); } else if(!strcmp(op, "create")) { + htsmsg_destroy(out); out = build_record_channel(channel_create()); } else if(!strcmp(op, "delete") && in != NULL) { From bb4ac298dd8a2de95dd3f15e361290f311d043a9 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Mon, 31 Dec 2012 14:02:29 +0100 Subject: [PATCH 171/503] Reduce code duplication --- src/tcp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index 001c4709..a97b079b 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -185,16 +185,12 @@ tcp_write_queue(int fd, htsbuf_queue_t *q) void *p; while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) { - TAILQ_REMOVE(&q->hq_q, hd, hd_link); - if (!r) { l = hd->hd_data_len - hd->hd_data_off; p = hd->hd_data + hd->hd_data_off; r = tvh_write(fd, p, l); } - - free(hd->hd_data); - free(hd); + htsbuf_data_free(q, hd); } q->hq_size = 0; return r; From ecc43232cc1d649c16cabc76dae11f34863fda55 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Mon, 31 Dec 2012 14:46:06 +0100 Subject: [PATCH 172/503] Remove autodestructor --- src/htsmsg.c | 9 --------- src/htsmsg.h | 3 --- src/webui/extjs.c | 8 ++++++-- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/htsmsg.c b/src/htsmsg.c index 935c30b6..90f4e4eb 100644 --- a/src/htsmsg.c +++ b/src/htsmsg.c @@ -703,13 +703,4 @@ htsmsg_get_cdata(htsmsg_t *m, const char *field) } -/** - * - */ -void -htsmsg_dtor(htsmsg_t **mp) -{ - if(*mp != NULL) - htsmsg_destroy(*mp); -} diff --git a/src/htsmsg.h b/src/htsmsg.h index 82fb7fda..8343634f 100644 --- a/src/htsmsg.h +++ b/src/htsmsg.h @@ -320,6 +320,3 @@ htsmsg_t *htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name); const char *htsmsg_get_cdata(htsmsg_t *m, const char *field); -extern void htsmsg_dtor(htsmsg_t **mp); - -#define htsmsg_autodtor(n) htsmsg_t *n __attribute__((cleanup(htsmsg_dtor))) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 059417c0..39b07884 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -470,10 +470,10 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque) if(op == NULL) return 400; - htsmsg_autodtor(in) = + htsmsg_t *in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL; - htsmsg_autodtor(out) = htsmsg_create_map(); + htsmsg_t *out = htsmsg_create_map(); scopedgloballock(); @@ -496,11 +496,15 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque) extjs_channels_update(in); } else { + htsmsg_destroy(in); + htsmsg_destroy(out); return 400; } htsmsg_json_serialize(out, hq, 0); http_output_content(hc, "text/x-json; charset=UTF-8"); + htsmsg_destroy(in); + htsmsg_destroy(out); return 0; } From 993c2decb3896326744b54bcdb9ae2c831e4f5f4 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 30 Dec 2012 19:37:57 +0000 Subject: [PATCH 173/503] formatting: fix some indentation and remove deprecated function. --- src/webui/webui.c | 117 ++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 77 deletions(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index 67ec1415..0f3560c5 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -154,7 +154,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) */ static void http_stream_run(http_connection_t *hc, streaming_queue_t *sq, - const char *name, muxer_container_type_t mc) + const char *name, muxer_container_type_t mc) { streaming_message_t *sm; int run = 1; @@ -189,11 +189,11 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, //Check socket status getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); if(err) { - tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); - run = 0; + tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); + run = 0; }else if(timeouts >= 20) { - tvhlog(LOG_WARNING, "webui", "Stop streaming %s, timeout waiting for packets", hc->hc_url_orig); - run = 0; + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, timeout waiting for packets", hc->hc_url_orig); + run = 0; } } pthread_mutex_unlock(&sq->sq_mutex); @@ -208,37 +208,38 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, case SMT_MPEGTS: case SMT_PACKET: if(started) { - muxer_write_pkt(mux, sm->sm_type, sm->sm_data); - sm->sm_data = NULL; + muxer_write_pkt(mux, sm->sm_type, sm->sm_data); + sm->sm_data = NULL; } break; case SMT_START: if(!started) { - tvhlog(LOG_DEBUG, "webui", "Start streaming %s", hc->hc_url_orig); - http_output_content(hc, muxer_mime(mux, sm->sm_data)); + tvhlog(LOG_DEBUG, "webui", "Start streaming %s", hc->hc_url_orig); + http_output_content(hc, muxer_mime(mux, sm->sm_data)); - if(muxer_init(mux, sm->sm_data, name) < 0) - run = 0; + if(muxer_init(mux, sm->sm_data, name) < 0) + run = 0; - started = 1; + started = 1; } else if(muxer_reconfigure(mux, sm->sm_data) < 0) { - tvhlog(LOG_WARNING, "webui", "Unable to reconfigure stream %s", hc->hc_url_orig); + tvhlog(LOG_WARNING, "webui", "Unable to reconfigure stream %s", hc->hc_url_orig); } break; case SMT_STOP: if(sm->sm_code != SM_CODE_SOURCE_RECONFIGURED) { - tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, - streaming_code2txt(sm->sm_code)); - run = 0; + tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, + streaming_code2txt(sm->sm_code)); + run = 0; } break; case SMT_SERVICE_STATUS: if(getsockopt(hc->hc_fd, SOL_SOCKET, SO_ERROR, &err, &errlen)) { - tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", hc->hc_url_orig); - run = 0; + tvhlog(LOG_DEBUG, "webui", "Stop streaming %s, client hung up", + hc->hc_url_orig); + run = 0; } break; @@ -246,14 +247,14 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, break; case SMT_NOSTART: - tvhlog(LOG_WARNING, "webui", "Couldn't start streaming %s, %s", hc->hc_url_orig, - streaming_code2txt(sm->sm_code)); + tvhlog(LOG_WARNING, "webui", "Couldn't start streaming %s, %s", + hc->hc_url_orig, streaming_code2txt(sm->sm_code)); run = 0; break; case SMT_EXIT: tvhlog(LOG_WARNING, "webui", "Stop streaming %s, %s", hc->hc_url_orig, - streaming_code2txt(sm->sm_code)); + streaming_code2txt(sm->sm_code)); run = 0; break; } @@ -291,7 +292,7 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel) htsbuf_qprintf(hq, "#EXTM3U\n"); htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel->ch_name); htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, - access_ticket_create(buf)); + access_ticket_create(buf)); http_output_content(hc, "audio/x-mpegurl"); @@ -318,7 +319,7 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag) snprintf(buf, sizeof(buf), "/stream/channelid/%d", ctm->ctm_channel->ch_id); htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ctm->ctm_channel->ch_name); htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, - access_ticket_create(buf)); + access_ticket_create(buf)); } http_output_content(hc, "audio/x-mpegurl"); @@ -349,7 +350,7 @@ http_tag_list_playlist(http_connection_t *hc) snprintf(buf, sizeof(buf), "/playlist/tagid/%d", ct->ct_identifier); htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ct->ct_name); htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, - access_ticket_create(buf)); + access_ticket_create(buf)); } http_output_content(hc, "audio/x-mpegurl"); @@ -378,7 +379,7 @@ http_channel_list_playlist(http_connection_t *hc) htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name); htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, - access_ticket_create(buf)); + access_ticket_create(buf)); } http_output_content(hc, "audio/x-mpegurl"); @@ -425,7 +426,7 @@ http_dvr_list_playlist(http_connection_t *hc) snprintf(buf, sizeof(buf), "/dvrfile/%d", de->de_id); htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, - access_ticket_create(buf)); + access_ticket_create(buf)); } http_output_content(hc, "audio/x-mpegurl"); @@ -678,9 +679,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) } s = subscription_create_from_channel(ch, priority, "HTTP", st, flags, - inet_ntoa(hc->hc_peer->sin_addr), - hc->hc_username, - http_arg_get(&hc->hc_args, "User-Agent")); + inet_ntoa(hc->hc_peer->sin_addr), + hc->hc_username, + http_arg_get(&hc->hc_args, "User-Agent")); if(s) { name = strdupa(ch->ch_name); @@ -759,45 +760,6 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque) } } - -/** - * Static download of a file from an embedded filebundle - */ -#if 0 -static int -page_static_bundle(http_connection_t *hc, const char *remain, void *opaque) -{ - const struct filebundle *fb = opaque; - const struct filebundle_entry *fbe; - const char *content = NULL, *postfix; - - if(remain == NULL) - return 404; - - postfix = strrchr(remain, '.'); - if(postfix != NULL) { - postfix++; - if(!strcmp(postfix, "js")) - content = "text/javascript; charset=UTF-8"; - } - - for(fbe = fb->entries; fbe->filename != NULL; fbe++) { - if(!strcmp(fbe->filename, remain)) { - - http_send_header(hc, 200, content, fbe->size, - fbe->original_size == -1 ? NULL : "gzip", NULL, 10, 0, - NULL); - /* ignore return value */ - if(write(hc->hc_fd, fbe->data, fbe->size) != fbe->size) - return -1; - return 0; - } - } - return 404; -} -#endif - - /** * Download a recorded file */ @@ -866,18 +828,18 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) content_len = file_end - file_start+1; sprintf(range_buf, "bytes %"PRId64"-%"PRId64"/%"PRId64"", - file_start, file_end, st.st_size); + file_start, file_end, st.st_size); if(file_start > 0) lseek(fd, file_start, SEEK_SET); if(de->de_title != NULL) { snprintf(disposition, sizeof(disposition), - "attachment; filename=%s.%s", lang_str_get(de->de_title, NULL), postfix); + "attachment; filename=%s.%s", lang_str_get(de->de_title, NULL), postfix); i = 20; while(disposition[i]) { if(disposition[i] == ' ') - disposition[i] = '_'; + disposition[i] = '_'; i++; } @@ -886,17 +848,17 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) } http_send_header(hc, range ? HTTP_STATUS_PARTIAL_CONTENT : HTTP_STATUS_OK, - content, content_len, NULL, NULL, 10, - range ? range_buf : NULL, - disposition[0] ? disposition : NULL); + content, content_len, NULL, NULL, 10, + range ? range_buf : NULL, + disposition[0] ? disposition : NULL); if(!hc->hc_no_output) { while(content_len > 0) { chunk = MIN(1024 * 1024 * 1024, content_len); r = sendfile(hc->hc_fd, fd, NULL, chunk); if(r == -1) { - close(fd); - return -1; + close(fd); + return -1; } content_len -= r; } @@ -913,7 +875,8 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) static void webui_static_content(const char *http_path, const char *source) { - http_path_add(http_path, strdup(source), page_static_file, ACCESS_WEB_INTERFACE); + http_path_add(http_path, strdup(source), page_static_file, + ACCESS_WEB_INTERFACE); } From 54ceb4ef00f7786a9c4dc14dd6e88d0e1e37848e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 30 Dec 2012 19:55:46 +0000 Subject: [PATCH 174/503] Replace all use of write() with tvh_write(). tvh_write() will deal with incomplete/interrupted writes and will ensure the write operation is completed unless a fatal error occurs. --- src/capmt.c | 13 ++++++------- src/cwc.c | 8 ++++---- src/dvb/dvb_adapter.c | 4 ++-- src/epgdb.c | 3 +-- src/htsp_server.c | 31 +++++++++---------------------- src/settings.c | 3 +-- src/v4l.c | 2 +- 7 files changed, 24 insertions(+), 40 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index 6da676cd..304bdd23 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -227,7 +227,7 @@ static int capmt_send_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len) { if (capmt->capmt_oscam) { - int i, sent = 0; + int i; // dumping current SID table for (i = 0; i < MAX_SOCKETS; i++) @@ -280,18 +280,17 @@ capmt_send_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len) tvhlog(LOG_DEBUG, "capmt", "created socket with socket_fd=%d", capmt->capmt_sock[i]); } if (capmt->capmt_sock[i] > 0) { - sent = write(capmt->capmt_sock[i], buf, len); - tvhlog(LOG_DEBUG, "capmt", "socket_fd=%d len=%d sent=%d", capmt->capmt_sock[i], (int)len, sent); - if (sent != len) { - tvhlog(LOG_ERR, "capmt", "%s: len != sent", __FUNCTION__); + if (tvh_write(capmt->capmt_sock[i], buf, len)) { + tvhlog(LOG_DEBUG, "capmt", "socket_fd=%d send failed", capmt->capmt_sock[i]); close(capmt->capmt_sock[i]); capmt->capmt_sock[i] = 0; + return -1; } } - return sent; } else // standard old capmt mode - return write(capmt->capmt_sock[0], buf, len); + tvh_write(capmt->capmt_sock[0], buf, len); + return 0; } static void diff --git a/src/cwc.c b/src/cwc.c index 79c07452..d8733ec7 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -477,7 +477,7 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq) { cwc_message_t *cm = malloc(sizeof(cwc_message_t)); uint8_t *buf = cm->cm_data; - int seq, n; + int seq; if(len + 12 > CWS_NETMSGSIZE) { free(cm); @@ -513,8 +513,7 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq) pthread_cond_signal(&cwc->cwc_writer_cond); pthread_mutex_unlock(&cwc->cwc_writer_mutex); } else { - n = write(cwc->cwc_fd, buf, len); - if(n != len) + if (tvh_write(cwc->cwc_fd, buf, len)) tvhlog(LOG_INFO, "cwc", "write error %s", strerror(errno)); free(cm); @@ -1032,7 +1031,8 @@ cwc_writer_thread(void *aux) TAILQ_REMOVE(&cwc->cwc_writeq, cm, cm_link); pthread_mutex_unlock(&cwc->cwc_writer_mutex); // int64_t ts = getmonoclock(); - r = write(cwc->cwc_fd, cm->cm_data, cm->cm_len); + if (tvh_write(cwc->cwc_fd, cm->cm_data, cm->cm_len)) + tvhlog(LOG_INFO, "cwc", "write error %s", strerror(errno)); // printf("Write took %lld usec\n", getmonoclock() - ts); free(cm); pthread_mutex_lock(&cwc->cwc_writer_mutex); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 21b9cab6..75681b5e 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -623,8 +623,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) /* Stop DVR thread */ if (tda->tda_dvr_pipe.rd != -1) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - int err = write(tda->tda_dvr_pipe.wr, "", 1); - assert(err != -1); + int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); + assert(!err); pthread_join(tda->tda_dvr_thread, NULL); close(tda->tda_dvr_pipe.rd); close(tda->tda_dvr_pipe.wr); diff --git a/src/epgdb.c b/src/epgdb.c index f56e5982..33e53143 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -241,9 +241,8 @@ static int _epg_write ( int fd, htsmsg_t *m ) int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000); htsmsg_destroy(m); if (!r) { - ssize_t w = write(fd, msgdata, msglen); + ret = tvh_write(fd, msgdata, msglen); free(msgdata); - if(w == msglen) ret = 0; } } else { ret = 0; diff --git a/src/htsp_server.c b/src/htsp_server.c index b84768e0..ebbb07a2 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1669,7 +1669,6 @@ static void * htsp_write_scheduler(void *aux) { htsp_connection_t *htsp = aux; - int r; htsp_msg_q_t *hmq; htsp_msg_t *hm; void *dptr; @@ -1706,33 +1705,21 @@ htsp_write_scheduler(void *aux) pthread_mutex_unlock(&htsp->htsp_out_mutex); - r = htsmsg_binary_serialize(hm->hm_msg, &dptr, &dlen, INT32_MAX); + if (htsmsg_binary_serialize(hm->hm_msg, &dptr, &dlen, INT32_MAX) != 0) { + tvhlog(LOG_WARNING, "htsp", "%s: failed to serialize data", + htsp->htsp_logname); + } htsp_msg_destroy(hm); - void *freeme = dptr; - - while(dlen > 0) { - r = write(htsp->htsp_fd, dptr, dlen); - if(r < 0) { - if(errno == EAGAIN || errno == EWOULDBLOCK) - continue; - tvhlog(LOG_INFO, "htsp", "%s: Write error -- %s", - htsp->htsp_logname, strerror(errno)); - break; - } - if(r == 0) { - tvhlog(LOG_ERR, "htsp", "%s: write() returned 0", - htsp->htsp_logname); - } - dptr += r; - dlen -= r; + if (tvh_write(htsp->htsp_fd, dptr, dlen)) { + tvhlog(LOG_INFO, "htsp", "%s: Write error -- %s", + htsp->htsp_logname, strerror(errno)); + break; } - free(freeme); + free(dptr); pthread_mutex_lock(&htsp->htsp_out_mutex); - if(dlen) - break; } // Shutdown socket to make receive thread terminate entire HTSP connection diff --git a/src/settings.c b/src/settings.c index 2f1ab077..c98fc2fd 100644 --- a/src/settings.c +++ b/src/settings.c @@ -158,8 +158,7 @@ hts_settings_save(htsmsg_t *record, const char *pathfmt, ...) htsbuf_queue_init(&hq, 0); htsmsg_json_serialize(record, &hq, 1); TAILQ_FOREACH(hd, &hq.hq_q, hd_link) - if(write(fd, hd->hd_data + hd->hd_data_off, hd->hd_data_len) != - hd->hd_data_len) { + if(tvh_write(fd, hd->hd_data + hd->hd_data_off, hd->hd_data_len)) { tvhlog(LOG_ALERT, "settings", "Failed to write file \"%s\" - %s", tmppath, strerror(errno)); ok = 0; diff --git a/src/v4l.c b/src/v4l.c index a7b86075..a640a387 100644 --- a/src/v4l.c +++ b/src/v4l.c @@ -253,7 +253,7 @@ v4l_service_stop(service_t *t) assert(va->va_current_service != NULL); - if(write(va->va_pipe[1], &c, 1) != 1) + if(tvh_write(va->va_pipe[1], &c, 1)) tvhlog(LOG_ERR, "v4l", "Unable to close video thread -- %s", strerror(errno)); From 2b0e495b7bdf908389a9b1214a7ee1798092e6a1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 1 Jan 2013 09:05:37 +0000 Subject: [PATCH 175/503] webui: simplification of the tvheadend_webroot processing. --- src/http.c | 8 ++------ src/main.c | 14 ++++++++++++-- src/tvheadend.h | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/http.c b/src/http.c index 7610adf1..dee8e1f8 100644 --- a/src/http.c +++ b/src/http.c @@ -607,16 +607,12 @@ http_path_add(const char *path, void *opaque, http_callback_t *callback, uint32_t accessmask) { http_path_t *hp = malloc(sizeof(http_path_t)); + char *tmp; if (tvheadend_webroot) { - char *tmp; const char *pre = ""; size_t len = strlen(tvheadend_webroot) + strlen(path) + 1; - if (*tvheadend_webroot != '/') { - len++; - pre = "/"; - } hp->hp_path = tmp = malloc(len); - sprintf(tmp, "%s%s%s", pre, tvheadend_webroot, path); + sprintf(tmp, "%s/%s", tvheadend_webroot, path); } else hp->hp_path = strdup(path); hp->hp_len = strlen(hp->hp_path); diff --git a/src/main.c b/src/main.c index baae957c..73afb075 100644 --- a/src/main.c +++ b/src/main.c @@ -76,7 +76,7 @@ int log_debug_to_console; int webui_port; int htsp_port; int htsp_port_extra; -char *tvheadend_cwd; +const char *tvheadend_cwd; const char *tvheadend_webroot; const char *tvheadend_capabilities[] = { @@ -270,6 +270,7 @@ main(int argc, char **argv) const char *pidpath = "/var/run/tvheadend.pid"; struct group *grp; struct passwd *pw; + char *webroot; const char *usernam = NULL; const char *groupnam = NULL; int logfacility = LOG_DAEMON; @@ -369,7 +370,16 @@ main(int argc, char **argv) join_transport = optarg; break; case 'W': - tvheadend_webroot = optarg; + webroot = malloc(strlen(optarg) + (*optarg == '/' ? 0 : 1)); + if (*optarg != '/') { + *webroot = '/'; + strcpy(webroot+1, optarg); + } else { + strcpy(webroot, optarg); + } + if (webroot[strlen(webroot)-1] == '/') + webroot[strlen(webroot)-1] = '\0'; + tvheadend_webroot = webroot; break; default: usage(argv[0]); diff --git a/src/tvheadend.h b/src/tvheadend.h index a601f80e..9bdaf1f2 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -35,7 +35,7 @@ #include "redblack.h" extern const char *tvheadend_version; -extern char *tvheadend_cwd; +extern const char *tvheadend_cwd; extern const char *tvheadend_capabilities[]; extern const char *tvheadend_webroot; From acdc094fe7ac27627ceca842f52b0fdf0a80b100 Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Sun, 7 Oct 2012 12:32:51 +0100 Subject: [PATCH 176/503] [PR-174] - Icon caching support to reduce overhead on upstream providers. --- Makefile | 4 +- docs/html/config_misc.html | 14 ++ src/config2.c | 47 +++++ src/config2.h | 12 ++ src/htsp_server.c | 8 +- src/http.c | 14 +- src/iconserve.c | 358 +++++++++++++++++++++++++++++++++ src/iconserve.h | 47 +++++ src/main.c | 3 + src/webui/extjs.c | 23 ++- src/webui/simpleui.c | 4 +- src/webui/statedump.c | 3 +- src/webui/static/app/config.js | 20 +- src/webui/webui.c | 5 +- src/webui/webui.h | 4 +- 15 files changed, 545 insertions(+), 21 deletions(-) create mode 100644 src/iconserve.c create mode 100644 src/iconserve.h diff --git a/Makefile b/Makefile index 3d8fab24..c63cbe58 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ CFLAGS += -Wmissing-prototypes -fms-extensions CFLAGS += -g -funsigned-char -O2 CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} -LDFLAGS += -lrt -ldl -lpthread -lm +LDFLAGS += -lrt -ldl -lpthread -lm -lcurl # # Other config @@ -140,6 +140,8 @@ SRCS += src/muxer.c \ src/muxer_pass.c \ src/muxer_tvh.c \ +SRCS += src/iconserve.c \ + # # Optional code # diff --git a/docs/html/config_misc.html b/docs/html/config_misc.html index 00e00b07..5f11cf7b 100644 --- a/docs/html/config_misc.html +++ b/docs/html/config_misc.html @@ -20,5 +20,19 @@ dvb-apps stores these in /usr/share/dvb/. Leave blank to use TVH's internal file set. +
Cache channel icons: +
+ Enable the caching of channel icons. This will cause TVH to download channel + icons locally, and then when requested via HTSP clients the URL for the icon + (or logo) will be retrieved from the TVH web server rather than fetched each + time by the HTSP client. + This REQUIRES the TVH server IP address field to also be populated + +
TVH Server IP Address: +
+ Enter the IP address of the TVH server used for HTSP clients. This permits the + above cache channel icons to work (TVH Clients are directed to the IP address + entered here to retrieve channel icons from) + diff --git a/src/config2.c b/src/config2.c index 5bcbd0d1..b90db4fe 100644 --- a/src/config2.c +++ b/src/config2.c @@ -74,3 +74,50 @@ int config_set_muxconfpath ( const char *path ) } return 0; } + +const char *config_get_iconserve ( void ) +{ + return htsmsg_get_str(config, "iconserve"); +} + +int config_set_iconserve ( const char *setting ) +{ + const char *c = config_get_iconserve(); + if (!c || strcmp(c, setting)) { + if (c) htsmsg_delete_field(config, "iconserve"); + htsmsg_add_str(config, "iconserve", setting); + return 1; + } + return 0; +} +const char *config_get_iconserve_periodicdownload ( void ) +{ + return htsmsg_get_str(config, "iconserve_periodicdownload"); +} + +int config_set_iconserve_periodicdownload ( const char *setting ) +{ + const char *c = config_get_iconserve_periodicdownload(); + if (!c || strcmp(c, setting)) { + if (c) htsmsg_delete_field(config, "iconserve_periodicdownload"); + htsmsg_add_str(config, "iconserve_periodicdownload", setting); + return 1; + } + return 0; +} + +const char *config_get_serverip ( void ) +{ + return htsmsg_get_str(config, "serverip"); +}; + +int config_set_serverip ( const char *setting ) +{ + const char *c = config_get_serverip(); + if (!c || strcmp(c, setting)) { + if (c) htsmsg_delete_field(config, "serverip"); + htsmsg_add_str(config, "serverip", setting); + return 1; + } + return 0; +}; diff --git a/src/config2.h b/src/config2.h index cd68e306..6e90be6f 100644 --- a/src/config2.h +++ b/src/config2.h @@ -32,6 +32,18 @@ const char *config_get_muxconfpath ( void ); int config_set_muxconfpath ( const char *str ) __attribute__((warn_unused_result)); +const char *config_get_iconserve ( void ); +int config_set_iconserve ( const char *str ) + __attribute__((warn_unused_result)); + +const char *config_get_iconserve_periodicdownload ( void ); +int config_set_iconserve_periodicdownload ( const char *str ) + __attribute__((warn_unused_result)); + +const char *config_get_serverip ( void ); +int config_set_serverip ( const char *str ) + __attribute__((warn_unused_result)); + const char *config_get_language ( void ); int config_set_language ( const char *str ) __attribute__((warn_unused_result)); diff --git a/src/htsp_server.c b/src/htsp_server.c index ebbb07a2..ba10139a 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -42,6 +42,8 @@ #include "htsmsg_binary.h" #include "epg.h" #include "plumbing/tsfix.h" +#include "iconserve.h" +#include "config2.h" #include #include "settings.h" @@ -447,8 +449,10 @@ htsp_build_channel(channel_t *ch, const char *method) htsmsg_add_u32(out, "channelNumber", ch->ch_number); htsmsg_add_str(out, "channelName", ch->ch_name); - if(ch->ch_icon != NULL) - htsmsg_add_str(out, "channelIcon", ch->ch_icon); + + if(ch->ch_icon != NULL) { + htsmsg_add_str(out, "channelIcon", logo_query(ch->ch_id, ch->ch_icon)); + }; now = ch->ch_epg_now; next = ch->ch_epg_next; diff --git a/src/http.c b/src/http.c index dee8e1f8..e87ec543 100644 --- a/src/http.c +++ b/src/http.c @@ -173,18 +173,18 @@ http_send_header(http_connection_t *hc, int rc, const char *content, tm = gmtime_r(&t, &tm0); htsbuf_qprintf(&hdrs, - "Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n", - cachedays[tm->tm_wday], tm->tm_year + 1900, - cachemonths[tm->tm_mon], tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + "Last-Modified: %s, %d %s %02d %02d:%02d:%02d GMT\r\n", + cachedays[tm->tm_wday], tm->tm_mday, + cachemonths[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec); t += maxage; tm = gmtime_r(&t, &tm0); htsbuf_qprintf(&hdrs, - "Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n", - cachedays[tm->tm_wday], tm->tm_year + 1900, - cachemonths[tm->tm_mon], tm->tm_mday, + "Expires: %s, %d %s %02d %02d:%02d:%02d GMT\r\n", + cachedays[tm->tm_wday], tm->tm_mday, + cachemonths[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec); htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage); diff --git a/src/iconserve.c b/src/iconserve.c new file mode 100644 index 00000000..85f0fe04 --- /dev/null +++ b/src/iconserve.c @@ -0,0 +1,358 @@ +/* + * Icon file server operations + * Copyright (C) 2012 Andy Brown + * + * 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 . + */ + +#define CURL_STATICLIB +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "tvheadend.h" +#include "channels.h" +#include "http.h" +#include "webui/webui.h" +#include "filebundle.h" +#include "iconserve.h" +#include "config2.h" +#include "queue.h" +#include "spawn.h" + +/* Queue, Cond to signal data and Mutex to protect it */ +static TAILQ_HEAD(,iconserve_grab_queue) iconserve_queue; +static pthread_mutex_t iconserve_mutex; +static pthread_cond_t iconserve_cond; + +/** + * https://github.com/andyb2000 Function to provide local icon files + */ +int +page_logo(http_connection_t *hc, const char *remain, void *opaque) +{ + const char *homedir = hts_settings_get_root(); + channel_t *ch = NULL; + char *inpath, *inpath2; + const char *outpath = "none"; + char homepath[254]; + char iconpath[100]; + pthread_mutex_lock(&global_lock); + fb_file *fp; + ssize_t size; + char buf[4096]; + char *mimetest_outbuf; + + if(remain == NULL) { + pthread_mutex_unlock(&global_lock); + return 404; + }; + + if(strstr(remain, "..")) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_BAD_REQUEST; + }; + + ch = channel_find_by_identifier(atoi(remain)); + if (ch == NULL || ch->ch_icon == NULL) { + pthread_mutex_unlock(&global_lock); + return 404; + }; + + snprintf(homepath, sizeof(homepath), "%s/icons", homedir); + inpath = NULL; + inpath2 = NULL; + outpath = NULL; + /* split icon to last component */ + inpath = strdup(ch->ch_icon); + inpath2 = strtok(inpath, "/"); + while (inpath2 != NULL) { + inpath2 = strtok(NULL, "/"); + if (inpath2 != NULL) { + outpath = strdup(inpath2); + }; + }; + snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath); + fp = fb_open(iconpath, 1, 0); + if (!fp) { + tvhlog(LOG_DEBUG, "page_logo", + "failed to open %s redirecting to http link for icon (%s)", + iconpath, ch->ch_icon); + http_redirect(hc, ch->ch_icon); + iconserve_queue_add ( ch->ch_id, ch->ch_icon ); + } else { + tvhlog(LOG_DEBUG, "page_logo", "File %s opened", iconpath); + size = fb_size(fp); + mimetest_outbuf = strdup("image/jpeg"); + http_send_header(hc, 200, mimetest_outbuf, size, NULL, NULL, 300, 0, NULL); + while (!fb_eof(fp)) { + ssize_t c = fb_read(fp, buf, sizeof(buf)); + if (c < 0) { + break; + }; + if (write(hc->hc_fd, buf, c) != c) { + break; + }; + }; + fb_close(fp); + }; + + pthread_mutex_unlock(&global_lock); + return 0; +} + +/* +* Logo loader functions, called from http htsp +* Will return local cache url instead of icon stored +*/ +const char +*logo_query(int ch_id, const char *ch_icon) +{ + const char *setting = config_get_iconserve(); + const char *serverip = config_get_serverip(); + char outiconpath[255]; + char *return_icon = strdup(ch_icon); + + if (!setting || !*setting || (strcmp(setting, "off") == 0)) { + return return_icon; + }; + + if (!serverip || !*serverip) { + return return_icon; + }; + + snprintf(outiconpath, sizeof(outiconpath), + "http://%s:%d/channellogo/%d", serverip, webui_port, ch_id); + return_icon = strdup(outiconpath); +return return_icon; +}; + +/* + * Icon grabber queue thread + */ +void *iconserve_thread ( void *aux ) +{ + iconserve_grab_queue_t *qe; + pthread_mutex_lock(&iconserve_mutex); + char *inpath, *inpath2; + const char *header_parse = NULL, *header_maxage = NULL; + const char *outpath = "none"; + CURL *curl; + FILE *curl_fp, *curl_fp_header; + CURLcode res; + fb_file *fp; + char iconpath[100], iconpath_header[100]; + char homepath[254]; + const char *homedir = hts_settings_get_root(); + struct stat fileStat; + int trigger_download = 0; + char buf[256]; + int file = 0; + time_t seconds; + int dif, compare_seconds, rc; + const char *periodicdownload = config_get_iconserve_periodicdownload(); + struct timespec timertrigger; + channel_t *ch; + + tvhlog(LOG_INFO, "iconserve_thread", "Thread startup"); + curl = curl_easy_init(); + snprintf(homepath, sizeof(homepath), "%s/icons", homedir); + if(stat(homepath, &fileStat) == 0 || mkdir(homepath, 0700) == 0) { + if (curl) { + while (1) { + + /* Get entry from queue */ + qe = TAILQ_FIRST(&iconserve_queue); + /* Check for queue data */ + if (!qe) { /* Queue Empty */ + periodicdownload = config_get_iconserve_periodicdownload(); + if (!periodicdownload || !*periodicdownload || + (strcmp(periodicdownload, "off") == 0)) { + tvhlog(LOG_DEBUG, "iconserve_thread", "Non-timer wakeup"); + rc = pthread_cond_wait(&iconserve_cond, &iconserve_mutex); + } else { + tvhlog(LOG_DEBUG, "iconserve_thread", "Timer wakeup set"); + timertrigger.tv_sec = time(NULL) + 86400; + timertrigger.tv_nsec = 0; + rc = pthread_cond_timedwait(&iconserve_cond, + &iconserve_mutex, &timertrigger); + }; + if (rc == ETIMEDOUT) { + tvhlog(LOG_INFO, "iconserve_thread", "Thread wakeup by timer"); + RB_FOREACH(ch, &channel_name_tree, ch_name_link) { + if (ch->ch_icon != NULL) { + iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t)); + qe->chan_number = ch->ch_id; + qe->icon_url = ch->ch_icon; + TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link); + }; + }; + }; + continue; + } + TAILQ_REMOVE(&iconserve_queue, qe, iconserve_link); + pthread_mutex_unlock(&iconserve_mutex); + + inpath = NULL; + inpath2 = NULL; + outpath = NULL; + curl_fp = NULL; + /* split icon to last component */ + inpath = strdup(qe->icon_url); + inpath2 = strtok(inpath, "/"); + while (inpath2 != NULL) { + inpath2 = strtok(NULL, "/"); + if (inpath2 != NULL) + outpath = strdup(inpath2); + }; + if (outpath != NULL) { + snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath); + snprintf(iconpath_header, sizeof(iconpath_header), "%s/%s.head", + homepath, outpath); + fp = fb_open(iconpath, 0, 1); + if (!fp) { + /* No file exists so grab immediately */ + tvhlog(LOG_INFO, "logo_loader", "No logo, downloading file %s", outpath); + trigger_download = 1; + } else { + /* File exists so compare expiry times to re-grab */ + fb_close(fp); + fp = fb_open(iconpath_header, 0, 0); + while (!fb_eof(fp)) { + memset(buf, 0, sizeof(buf)); + if (!fb_gets(fp, buf, sizeof(buf) - 1)) break; + if (buf[strlen(buf) - 1] == '\n') { + buf[strlen(buf) - 1] = '\0'; + }; + if(strstr(buf, "Cache-Control: ")) { + header_parse = strtok(buf, "="); + header_parse = strtok ( NULL, "="); + header_maxage = strdup(header_parse); + }; + }; + fb_close(fp); + file=open(iconpath, O_RDONLY); + fstat(file,&fileStat); + seconds = time (NULL); + dif = difftime (seconds,fileStat.st_mtime); + compare_seconds=atoi(header_maxage); + if (dif > compare_seconds) { + tvhlog(LOG_DEBUG, "logo_loader", "Logo expired, downloading %s", outpath); + trigger_download = 1; + } else { + tvhlog(LOG_INFO, "logo_loader", "Logo not expired %s", outpath); + }; + close(file); + }; + if (trigger_download == 1) { + curl_fp=fopen(iconpath,"wb"); + curl_fp_header=fopen(iconpath_header,"w"); + curl_easy_setopt(curl, CURLOPT_URL, qe->icon_url); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_fp); + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, curl_fp_header); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend"); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + res = curl_easy_perform(curl); + if (res == 0) { + tvhlog(LOG_INFO, "logo_loader", "Downloaded icon via curl (%s)", + qe->icon_url); + } else { + tvhlog(LOG_WARNING, "logo_loader", "Error with curl download (%s)", + qe->icon_url); + }; + fclose(curl_fp); + fclose(curl_fp_header); + trigger_download = 0; + }; + }; + }; /* while loop */ + curl_easy_cleanup(curl); + } else { + tvhlog(LOG_WARNING, "iconserve", "CURL cannot initialise"); + }; + }; + return NULL; +}; + +/* + * Add data to the queue + */ +void iconserve_queue_add ( int chan_number, char *icon_url ) +{ + /* Create entry */ + tvhlog(LOG_DEBUG, "iconserve_queue_add", "Adding chan_number to queue: %i", + chan_number); + iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t)); + qe->chan_number = chan_number; + qe->icon_url = strdup(icon_url); + + pthread_mutex_lock(&iconserve_mutex); + TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link); + pthread_cond_signal(&iconserve_cond); + pthread_mutex_unlock(&iconserve_mutex); +} + + +/** + * Loader for icons, check config params and pull them in one go + */ +void +logo_loader(void) +{ + channel_t *ch; + const char *setting = config_get_iconserve(); + const char *serverip = config_get_serverip(); + + if (!setting || !*setting || (strcmp(setting, "off") == 0)) { + tvhlog(LOG_DEBUG, "logo_loader", "Disabled by config, skipping"); + return; + }; + + if (!serverip || !*serverip) { + tvhlog(LOG_ALERT, "logo_loader", "No server IP, skipping icon cache"); + return; + }; + + + pthread_t tid; + pthread_mutex_init(&iconserve_mutex, NULL); + pthread_cond_init(&iconserve_cond, NULL); + TAILQ_INIT(&iconserve_queue); + /* Start thread - presumably permanently active */ + pthread_create(&tid, NULL, iconserve_thread, NULL); // last param is passed as aux + // as this is single global + // you can probably use global + // vars + + tvhlog(LOG_INFO, "logo_loader", "Caching logos locally"); + /* loop through channels and load logo files */ + RB_FOREACH(ch, &channel_name_tree, ch_name_link) { + if (ch->ch_icon != NULL) { + iconserve_queue_add ( ch->ch_id, ch->ch_icon ); + }; + }; + pthread_mutex_lock(&iconserve_mutex); + pthread_cond_signal(&iconserve_cond); // tell thread data is available + pthread_mutex_unlock(&iconserve_mutex); +}; diff --git a/src/iconserve.h b/src/iconserve.h new file mode 100644 index 00000000..ff8a0f92 --- /dev/null +++ b/src/iconserve.h @@ -0,0 +1,47 @@ +/* + * Icon file serve operations + * Copyright (C) 2012 Andy Brown + * + * 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 ICONSERVE_H +#define ICONSERVE_H + +#include "http.h" + +/* Struct of entries for icon grabbing + * FIELD: chan_number + * FIELD: icon url + */ +typedef struct iconserve_grab_queue +{ + TAILQ_ENTRY(iconserve_grab_queue) iconserve_link; + int chan_number; + char *icon_url; +} iconserve_grab_queue_t; + + +int page_logo(http_connection_t *hc, const char *remain, void *opaque); +size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream); + +void *iconserve_thread ( void *aux ); + +const char *logo_query(int ch_id, const char *ch_icon); + +void iconserve_queue_add ( int chan_number, char *icon_url ); + +void logo_loader(void); + +#endif /* ICONSERVE_H */ diff --git a/src/main.c b/src/main.c index 73afb075..1bb3b509 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,7 @@ #include "ffdecsa/FFdecsa.h" #include "muxes.h" #include "config2.h" +#include "iconserve.h" int running; time_t dispatch_clock; @@ -488,6 +489,8 @@ main(int argc, char **argv) http_server_init(); webui_init(); + logo_loader(); + serviceprobe_init(); #if ENABLE_CWC diff --git a/src/webui/extjs.c b/src/webui/extjs.c index bf254d03..6999eacf 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -47,6 +47,7 @@ #include "config2.h" #include "lang_codes.h" #include "subscriptions.h" +#include "iconserve.h" /** * @@ -858,7 +859,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(m, "channel", ch->ch_name); htsmsg_add_u32(m, "channelid", ch->ch_id); if(ch->ch_icon != NULL) - htsmsg_add_str(m, "chicon", ch->ch_icon); + htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon)); if((s = epg_episode_get_title(ee, lang))) htsmsg_add_str(m, "title", s); @@ -940,7 +941,7 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque) m = htsmsg_create_map(); htsmsg_add_u32(m, "id", ebc->id); if ( ch->ch_name ) htsmsg_add_str(m, "channel", ch->ch_name); - if ( ch->ch_icon ) htsmsg_add_str(m, "chicon", ch->ch_icon); + if ( ch->ch_icon ) htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon)); htsmsg_add_u32(m, "start", ebc->start); htsmsg_add_msg(array, NULL, m); } @@ -1340,7 +1341,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, if(de->de_channel != NULL) { htsmsg_add_str(m, "channel", de->de_channel->ch_name); if(de->de_channel->ch_icon != NULL) - htsmsg_add_str(m, "chicon", de->de_channel->ch_icon); + htsmsg_add_str(m, "chicon", logo_query(de->de_channel->ch_id, de->de_channel->ch_icon)); } htsmsg_add_str(m, "config_name", de->de_config_name); @@ -1930,7 +1931,23 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) save |= config_set_muxconfpath(str); if ((str = http_arg_get(&hc->hc_req_args, "language"))) save |= config_set_language(str); + str = http_arg_get(&hc->hc_req_args, "iconserve"); + if (str != NULL) { + save |= config_set_iconserve(str); + } else { + save |= config_set_iconserve("off"); + }; + str = http_arg_get(&hc->hc_req_args, "iconserve_periodicdownload"); + if (str != NULL) { + save |= config_set_iconserve_periodicdownload(str); + } else { + save |= config_set_iconserve_periodicdownload("off"); + }; + if ((str = http_arg_get(&hc->hc_req_args, "serverip"))) + save |= config_set_serverip(str); if (save) config_save(); + /* trigger the iconserve init routine */ + logo_loader(); pthread_mutex_unlock(&global_lock); out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index 0950fd72..faabaf8d 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -482,7 +482,5 @@ simpleui_start(void) http_path_add("/simple.html", NULL, page_simple, ACCESS_SIMPLE); http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE); http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE); - http_path_add("/status.xml", NULL, page_status, ACCESS_SIMPLE); + http_path_add("/status.xml", NULL, page_status, ACCESS_SIMPLE); } - - diff --git a/src/webui/statedump.c b/src/webui/statedump.c index 603fd158..29425143 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -30,6 +30,7 @@ #include "epg.h" #include "psi.h" #include "channels.h" +#include "iconserve.h" #if ENABLE_LINUXDVB #include "dvr/dvr.h" #include "dvb/dvb.h" @@ -72,7 +73,7 @@ dumpchannels(htsbuf_queue_t *hq) ch->ch_refcount, ch->ch_zombie, ch->ch_number, - ch->ch_icon ?: ""); + logo_query(ch->ch_id, ch->ch_icon) ?: ""); } } diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index 2c9b6a86..a862e0bb 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -37,7 +37,7 @@ tvheadend.miscconf = function() { */ var confreader = new Ext.data.JsonReader({ root : 'config' - }, [ 'muxconfpath', 'language' ]); + }, [ 'muxconfpath', 'language', 'iconserve', 'serverip' ]); /* **************************************************************** * Form Fields @@ -50,6 +50,22 @@ tvheadend.miscconf = function() { width: 400 }); + var iconServeConfig = new Ext.form.Checkbox({ + name : 'iconserve', + fieldLabel : 'Cache channel icons' + }); + var iconPeriodicDownload = new Ext.form.Checkbox({ + name : 'iconserve_periodicdownload', + fieldLabel : 'Periodically check for updated icons' + }); + var serveripConfig = new Ext.form.TextField({ + fieldLabel : 'TVH Server IP address', + name : 'serverip', + allowBlank : true, + width: 150 + }); + + var language = new Ext.ux.ItemSelector({ name: 'language', fromStore: tvheadend.languages, @@ -95,7 +111,7 @@ tvheadend.miscconf = function() { layout : 'form', defaultType : 'textfield', autoHeight : true, - items : [ language, dvbscanPath ], + items : [ language, dvbscanPath, iconServeConfig, iconPeriodicDownload, serveripConfig ], tbar : [ saveButton, '->', helpButton ] }); diff --git a/src/webui/webui.c b/src/webui/webui.c index 0f3560c5..6376c812 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -44,6 +44,7 @@ #include "muxer.h" #include "dvb/dvb.h" #include "dvb/dvb_support.h" +#include "iconserve.h" /** * @@ -95,7 +96,7 @@ page_root2(http_connection_t *hc, const char *remain, void *opaque) /** * Static download of a file from the filesystem */ -static int +int page_static_file(http_connection_t *hc, const char *remain, void *opaque) { int ret = 0; @@ -909,6 +910,8 @@ webui_init(void) http_path_add("/stream", NULL, http_stream, ACCESS_STREAMING); + http_path_add("/channellogo", NULL, page_logo, ACCESS_ANONYMOUS); + webui_static_content("/static", "src/webui/static"); webui_static_content("/docs", "docs/html"); webui_static_content("/docresources", "docs/docresources"); diff --git a/src/webui/webui.h b/src/webui/webui.h index cbbec569..0d50b554 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -20,6 +20,7 @@ #define WEBUI_H_ #include "htsmsg.h" +#include "http.h" void webui_init(void); @@ -30,6 +31,8 @@ void extjs_start(void); size_t html_escaped_len(const char *src); const char* html_escape(char *dst, const char *src, size_t len); +int page_static_file(http_connection_t *hc, const char *remain, void *opaque); + #if ENABLE_LINUXDVB void extjs_list_dvb_adapters(htsmsg_t *array); void extjs_start_dvb(void); @@ -54,5 +57,4 @@ void comet_mailbox_add_message(htsmsg_t *m, int isdebug); void comet_flush(void); - #endif /* WEBUI_H_ */ From 93fe784960b6635a00bc581948f6d614afbbcf08 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 30 Dec 2012 20:09:27 +0000 Subject: [PATCH 177/503] [PR-174] - Replaced user submitted icon cache with more generic image cache. This allows file:// paths to be specified for channel icons even if image cache support is disabled. The image cache functionality is compile time optional (for those without curl support) and also run-time configurable for those that don't want it. All images, including EPG ones should be cached. --- Makefile | 5 +- configure | 13 +- src/channels.c | 3 + src/config2.c | 47 ---- src/config2.h | 12 - src/epg.c | 19 +- src/htsp_server.c | 44 +++- src/iconserve.c | 358 --------------------------- src/iconserve.h | 47 ---- src/imagecache.c | 440 +++++++++++++++++++++++++++++++++ src/imagecache.h | 57 +++++ src/main.c | 9 +- src/settings.c | 28 ++- src/settings.h | 2 + src/webui/extjs.c | 62 +++-- src/webui/statedump.c | 3 +- src/webui/static/app/config.js | 57 +++-- src/webui/webui.c | 44 +++- 18 files changed, 713 insertions(+), 537 deletions(-) delete mode 100644 src/iconserve.c delete mode 100644 src/iconserve.h create mode 100644 src/imagecache.c create mode 100644 src/imagecache.h diff --git a/Makefile b/Makefile index c63cbe58..63629f4a 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ CFLAGS += -Wmissing-prototypes -fms-extensions CFLAGS += -g -funsigned-char -O2 CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} -LDFLAGS += -lrt -ldl -lpthread -lm -lcurl +LDFLAGS += -lrt -ldl -lpthread -lm # # Other config @@ -109,6 +109,7 @@ SRCS = src/main.c \ src/config2.c \ src/lang_codes.c \ src/lang_str.c \ + src/imagecache.c SRCS += src/epggrab/module.c\ src/epggrab/channel.c\ @@ -140,8 +141,6 @@ SRCS += src/muxer.c \ src/muxer_pass.c \ src/muxer_tvh.c \ -SRCS += src/iconserve.c \ - # # Optional code # diff --git a/configure b/configure index 8e02c1b4..765a9898 100755 --- a/configure +++ b/configure @@ -20,6 +20,7 @@ OPTIONS=( "v4l:yes" "linuxdvb:yes" "dvbscan:yes" + "imagecache:auto" "avahi:auto" "zlib:auto" "bundle:no" @@ -126,7 +127,17 @@ if enabled cwc && enabled dvbcsa; then die "Failed to find dvbcsa support (use --disable-dvbcsa)" LDFLAGS="$LDFLAGS -ldvbcsa" fi - + +# +# Icon caching +# +if enabled_or_auto imagecache; then + if check_pkg libcurl; then + enable imagecache + elif enabled imagecache; then + die "Libcurl support not found (use --disable-imagecache)" + fi +fi # ########################################################################### # Write config diff --git a/src/channels.c b/src/channels.c index 7c8112ec..970da8e6 100644 --- a/src/channels.c +++ b/src/channels.c @@ -39,6 +39,7 @@ #include "notify.h" #include "dvr/dvr.h" #include "htsp_server.h" +#include "imagecache.h" struct channel_tree channel_name_tree; static struct channel_tree channel_identifier_tree; @@ -268,6 +269,7 @@ channel_load_one(htsmsg_t *c, int id) epggrab_channel_add(ch); tvh_str_update(&ch->ch_icon, htsmsg_get_str(c, "icon")); + imagecache_get_id(ch->ch_icon); htsmsg_get_s32(c, "dvr_extra_time_pre", &ch->ch_dvr_extra_time_pre); htsmsg_get_s32(c, "dvr_extra_time_post", &ch->ch_dvr_extra_time_post); @@ -452,6 +454,7 @@ channel_set_icon(channel_t *ch, const char *icon) free(ch->ch_icon); ch->ch_icon = strdup(icon); + imagecache_get_id(icon); channel_save(ch); htsp_channel_update(ch); } diff --git a/src/config2.c b/src/config2.c index b90db4fe..5bcbd0d1 100644 --- a/src/config2.c +++ b/src/config2.c @@ -74,50 +74,3 @@ int config_set_muxconfpath ( const char *path ) } return 0; } - -const char *config_get_iconserve ( void ) -{ - return htsmsg_get_str(config, "iconserve"); -} - -int config_set_iconserve ( const char *setting ) -{ - const char *c = config_get_iconserve(); - if (!c || strcmp(c, setting)) { - if (c) htsmsg_delete_field(config, "iconserve"); - htsmsg_add_str(config, "iconserve", setting); - return 1; - } - return 0; -} -const char *config_get_iconserve_periodicdownload ( void ) -{ - return htsmsg_get_str(config, "iconserve_periodicdownload"); -} - -int config_set_iconserve_periodicdownload ( const char *setting ) -{ - const char *c = config_get_iconserve_periodicdownload(); - if (!c || strcmp(c, setting)) { - if (c) htsmsg_delete_field(config, "iconserve_periodicdownload"); - htsmsg_add_str(config, "iconserve_periodicdownload", setting); - return 1; - } - return 0; -} - -const char *config_get_serverip ( void ) -{ - return htsmsg_get_str(config, "serverip"); -}; - -int config_set_serverip ( const char *setting ) -{ - const char *c = config_get_serverip(); - if (!c || strcmp(c, setting)) { - if (c) htsmsg_delete_field(config, "serverip"); - htsmsg_add_str(config, "serverip", setting); - return 1; - } - return 0; -}; diff --git a/src/config2.h b/src/config2.h index 6e90be6f..cd68e306 100644 --- a/src/config2.h +++ b/src/config2.h @@ -32,18 +32,6 @@ const char *config_get_muxconfpath ( void ); int config_set_muxconfpath ( const char *str ) __attribute__((warn_unused_result)); -const char *config_get_iconserve ( void ); -int config_set_iconserve ( const char *str ) - __attribute__((warn_unused_result)); - -const char *config_get_iconserve_periodicdownload ( void ); -int config_set_iconserve_periodicdownload ( const char *str ) - __attribute__((warn_unused_result)); - -const char *config_get_serverip ( void ); -int config_set_serverip ( const char *str ) - __attribute__((warn_unused_result)); - const char *config_get_language ( void ); int config_set_language ( const char *str ) __attribute__((warn_unused_result)); diff --git a/src/epg.c b/src/epg.c index fafb65dd..6984fa0e 100644 --- a/src/epg.c +++ b/src/epg.c @@ -32,6 +32,7 @@ #include "dvr/dvr.h" #include "htsp_server.h" #include "epggrab.h" +#include "imagecache.h" /* Broadcast hashing */ #define EPG_HASH_WIDTH 1024 @@ -459,8 +460,12 @@ int epg_brand_set_summary int epg_brand_set_image ( epg_brand_t *brand, const char *image, epggrab_module_t *src ) { + int save; if (!brand || !image) return 0; - return _epg_object_set_str(brand, &brand->image, image, src); + save = _epg_object_set_str(brand, &brand->image, image, src); + if (save) + imagecache_get_id(image); + return save; } int epg_brand_set_season_count @@ -628,8 +633,12 @@ int epg_season_set_summary int epg_season_set_image ( epg_season_t *season, const char *image, epggrab_module_t *src ) { + int save; if (!season || !image) return 0; - return _epg_object_set_str(season, &season->image, image, src); + save = _epg_object_set_str(season, &season->image, image, src); + if (save) + imagecache_get_id(image); + return save; } int epg_season_set_episode_count @@ -891,8 +900,12 @@ int epg_episode_set_description int epg_episode_set_image ( epg_episode_t *episode, const char *image, epggrab_module_t *src ) { + int save; if (!episode || !image) return 0; - return _epg_object_set_str(episode, &episode->image, image, src); + save = _epg_object_set_str(episode, &episode->image, image, src); + if (save) + imagecache_get_id(image); + return save; } int epg_episode_set_number diff --git a/src/htsp_server.c b/src/htsp_server.c index ba10139a..243ff77e 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -42,8 +42,7 @@ #include "htsmsg_binary.h" #include "epg.h" #include "plumbing/tsfix.h" -#include "iconserve.h" -#include "config2.h" +#include "imagecache.h" #include #include "settings.h" @@ -434,7 +433,7 @@ htsp_file_destroy(htsp_file_t *hf) * */ static htsmsg_t * -htsp_build_channel(channel_t *ch, const char *method) +htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) { channel_tag_mapping_t *ctm; channel_tag_t *ct; @@ -449,10 +448,26 @@ htsp_build_channel(channel_t *ch, const char *method) htsmsg_add_u32(out, "channelNumber", ch->ch_number); htsmsg_add_str(out, "channelName", ch->ch_name); - if(ch->ch_icon != NULL) { - htsmsg_add_str(out, "channelIcon", logo_query(ch->ch_id, ch->ch_icon)); - }; + uint32_t id = imagecache_get_id(ch->ch_icon); + if (id) { + size_t p = 0; + char url[256]; + if (htsp->htsp_version <= 7) { + strcpy(url, "http://"); + p = 7; + inet_ntop(AF_INET, &(htsp->htsp_peer->sin_addr), url+p, sizeof(url)-p); + p = strlen(url); + p += snprintf(url+p, sizeof(url)-p, ":%hd", webui_port); + } + if (tvheadend_webroot) + p += snprintf(url+p, sizeof(url)-p, "%s", tvheadend_webroot); + snprintf(url+p, sizeof(url)-p, "/imagecache/%d", id); + htsmsg_add_str(out, "channelIcon", url); + } else { + htsmsg_add_str(out, "channelIcon", ch->ch_icon); + } + } now = ch->ch_epg_now; next = ch->ch_epg_next; @@ -802,7 +817,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in) /* Send all channels */ RB_FOREACH(ch, &channel_name_tree, ch_name_link) - htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd"), NULL); + htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL); /* Send all enabled and external tags (now with channel mappings) */ TAILQ_FOREACH(ct, &channel_tags, ct_link) @@ -1880,23 +1895,30 @@ htsp_channel_update_current(channel_t *ch) /** * Called from channel.c when a new channel is created */ +static void +_htsp_channel_update(channel_t *ch, const char *msg) +{ + htsp_connection_t *htsp; + LIST_FOREACH(htsp, &htsp_async_connections, htsp_async_link) + if (htsp->htsp_async_mode & HTSP_ASYNC_ON) + htsp_send_message(htsp, htsp_build_channel(ch, msg, htsp), NULL); +} + void htsp_channel_add(channel_t *ch) { - htsp_async_send(htsp_build_channel(ch, "channelAdd"), HTSP_ASYNC_ON); + _htsp_channel_update(ch, "channelAdd"); } - /** * Called from channel.c when a channel is updated */ void htsp_channel_update(channel_t *ch) { - htsp_async_send(htsp_build_channel(ch, "channelUpdate"), HTSP_ASYNC_ON); + _htsp_channel_update(ch, "channelUpdate"); } - /** * Called from channel.c when a channel is deleted */ diff --git a/src/iconserve.c b/src/iconserve.c deleted file mode 100644 index 85f0fe04..00000000 --- a/src/iconserve.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Icon file server operations - * Copyright (C) 2012 Andy Brown - * - * 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 . - */ - -#define CURL_STATICLIB -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "settings.h" -#include "tvheadend.h" -#include "channels.h" -#include "http.h" -#include "webui/webui.h" -#include "filebundle.h" -#include "iconserve.h" -#include "config2.h" -#include "queue.h" -#include "spawn.h" - -/* Queue, Cond to signal data and Mutex to protect it */ -static TAILQ_HEAD(,iconserve_grab_queue) iconserve_queue; -static pthread_mutex_t iconserve_mutex; -static pthread_cond_t iconserve_cond; - -/** - * https://github.com/andyb2000 Function to provide local icon files - */ -int -page_logo(http_connection_t *hc, const char *remain, void *opaque) -{ - const char *homedir = hts_settings_get_root(); - channel_t *ch = NULL; - char *inpath, *inpath2; - const char *outpath = "none"; - char homepath[254]; - char iconpath[100]; - pthread_mutex_lock(&global_lock); - fb_file *fp; - ssize_t size; - char buf[4096]; - char *mimetest_outbuf; - - if(remain == NULL) { - pthread_mutex_unlock(&global_lock); - return 404; - }; - - if(strstr(remain, "..")) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - }; - - ch = channel_find_by_identifier(atoi(remain)); - if (ch == NULL || ch->ch_icon == NULL) { - pthread_mutex_unlock(&global_lock); - return 404; - }; - - snprintf(homepath, sizeof(homepath), "%s/icons", homedir); - inpath = NULL; - inpath2 = NULL; - outpath = NULL; - /* split icon to last component */ - inpath = strdup(ch->ch_icon); - inpath2 = strtok(inpath, "/"); - while (inpath2 != NULL) { - inpath2 = strtok(NULL, "/"); - if (inpath2 != NULL) { - outpath = strdup(inpath2); - }; - }; - snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath); - fp = fb_open(iconpath, 1, 0); - if (!fp) { - tvhlog(LOG_DEBUG, "page_logo", - "failed to open %s redirecting to http link for icon (%s)", - iconpath, ch->ch_icon); - http_redirect(hc, ch->ch_icon); - iconserve_queue_add ( ch->ch_id, ch->ch_icon ); - } else { - tvhlog(LOG_DEBUG, "page_logo", "File %s opened", iconpath); - size = fb_size(fp); - mimetest_outbuf = strdup("image/jpeg"); - http_send_header(hc, 200, mimetest_outbuf, size, NULL, NULL, 300, 0, NULL); - while (!fb_eof(fp)) { - ssize_t c = fb_read(fp, buf, sizeof(buf)); - if (c < 0) { - break; - }; - if (write(hc->hc_fd, buf, c) != c) { - break; - }; - }; - fb_close(fp); - }; - - pthread_mutex_unlock(&global_lock); - return 0; -} - -/* -* Logo loader functions, called from http htsp -* Will return local cache url instead of icon stored -*/ -const char -*logo_query(int ch_id, const char *ch_icon) -{ - const char *setting = config_get_iconserve(); - const char *serverip = config_get_serverip(); - char outiconpath[255]; - char *return_icon = strdup(ch_icon); - - if (!setting || !*setting || (strcmp(setting, "off") == 0)) { - return return_icon; - }; - - if (!serverip || !*serverip) { - return return_icon; - }; - - snprintf(outiconpath, sizeof(outiconpath), - "http://%s:%d/channellogo/%d", serverip, webui_port, ch_id); - return_icon = strdup(outiconpath); -return return_icon; -}; - -/* - * Icon grabber queue thread - */ -void *iconserve_thread ( void *aux ) -{ - iconserve_grab_queue_t *qe; - pthread_mutex_lock(&iconserve_mutex); - char *inpath, *inpath2; - const char *header_parse = NULL, *header_maxage = NULL; - const char *outpath = "none"; - CURL *curl; - FILE *curl_fp, *curl_fp_header; - CURLcode res; - fb_file *fp; - char iconpath[100], iconpath_header[100]; - char homepath[254]; - const char *homedir = hts_settings_get_root(); - struct stat fileStat; - int trigger_download = 0; - char buf[256]; - int file = 0; - time_t seconds; - int dif, compare_seconds, rc; - const char *periodicdownload = config_get_iconserve_periodicdownload(); - struct timespec timertrigger; - channel_t *ch; - - tvhlog(LOG_INFO, "iconserve_thread", "Thread startup"); - curl = curl_easy_init(); - snprintf(homepath, sizeof(homepath), "%s/icons", homedir); - if(stat(homepath, &fileStat) == 0 || mkdir(homepath, 0700) == 0) { - if (curl) { - while (1) { - - /* Get entry from queue */ - qe = TAILQ_FIRST(&iconserve_queue); - /* Check for queue data */ - if (!qe) { /* Queue Empty */ - periodicdownload = config_get_iconserve_periodicdownload(); - if (!periodicdownload || !*periodicdownload || - (strcmp(periodicdownload, "off") == 0)) { - tvhlog(LOG_DEBUG, "iconserve_thread", "Non-timer wakeup"); - rc = pthread_cond_wait(&iconserve_cond, &iconserve_mutex); - } else { - tvhlog(LOG_DEBUG, "iconserve_thread", "Timer wakeup set"); - timertrigger.tv_sec = time(NULL) + 86400; - timertrigger.tv_nsec = 0; - rc = pthread_cond_timedwait(&iconserve_cond, - &iconserve_mutex, &timertrigger); - }; - if (rc == ETIMEDOUT) { - tvhlog(LOG_INFO, "iconserve_thread", "Thread wakeup by timer"); - RB_FOREACH(ch, &channel_name_tree, ch_name_link) { - if (ch->ch_icon != NULL) { - iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t)); - qe->chan_number = ch->ch_id; - qe->icon_url = ch->ch_icon; - TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link); - }; - }; - }; - continue; - } - TAILQ_REMOVE(&iconserve_queue, qe, iconserve_link); - pthread_mutex_unlock(&iconserve_mutex); - - inpath = NULL; - inpath2 = NULL; - outpath = NULL; - curl_fp = NULL; - /* split icon to last component */ - inpath = strdup(qe->icon_url); - inpath2 = strtok(inpath, "/"); - while (inpath2 != NULL) { - inpath2 = strtok(NULL, "/"); - if (inpath2 != NULL) - outpath = strdup(inpath2); - }; - if (outpath != NULL) { - snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath); - snprintf(iconpath_header, sizeof(iconpath_header), "%s/%s.head", - homepath, outpath); - fp = fb_open(iconpath, 0, 1); - if (!fp) { - /* No file exists so grab immediately */ - tvhlog(LOG_INFO, "logo_loader", "No logo, downloading file %s", outpath); - trigger_download = 1; - } else { - /* File exists so compare expiry times to re-grab */ - fb_close(fp); - fp = fb_open(iconpath_header, 0, 0); - while (!fb_eof(fp)) { - memset(buf, 0, sizeof(buf)); - if (!fb_gets(fp, buf, sizeof(buf) - 1)) break; - if (buf[strlen(buf) - 1] == '\n') { - buf[strlen(buf) - 1] = '\0'; - }; - if(strstr(buf, "Cache-Control: ")) { - header_parse = strtok(buf, "="); - header_parse = strtok ( NULL, "="); - header_maxage = strdup(header_parse); - }; - }; - fb_close(fp); - file=open(iconpath, O_RDONLY); - fstat(file,&fileStat); - seconds = time (NULL); - dif = difftime (seconds,fileStat.st_mtime); - compare_seconds=atoi(header_maxage); - if (dif > compare_seconds) { - tvhlog(LOG_DEBUG, "logo_loader", "Logo expired, downloading %s", outpath); - trigger_download = 1; - } else { - tvhlog(LOG_INFO, "logo_loader", "Logo not expired %s", outpath); - }; - close(file); - }; - if (trigger_download == 1) { - curl_fp=fopen(iconpath,"wb"); - curl_fp_header=fopen(iconpath_header,"w"); - curl_easy_setopt(curl, CURLOPT_URL, qe->icon_url); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_fp); - curl_easy_setopt(curl, CURLOPT_WRITEHEADER, curl_fp_header); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend"); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - res = curl_easy_perform(curl); - if (res == 0) { - tvhlog(LOG_INFO, "logo_loader", "Downloaded icon via curl (%s)", - qe->icon_url); - } else { - tvhlog(LOG_WARNING, "logo_loader", "Error with curl download (%s)", - qe->icon_url); - }; - fclose(curl_fp); - fclose(curl_fp_header); - trigger_download = 0; - }; - }; - }; /* while loop */ - curl_easy_cleanup(curl); - } else { - tvhlog(LOG_WARNING, "iconserve", "CURL cannot initialise"); - }; - }; - return NULL; -}; - -/* - * Add data to the queue - */ -void iconserve_queue_add ( int chan_number, char *icon_url ) -{ - /* Create entry */ - tvhlog(LOG_DEBUG, "iconserve_queue_add", "Adding chan_number to queue: %i", - chan_number); - iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t)); - qe->chan_number = chan_number; - qe->icon_url = strdup(icon_url); - - pthread_mutex_lock(&iconserve_mutex); - TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link); - pthread_cond_signal(&iconserve_cond); - pthread_mutex_unlock(&iconserve_mutex); -} - - -/** - * Loader for icons, check config params and pull them in one go - */ -void -logo_loader(void) -{ - channel_t *ch; - const char *setting = config_get_iconserve(); - const char *serverip = config_get_serverip(); - - if (!setting || !*setting || (strcmp(setting, "off") == 0)) { - tvhlog(LOG_DEBUG, "logo_loader", "Disabled by config, skipping"); - return; - }; - - if (!serverip || !*serverip) { - tvhlog(LOG_ALERT, "logo_loader", "No server IP, skipping icon cache"); - return; - }; - - - pthread_t tid; - pthread_mutex_init(&iconserve_mutex, NULL); - pthread_cond_init(&iconserve_cond, NULL); - TAILQ_INIT(&iconserve_queue); - /* Start thread - presumably permanently active */ - pthread_create(&tid, NULL, iconserve_thread, NULL); // last param is passed as aux - // as this is single global - // you can probably use global - // vars - - tvhlog(LOG_INFO, "logo_loader", "Caching logos locally"); - /* loop through channels and load logo files */ - RB_FOREACH(ch, &channel_name_tree, ch_name_link) { - if (ch->ch_icon != NULL) { - iconserve_queue_add ( ch->ch_id, ch->ch_icon ); - }; - }; - pthread_mutex_lock(&iconserve_mutex); - pthread_cond_signal(&iconserve_cond); // tell thread data is available - pthread_mutex_unlock(&iconserve_mutex); -}; diff --git a/src/iconserve.h b/src/iconserve.h deleted file mode 100644 index ff8a0f92..00000000 --- a/src/iconserve.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Icon file serve operations - * Copyright (C) 2012 Andy Brown - * - * 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 ICONSERVE_H -#define ICONSERVE_H - -#include "http.h" - -/* Struct of entries for icon grabbing - * FIELD: chan_number - * FIELD: icon url - */ -typedef struct iconserve_grab_queue -{ - TAILQ_ENTRY(iconserve_grab_queue) iconserve_link; - int chan_number; - char *icon_url; -} iconserve_grab_queue_t; - - -int page_logo(http_connection_t *hc, const char *remain, void *opaque); -size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream); - -void *iconserve_thread ( void *aux ); - -const char *logo_query(int ch_id, const char *ch_icon); - -void iconserve_queue_add ( int chan_number, char *icon_url ); - -void logo_loader(void); - -#endif /* ICONSERVE_H */ diff --git a/src/imagecache.c b/src/imagecache.c new file mode 100644 index 00000000..224c72cc --- /dev/null +++ b/src/imagecache.c @@ -0,0 +1,440 @@ +/* + * Icon file server operations + * Copyright (C) 2012 Andy Brown + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "tvheadend.h" +#include "filebundle.h" +#include "imagecache.h" +#include "queue.h" +#include "redblack.h" + +#if ENABLE_IMAGECACHE +#define CURL_STATICLIB +#include +#include +#endif + +// TODO: icon cache flushing? +// TODO: md5 validation? +// TODO: allow cache to be disabled by users + +/* + * Image metadata + */ +typedef struct imagecache_image +{ + int id; ///< Internal ID + const char *url; ///< Upstream URL + int failed; ///< Last update failed + time_t updated; ///< Last time the file was checked + enum { + IDLE, + QUEUED, + FETCHING + } state; ///< fetch status + + TAILQ_ENTRY(imagecache_image) q_link; ///< Fetch Q link + RB_ENTRY(imagecache_image) id_link; ///< Index by ID + RB_ENTRY(imagecache_image) url_link; ///< Index by URL +} imagecache_image_t; + +static int _imagecache_id; +static RB_HEAD(,imagecache_image) _imagecache_by_id; +static RB_HEAD(,imagecache_image) _imagecache_by_url; + +pthread_mutex_t imagecache_mutex; + +static void _imagecache_save ( imagecache_image_t *img ); + +#if ENABLE_IMAGECACHE +uint32_t imagecache_enabled; +uint32_t imagecache_ok_period; +uint32_t imagecache_fail_period; + +static pthread_cond_t _imagecache_cond; +static TAILQ_HEAD(, imagecache_image) _imagecache_queue; +static void _imagecache_add ( imagecache_image_t *img ); +static void* _imagecache_thread ( void *p ); +static int _imagecache_fetch ( imagecache_image_t *img ); +#endif + +static int _url_cmp ( void *a, void *b ) +{ + return strcmp(((imagecache_image_t*)a)->url, ((imagecache_image_t*)b)->url); +} + +static int _id_cmp ( void *a, void *b ) +{ + return ((imagecache_image_t*)a)->id - ((imagecache_image_t*)b)->id; +} + +/* + * Initialise + */ +void imagecache_init ( void ) +{ + htsmsg_t *m, *e; + htsmsg_field_t *f; + imagecache_image_t *img, *i; + const char *url; + uint32_t id; + + /* Init vars */ + _imagecache_id = 0; +#if ENABLE_IMAGECACHE + imagecache_enabled = 0; + imagecache_ok_period = 24 * 7; // weekly + imagecache_fail_period = 24; // daily +#endif + + /* Create threads */ + pthread_mutex_init(&imagecache_mutex, NULL); +#if ENABLE_IMAGECACHE + pthread_cond_init(&_imagecache_cond, NULL); + TAILQ_INIT(&_imagecache_queue); +#endif + + /* Load settings */ +#if ENABLE_IMAGECACHE + if ((m = hts_settings_load("imagecache/config"))) { + htsmsg_get_u32(m, "enabled", &imagecache_enabled); + htsmsg_get_u32(m, "ok_period", &imagecache_ok_period); + htsmsg_get_u32(m, "fail_period", &imagecache_fail_period); + htsmsg_destroy(m); + } +#endif + if ((m = hts_settings_load("imagecache/meta"))) { + HTSMSG_FOREACH(f, m) { + if (!(e = htsmsg_get_map_by_field(f))) continue; + if (!(id = atoi(f->hmf_name))) continue; + if (!(url = htsmsg_get_str(e, "url"))) continue; + img = calloc(1, sizeof(imagecache_image_t)); + img->id = id; + img->url = strdup(url); + img->updated = htsmsg_get_s64_or_default(e, "updated", 0); + i = RB_INSERT_SORTED(&_imagecache_by_url, img, url_link, _url_cmp); + if (i) { + hts_settings_remove("imagecache/meta/%d", id); + hts_settings_remove("imagecache/data/%d", id); + free(img); + continue; + } + i = RB_INSERT_SORTED(&_imagecache_by_id, img, id_link, _id_cmp); + assert(!i); + if (id > _imagecache_id) + _imagecache_id = id; +#if ENABLE_IMAGECACHE + if (!img->updated) + _imagecache_add(img); +#endif + } + htsmsg_destroy(m); + } + + /* Start threads */ +#if ENABLE_IMAGECACHE + { + pthread_t tid; + pthread_create(&tid, NULL, _imagecache_thread, NULL); + } +#endif +} + +/* + * Save settings + */ +#if ENABLE_IMAGECACHE +void imagecache_save ( void ) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_u32(m, "enabled", imagecache_enabled); + htsmsg_add_u32(m, "ok_period", imagecache_ok_period); + htsmsg_add_u32(m, "fail_period", imagecache_fail_period); + hts_settings_save(m, "imagecache/config"); +} + +/* + * Enable/disable + */ +int imagecache_set_enabled ( uint32_t e ) +{ + if (e == imagecache_enabled) + return 0; + imagecache_enabled = e; + if (e) + pthread_cond_broadcast(&_imagecache_cond); + return 1; +} + +/* + * Set ok period + */ +int imagecache_set_ok_period ( uint32_t p ) +{ + if (p == imagecache_ok_period) + return 0; + imagecache_ok_period = p; + return 1; +} + +/* + * Set fail period + */ +int imagecache_set_fail_period ( uint32_t p ) +{ + if (p == imagecache_fail_period) + return 0; + imagecache_fail_period = p; + return 1; +} +#endif + +/* + * Fetch a URLs ID + */ +uint32_t imagecache_get_id ( const char *url ) +{ + uint32_t id = 0; + imagecache_image_t *i; + static imagecache_image_t *skel = NULL; + + /* Invalid */ + if (!url) + return 0; + + /* Disabled */ +#if !ENABLE_IMAGECACHE + if (strncasecmp(url, "file://", 7)) + return 0; +#endif + + /* Skeleton */ + if (!skel) + skel = calloc(1, sizeof(imagecache_image_t)); + skel->url = url; + + /* Create/Find */ + pthread_mutex_lock(&imagecache_mutex); + i = RB_INSERT_SORTED(&_imagecache_by_url, skel, url_link, _url_cmp); + if (!i) { + i = skel; + i->url = strdup(url); + i->id = ++_imagecache_id; + skel = RB_INSERT_SORTED(&_imagecache_by_id, i, id_link, _id_cmp); + assert(!skel); +#if ENABLE_IMAGECACHE + _imagecache_add(i); +#endif + _imagecache_save(i); + } +#if ENABLE_IMAGECACHE + if (!strncasecmp(url, "file://", 7) || imagecache_enabled) + id = i->id; +#else + if (!strncasecmp(url, "file://", 7)) + id = i->id; +#endif + pthread_mutex_unlock(&imagecache_mutex); + + return id; +} + +/* + * Get data + */ +int imagecache_open ( uint32_t id ) +{ + imagecache_image_t skel, *i; + int fd = -1; + + pthread_mutex_lock(&imagecache_mutex); + + /* Find */ + skel.id = id; + i = RB_FIND(&_imagecache_by_id, &skel, id_link, _id_cmp); + + /* Invalid */ + if (!i) { + pthread_mutex_unlock(&imagecache_mutex); + return -1; + } + + /* Local file */ + if (!strncasecmp(i->url, "file://", 7)) + fd = open(i->url + 7, O_RDONLY); + + /* Remote file */ +#if ENABLE_IMAGECACHE + else if (imagecache_enabled) { + struct timespec ts; + int err; + if (i->updated) { + // use existing + } else if (i->state == FETCHING) { + ts.tv_nsec = 0; + time(&ts.tv_sec); + ts.tv_sec += 10; // TODO: sensible timeout? + err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts); + if (err == ETIMEDOUT) { + pthread_mutex_unlock(&imagecache_mutex); + return -1; + } + } else if (i->state == QUEUED) { + i->state = FETCHING; + TAILQ_REMOVE(&_imagecache_queue, i, q_link); + pthread_mutex_unlock(&imagecache_mutex); + if (_imagecache_fetch(i)) + return -1; + pthread_mutex_lock(&imagecache_mutex); + } + fd = hts_settings_open_file(0, "imagecache/data/%d", i->id); + } +#endif + pthread_mutex_unlock(&imagecache_mutex); + + return fd; +} + +static void _imagecache_save ( imagecache_image_t *img ) +{ + htsmsg_t *m = htsmsg_create_map(); + + htsmsg_add_str(m, "url", img->url); + if (img->updated) + htsmsg_add_s64(m, "updated", img->updated); + + hts_settings_save(m, "imagecache/meta/%d", img->id); +} + +#if ENABLE_IMAGECACHE +static void _imagecache_add ( imagecache_image_t *img ) +{ + if (strncasecmp("file://", img->url, 7)) { + img->state = QUEUED; + TAILQ_INSERT_TAIL(&_imagecache_queue, img, q_link); + pthread_cond_broadcast(&_imagecache_cond); + } else { + time(&img->updated); + } +} + +static void *_imagecache_thread ( void *p ) +{ + int err; + imagecache_image_t *img; + struct timespec ts; + ts.tv_nsec = 0; + + while (1) { + + /* Get entry */ + pthread_mutex_lock(&imagecache_mutex); + if (!imagecache_enabled) { + pthread_cond_wait(&_imagecache_cond, &imagecache_mutex); + pthread_mutex_unlock(&imagecache_mutex); + continue; + } + img = TAILQ_FIRST(&_imagecache_queue); + if (!img) { + time(&ts.tv_sec); + ts.tv_sec += 60; + err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts); + if (err == ETIMEDOUT) { + RB_FOREACH(img, &_imagecache_by_url, url_link) { + if (img->state != IDLE) continue; + if ((ts.tv_sec - img->updated) > + (img->failed ? imagecache_fail_period : imagecache_ok_period)) + _imagecache_add(img); + } + } + pthread_mutex_unlock(&imagecache_mutex); + continue; + } + img->state = FETCHING; + TAILQ_REMOVE(&_imagecache_queue, img, q_link); + pthread_mutex_unlock(&imagecache_mutex); + + /* Fetch */ + _imagecache_fetch(img); + } + + return NULL; +} + +static int _imagecache_fetch ( imagecache_image_t *img ) +{ + int res; + CURL *curl; + FILE *fp; + char tmp[256], path[256]; + + /* Open file */ + if (hts_settings_buildpath(path, sizeof(path), "imagecache/data/%d", + img->id)) + return 1; + if (hts_settings_makedirs(path)) + return 1; + snprintf(tmp, sizeof(tmp), "%s.tmp", path); + if (!(fp = fopen(tmp, "wb"))) + return 1; + + /* Fetch file */ + tvhlog(LOG_DEBUG, "imagecache", "fetch %s", img->url); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, img->url); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend"); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(fp); + + /* Process */ + pthread_mutex_lock(&imagecache_mutex); + img->state = IDLE; + time(&img->updated); // even if failed (possibly request sooner?) + if (res) { + img->failed = 1; + unlink(tmp); + tvhlog(LOG_WARNING, "imagecache", "failed to download %s", img->url); + } else { + img->failed = 0; + unlink(path); + rename(tmp, path); + tvhlog(LOG_DEBUG, "imagecache", "downloaded %s", img->url); + } + _imagecache_save(img); + pthread_cond_broadcast(&_imagecache_cond); + pthread_mutex_unlock(&imagecache_mutex); + + return res; +}; +#endif diff --git a/src/imagecache.h b/src/imagecache.h new file mode 100644 index 00000000..5776f46f --- /dev/null +++ b/src/imagecache.h @@ -0,0 +1,57 @@ +/* + * Icon file serve operations + * Copyright (C) 2012 Andy Brown + * + * 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 __IMAGE_CACHE_H__ +#define __IMAGE_CACHE_H__ + +#include + +extern uint32_t imagecache_enabled; +extern uint32_t imagecache_ok_period; +extern uint32_t imagecache_fail_period; + +extern pthread_mutex_t imagecache_mutex; + +void imagecache_init ( void ); + +void imagecache_save ( void ); + +int imagecache_set_enabled ( uint32_t e ) + __attribute__((warn_unused_result)); +int imagecache_set_ok_period ( uint32_t e ) + __attribute__((warn_unused_result)); +int imagecache_set_fail_period ( uint32_t e ) + __attribute__((warn_unused_result)); + +// Note: will return 0 if invalid (must serve original URL) +uint32_t imagecache_get_id ( const char *url ); + +int imagecache_open ( uint32_t id ); + +#define htsmsg_add_imageurl(_msg, _fld, _fmt, _url)\ + {\ + char _tmp[64];\ + uint32_t _id = imagecache_get_id(_url);\ + if (_id) {\ + snprintf(_tmp, sizeof(_tmp), _fmt, _id);\ + } else {\ + htsmsg_add_str(_msg, _fld, _url);\ + }\ + } + +#endif /* __IMAGE_CACHE_H__ */ diff --git a/src/main.c b/src/main.c index 1bb3b509..d56c63cb 100644 --- a/src/main.c +++ b/src/main.c @@ -60,7 +60,7 @@ #include "ffdecsa/FFdecsa.h" #include "muxes.h" #include "config2.h" -#include "iconserve.h" +#include "imagecache.h" int running; time_t dispatch_clock; @@ -89,6 +89,9 @@ const char *tvheadend_capabilities[] = { #endif #if ENABLE_LINUXDVB "linuxdvb", +#endif +#if ENABLE_IMAGECACHE + "imagecache", #endif NULL }; @@ -466,6 +469,8 @@ main(int argc, char **argv) config_init(); + imagecache_init(); + service_init(); channels_init(); @@ -489,8 +494,6 @@ main(int argc, char **argv) http_server_init(); webui_init(); - logo_loader(); - serviceprobe_init(); #if ENABLE_CWC diff --git a/src/settings.c b/src/settings.c index c98fc2fd..da46c455 100644 --- a/src/settings.c +++ b/src/settings.c @@ -101,7 +101,7 @@ hts_settings_makedirs ( const char *inpath ) * */ static void -hts_settings_buildpath +_hts_settings_buildpath (char *dst, size_t dstsize, const char *fmt, va_list ap, const char *prefix) { char tmp[256]; @@ -120,6 +120,18 @@ hts_settings_buildpath } } +int +hts_settings_buildpath + (char *dst, size_t dstsize, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + if (!settingspath) + return 1; + _hts_settings_buildpath(dst, dstsize, fmt, va, settingspath); + return 0; +} + /** * */ @@ -139,7 +151,7 @@ hts_settings_save(htsmsg_t *record, const char *pathfmt, ...) /* Clean the path */ va_start(ap, pathfmt); - hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath); + _hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath); va_end(ap); /* Create directories */ @@ -261,16 +273,16 @@ hts_settings_load(const char *pathfmt, ...) /* Try normal path */ va_start(ap, pathfmt); - hts_settings_buildpath(fullpath, sizeof(fullpath), - pathfmt, ap, settingspath); + _hts_settings_buildpath(fullpath, sizeof(fullpath), + pathfmt, ap, settingspath); va_end(ap); ret = _hts_settings_load(fullpath); /* Try bundle path */ if (!ret && *pathfmt != '/') { va_start(ap, pathfmt); - hts_settings_buildpath(fullpath, sizeof(fullpath), - pathfmt, ap, "data/conf"); + _hts_settings_buildpath(fullpath, sizeof(fullpath), + pathfmt, ap, "data/conf"); va_end(ap); ret = _hts_settings_load(fullpath); } @@ -289,7 +301,7 @@ hts_settings_remove(const char *pathfmt, ...) struct stat st; va_start(ap, pathfmt); - hts_settings_buildpath(fullpath, sizeof(fullpath), + _hts_settings_buildpath(fullpath, sizeof(fullpath), pathfmt, ap, settingspath); va_end(ap); if (stat(fullpath, &st) == 0) { @@ -311,7 +323,7 @@ hts_settings_open_file(int for_write, const char *pathfmt, ...) /* Build path */ va_start(ap, pathfmt); - hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath); + _hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath); va_end(ap); /* Create directories */ diff --git a/src/settings.h b/src/settings.h index 7f0bb8d0..a1db839c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -34,6 +34,8 @@ const char *hts_settings_get_root(void); int hts_settings_open_file(int for_write, const char *pathfmt, ...); +int hts_settings_buildpath(char *dst, size_t dstsize, const char *pathfmt, ...); + int hts_settings_makedirs ( const char *path ); #endif /* HTSSETTINGS_H__ */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 6999eacf..1fc3a8bf 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -47,7 +47,7 @@ #include "config2.h" #include "lang_codes.h" #include "subscriptions.h" -#include "iconserve.h" +#include "imagecache.h" /** * @@ -859,7 +859,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(m, "channel", ch->ch_name); htsmsg_add_u32(m, "channelid", ch->ch_id); if(ch->ch_icon != NULL) - htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon)); + htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon); if((s = epg_episode_get_title(ee, lang))) htsmsg_add_str(m, "title", s); @@ -941,7 +941,8 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque) m = htsmsg_create_map(); htsmsg_add_u32(m, "id", ebc->id); if ( ch->ch_name ) htsmsg_add_str(m, "channel", ch->ch_name); - if ( ch->ch_icon ) htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon)); + if (ch->ch_icon) + htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon); htsmsg_add_u32(m, "start", ebc->start); htsmsg_add_msg(array, NULL, m); } @@ -1340,8 +1341,9 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, if(de->de_channel != NULL) { htsmsg_add_str(m, "channel", de->de_channel->ch_name); - if(de->de_channel->ch_icon != NULL) - htsmsg_add_str(m, "chicon", logo_query(de->de_channel->ch_id, de->de_channel->ch_icon)); + if (de->de_channel->ch_icon) + htsmsg_add_imageurl(m, "chicon", "imagecache/%d", + de->de_channel->ch_icon); } htsmsg_add_str(m, "config_name", de->de_config_name); @@ -1915,40 +1917,54 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) pthread_mutex_unlock(&global_lock); - /* Basic settings (not the advanced schedule) */ + /* Basic settings */ if(!strcmp(op, "loadSettings")) { + + /* Misc */ pthread_mutex_lock(&global_lock); m = config_get_all(); pthread_mutex_unlock(&global_lock); + + /* Image cache */ +#if ENABLE_IMAGECACHE + pthread_mutex_lock(&imagecache_mutex); + htsmsg_add_u32(m, "imagecache_enabled", imagecache_enabled); + htsmsg_add_u32(m, "imagecache_ok_period", imagecache_ok_period); + htsmsg_add_u32(m, "imagecache_fail_period", imagecache_fail_period); + pthread_mutex_unlock(&imagecache_mutex); +#endif + if (!m) return HTTP_STATUS_BAD_REQUEST; out = json_single_record(m, "config"); /* Save settings */ } else if (!strcmp(op, "saveSettings") ) { int save = 0; + + /* Misc settings */ pthread_mutex_lock(&global_lock); if ((str = http_arg_get(&hc->hc_req_args, "muxconfpath"))) save |= config_set_muxconfpath(str); if ((str = http_arg_get(&hc->hc_req_args, "language"))) save |= config_set_language(str); - str = http_arg_get(&hc->hc_req_args, "iconserve"); - if (str != NULL) { - save |= config_set_iconserve(str); - } else { - save |= config_set_iconserve("off"); - }; - str = http_arg_get(&hc->hc_req_args, "iconserve_periodicdownload"); - if (str != NULL) { - save |= config_set_iconserve_periodicdownload(str); - } else { - save |= config_set_iconserve_periodicdownload("off"); - }; - if ((str = http_arg_get(&hc->hc_req_args, "serverip"))) - save |= config_set_serverip(str); - if (save) config_save(); - /* trigger the iconserve init routine */ - logo_loader(); + if (save) + config_save(); pthread_mutex_unlock(&global_lock); + + /* Image Cache */ +#if ENABLE_IMAGECACHE + pthread_mutex_lock(&imagecache_mutex); + str = http_arg_get(&hc->hc_req_args, "imagecache_enabled"); + save = imagecache_set_enabled(!!str); + if ((str = http_arg_get(&hc->hc_req_args, "imagecache_ok_period"))) + save |= imagecache_set_ok_period(atoi(str)); + if ((str = http_arg_get(&hc->hc_req_args, "imagecache_fail_period"))) + save |= imagecache_set_fail_period(atoi(str)); + if (save) + imagecache_save(); + pthread_mutex_unlock(&imagecache_mutex); +#endif + out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); diff --git a/src/webui/statedump.c b/src/webui/statedump.c index 29425143..603fd158 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -30,7 +30,6 @@ #include "epg.h" #include "psi.h" #include "channels.h" -#include "iconserve.h" #if ENABLE_LINUXDVB #include "dvr/dvr.h" #include "dvb/dvb.h" @@ -73,7 +72,7 @@ dumpchannels(htsbuf_queue_t *hq) ch->ch_refcount, ch->ch_zombie, ch->ch_number, - logo_query(ch->ch_id, ch->ch_icon) ?: ""); + ch->ch_icon ?: ""); } } diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index a862e0bb..42eb6082 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -37,12 +37,18 @@ tvheadend.miscconf = function() { */ var confreader = new Ext.data.JsonReader({ root : 'config' - }, [ 'muxconfpath', 'language', 'iconserve', 'serverip' ]); + }, [ 'muxconfpath', 'language', + 'imagecache_enabled', 'imagecache_ok_period', + 'imagecache_fail_period']); /* **************************************************************** * Form Fields * ***************************************************************/ + /* + * DVB path + */ + var dvbscanPath = new Ext.form.TextField({ fieldLabel : 'DVB scan files path', name : 'muxconfpath', @@ -50,21 +56,9 @@ tvheadend.miscconf = function() { width: 400 }); - var iconServeConfig = new Ext.form.Checkbox({ - name : 'iconserve', - fieldLabel : 'Cache channel icons' - }); - var iconPeriodicDownload = new Ext.form.Checkbox({ - name : 'iconserve_periodicdownload', - fieldLabel : 'Periodically check for updated icons' - }); - var serveripConfig = new Ext.form.TextField({ - fieldLabel : 'TVH Server IP address', - name : 'serverip', - allowBlank : true, - width: 150 - }); - + /* + * Language + */ var language = new Ext.ux.ItemSelector({ name: 'language', @@ -81,6 +75,34 @@ tvheadend.miscconf = function() { fromLegend: 'Available' }); + /* + * Image cache + */ + var imagecacheEnabled = new Ext.form.Checkbox({ + name: 'imagecache_enabled', + fieldLabel: 'Enabled', + }); + + var imagecacheOkPeriod = new Ext.form.NumberField({ + name: 'imagecache_ok_period', + fieldLabel: 'Re-fetch period (hours)' + }); + + var imagecacheFailPeriod = new Ext.form.NumberField({ + name: 'imagecache_fail_period', + fieldLabel: 'Re-try period (hours)', + }); + + var imagecachePanel = new Ext.form.FieldSet({ + title: 'Image Caching', + width: 700, + autoHeight: true, + collapsible: true, + items : [ imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod ] + }); + if (tvheadend.capabilities.indexOf('imagecache') == -1) + imagecachePanel.hide(); + /* **************************************************************** * Form * ***************************************************************/ @@ -111,7 +133,8 @@ tvheadend.miscconf = function() { layout : 'form', defaultType : 'textfield', autoHeight : true, - items : [ language, dvbscanPath, iconServeConfig, iconPeriodicDownload, serveripConfig ], + items : [ language, dvbscanPath, + imagecachePanel ], tbar : [ saveButton, '->', helpButton ] }); diff --git a/src/webui/webui.c b/src/webui/webui.c index 6376c812..69ccadf0 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -44,7 +44,7 @@ #include "muxer.h" #include "dvb/dvb.h" #include "dvb/dvb_support.h" -#include "iconserve.h" +#include "imagecache.h" /** * @@ -868,7 +868,47 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) return 0; } +/** + * Fetch image cache image + */ +/** + * Static download of a file from the filesystem + */ +static int +page_imagecache(http_connection_t *hc, const char *remain, void *opaque) +{ + uint32_t id; + int fd; + char buf[8192]; + struct stat st; + ssize_t c; + if(remain == NULL) + return 404; + + if(sscanf(remain, "%d", &id) != 1) + return HTTP_STATUS_BAD_REQUEST; + + if ((fd = imagecache_open(id)) < 0) + return 404; + if (fstat(fd, &st)) { + close(fd); + return 404; + } + + http_send_header(hc, 200, NULL, st.st_size, 0, NULL, 10, 0, NULL); + + while (1) { + c = read(fd, buf, sizeof(buf)); + if (c <= 0) + break; + if (tvh_write(hc->hc_fd, buf, c)) + break; + } + close(fd); + + return 0; +} /** * @@ -910,7 +950,7 @@ webui_init(void) http_path_add("/stream", NULL, http_stream, ACCESS_STREAMING); - http_path_add("/channellogo", NULL, page_logo, ACCESS_ANONYMOUS); + http_path_add("/imagecache", NULL, page_imagecache, ACCESS_ANONYMOUS); webui_static_content("/static", "src/webui/static"); webui_static_content("/docs", "docs/html"); From c9088eb739740b3e650fb14a54ff786c9d0ecf06 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Wed, 2 Jan 2013 17:33:44 +0000 Subject: [PATCH 178/503] Add support for the DVB terrestrial delivery descriptor (i.e. auto-detection of DVB-T muxes). Note that DVB-T2 is a different descriptor and not included in this commit. --- src/dvb/dvb_support.h | 1 + src/dvb/dvb_tables.c | 70 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/dvb/dvb_support.h b/src/dvb/dvb_support.h index 22e81eca..e9baf7be 100644 --- a/src/dvb/dvb_support.h +++ b/src/dvb/dvb_support.h @@ -47,6 +47,7 @@ #define DVB_DESC_PARENTAL_RAT 0x55 #define DVB_DESC_TELETEXT 0x56 #define DVB_DESC_SUBTITLE 0x59 +#define DVB_DESC_TERR 0x5a #define DVB_DESC_AC3 0x6a #define DVB_DESC_DEF_AUTHORITY 0x73 #define DVB_DESC_CRID 0x76 diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 7396e6e3..725d01ca 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -612,6 +612,32 @@ static const fe_modulation_t qam_tab [6] = { QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256 }; +static const fe_bandwidth_t bandwidth_tab [8] = { + BANDWIDTH_8_MHZ, BANDWIDTH_7_MHZ, BANDWIDTH_6_MHZ, BANDWIDTH_AUTO, + BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO +}; + +static const fe_modulation_t constellation_tab [4] = { + QPSK, QAM_16, QAM_64, QAM_AUTO +}; + +static const fe_code_rate_t code_rate_tab [8] = { + FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_AUTO, FEC_AUTO, FEC_AUTO +}; + +static const fe_guard_interval_t guard_interval_tab [4] = { + GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 +}; + +static const fe_transmit_mode_t transmission_mode_tab [4] = { + TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_AUTO +}; + +static const fe_hierarchy_t hierarchy_info_tab [8] = { + HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, + HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4 +}; + /** * Cable delivery descriptor */ @@ -746,6 +772,46 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } +/** + * Terrestrial delivery descriptor + */ +static int +dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, + uint16_t tsid, uint16_t onid) +{ + struct dvb_mux_conf dmc; + int freq; + + if(!tdmi->tdmi_adapter->tda_autodiscovery) + return -1; + + if(len < 11) + return -1; + + memset(&dmc, 0, sizeof(dmc)); + dmc.dmc_fe_params.inversion = INVERSION_AUTO; + + freq = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]) * 10; + + if(!freq) + return -1; + + dmc.dmc_fe_params.frequency = freq; + dmc.dmc_fe_params.u.ofdm.bandwidth = bandwidth_tab[(ptr[4] & 0xe0) >> 5]; + dmc.dmc_fe_params.u.ofdm.constellation=constellation_tab[(ptr[5] & 0xc0) >> 6]; + dmc.dmc_fe_params.u.ofdm.hierarchy_information=hierarchy_info_tab[(ptr[5] & 0x38) >> 3]; + dmc.dmc_fe_params.u.ofdm.code_rate_HP=code_rate_tab[ptr[5] & 0x3]; + dmc.dmc_fe_params.u.ofdm.code_rate_LP=code_rate_tab[(ptr[6] & 0xe0) >> 5]; + dmc.dmc_fe_params.u.ofdm.guard_interval=guard_interval_tab[(ptr[6] & 0x18) >> 3]; + dmc.dmc_fe_params.u.ofdm.transmission_mode=transmission_mode_tab[(ptr[6] & 0x06) >> 1]; + + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, + "automatic mux discovery", 1, 1, NULL, NULL); + + return 0; +} + + /** * */ @@ -881,6 +947,10 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(tdmi->tdmi_adapter->tda_type == FE_QAM) dvb_table_cable_delivery(tdmi, ptr, tlen, tsid, onid); break; + case DVB_DESC_TERR: + if(tdmi->tdmi_adapter->tda_type == FE_OFDM) + dvb_table_terr_delivery(tdmi, ptr, tlen, tsid, onid); + break; case DVB_DESC_LOCAL_CHAN: dvb_table_local_channel(tdmi, ptr, tlen, tsid, onid); break; From c066cf431afa87a828fca8afd80ef8dc23b93246 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Thu, 3 Jan 2013 16:21:58 +0000 Subject: [PATCH 179/503] Fix javascript error (typo) spotted by andyb2000 --- src/webui/static/app/dvr.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 80be21a6..564eab3a 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -129,7 +129,6 @@ tvheadend.dvrDetails = function(entry) { success : function(response, options) { win.close(); - v }, failure : function(response, options) { From fe5fd6888376e2a3173a09eb284bdd2f2c8b0347 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 2 Jan 2013 22:19:34 +0000 Subject: [PATCH 180/503] imagecache: fix mistake in imagecache address checking. This will still may not work if the server is behind a proxy, as this could lead to the wrong IP being detected. For clients with HTSPv7 support they will only get the /imagecache/ID part of the URL and will have to add the rest. Note: there will be a number of clients that may report v7 support but not be fully compliant due to the release schedules etc... users in this position will not be able to use the image caching. --- src/htsp_server.c | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 243ff77e..2e4bd516 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -370,13 +370,16 @@ htsp_generate_challenge(htsp_connection_t *htsp) * */ static htsmsg_t * -htsp_file_open(htsp_connection_t *htsp, const char *path) +htsp_file_open(htsp_connection_t *htsp, const char *path, int fd) { struct stat st; - int fd = open(path, O_RDONLY); - tvhlog(LOG_DEBUG, "HTSP", "Opening file %s -- %s", path, fd < 0 ? strerror(errno) : "OK"); - if(fd == -1) - return htsp_error("Unable to open file"); + + if (fd <= 0) { + fd = open(path, O_RDONLY); + tvhlog(LOG_DEBUG, "HTSP", "Opening file %s -- %s", path, fd < 0 ? strerror(errno) : "OK"); + if(fd == -1) + return htsp_error("Unable to open file"); + } htsp_file_t *hf = calloc(1, sizeof(htsp_file_t)); hf->hf_fd = fd; @@ -449,19 +452,23 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) htsmsg_add_str(out, "channelName", ch->ch_name); if(ch->ch_icon != NULL) { - uint32_t id = imagecache_get_id(ch->ch_icon); - if (id) { + uint32_t id; + struct sockaddr_in addr; + socklen_t addrlen; + if ((id = imagecache_get_id(ch->ch_icon))) { size_t p = 0; char url[256]; - if (htsp->htsp_version <= 7) { + if (htsp->htsp_version < 7) { + addrlen = sizeof(addr); + getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen); strcpy(url, "http://"); - p = 7; - inet_ntop(AF_INET, &(htsp->htsp_peer->sin_addr), url+p, sizeof(url)-p); p = strlen(url); - p += snprintf(url+p, sizeof(url)-p, ":%hd", webui_port); + inet_ntop(AF_INET, &addr.sin_addr, url+p, sizeof(url)-p); + p = strlen(url); + p += snprintf(url+p, sizeof(url)-p, ":%hd%s", + webui_port, + tvheadend_webroot ?: ""); } - if (tvheadend_webroot) - p += snprintf(url+p, sizeof(url)-p, "%s", tvheadend_webroot); snprintf(url+p, sizeof(url)-p, "/imagecache/%d", id); htsmsg_add_str(out, "channelIcon", url); } else { @@ -1360,6 +1367,10 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) if((str = htsmsg_get_str(in, "file")) == NULL) return htsp_error("Missing argument 'file'"); + // optional leading slash + if (*str == '/') + str++; + if((s2 = tvh_strbegins(str, "dvr/")) != NULL) { dvr_entry_t *de = dvr_entry_find_by_id(atoi(s2)); if(de == NULL) @@ -1369,11 +1380,17 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) return htsp_error("DVR entry does not have a file yet"); filename = de->de_filename; + return htsp_file_open(htsp, filename, 0); + + } else if ((s2 = tvh_strbegins(str, "imagecache/")) != NULL) { + int fd = imagecache_open(atoi(s2)); + if (fd <= 0) + return htsp_error("failed to open image"); + return htsp_file_open(htsp, NULL, fd); + } else { return htsp_error("Unknown file"); } - - return htsp_file_open(htsp, filename); } /** From f7f1051eaa698591abdae7feae285334afe81908 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 2 Jan 2013 22:30:20 +0000 Subject: [PATCH 181/503] htsp: make DVR file open compatible with webui URLs. --- src/htsp_server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 2e4bd516..23319b3e 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1371,7 +1371,8 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) if (*str == '/') str++; - if((s2 = tvh_strbegins(str, "dvr/")) != NULL) { + if((s2 = tvh_strbegins(str, "dvr/")) != NULL || + (s2 = tvh_strbegins(str, "dvrfile/")) != NULL) { dvr_entry_t *de = dvr_entry_find_by_id(atoi(s2)); if(de == NULL) return htsp_error("DVR entry does not exist"); From 8de4efe6e5eea5c9926e186172a43a1242ad2874 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 3 Jan 2013 11:52:36 +0000 Subject: [PATCH 182/503] imagecache: fix re-fetch period mistake. --- src/imagecache.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/imagecache.c b/src/imagecache.c index 224c72cc..cd2583be 100644 --- a/src/imagecache.c +++ b/src/imagecache.c @@ -366,10 +366,12 @@ static void *_imagecache_thread ( void *p ) ts.tv_sec += 60; err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts); if (err == ETIMEDOUT) { + uint32_t period; RB_FOREACH(img, &_imagecache_by_url, url_link) { if (img->state != IDLE) continue; - if ((ts.tv_sec - img->updated) > - (img->failed ? imagecache_fail_period : imagecache_ok_period)) + period = img->failed ? imagecache_fail_period : imagecache_ok_period; + period *= 86400; + if (period && ((ts.tv_sec - img->updated) > period)) _imagecache_add(img); } } From a454305b332faa6bbf74a0c6dd0a5f824c744d78 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 3 Jan 2013 19:30:06 +0000 Subject: [PATCH 183/503] htsp: bump version to 8 New imagecache URLs will break XBMCs pvr.hts which already reports v7. --- src/htsp_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 23319b3e..dece14a5 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -54,7 +54,7 @@ static void *htsp_server, *htsp_server_2; -#define HTSP_PROTO_VERSION 7 +#define HTSP_PROTO_VERSION 8 #define HTSP_ASYNC_OFF 0x00 #define HTSP_ASYNC_ON 0x01 @@ -458,7 +458,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) if ((id = imagecache_get_id(ch->ch_icon))) { size_t p = 0; char url[256]; - if (htsp->htsp_version < 7) { + if (htsp->htsp_version < 8) { addrlen = sizeof(addr); getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen); strcpy(url, "http://"); From f372e46ec36187aaca8f710cb716e0dd5e11493b Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Fri, 4 Jan 2013 09:08:54 +0100 Subject: [PATCH 184/503] capmt: don't start when disabled --- src/capmt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/capmt.c b/src/capmt.c index 304bdd23..cac49c14 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -1009,6 +1009,10 @@ capmt_service_start(service_t *t) lock_assert(&global_lock); TAILQ_FOREACH(capmt, &capmts, capmt_link) { + /* skip, if we're not active */ + if (!capmt->capmt_enabled) + continue; + tvhlog(LOG_INFO, "capmt", "Starting capmt server for service \"%s\" on tuner %d", t->s_svcname, From 4b71809d544f6e06f0a50a18001b7ec52f5548b5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 12:58:12 +0000 Subject: [PATCH 185/503] imagecache: another silly mistake in timer periods. --- src/imagecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imagecache.c b/src/imagecache.c index cd2583be..53198945 100644 --- a/src/imagecache.c +++ b/src/imagecache.c @@ -370,7 +370,7 @@ static void *_imagecache_thread ( void *p ) RB_FOREACH(img, &_imagecache_by_url, url_link) { if (img->state != IDLE) continue; period = img->failed ? imagecache_fail_period : imagecache_ok_period; - period *= 86400; + period *= 3600; if (period && ((ts.tv_sec - img->updated) > period)) _imagecache_add(img); } From 9e854347fa3318a51148d78e89d1936ffebe11f4 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 15:27:39 +0000 Subject: [PATCH 186/503] Minor addition to the way the server capabilities are checked. --- src/htsp_server.c | 12 ++++-------- src/main.c | 13 ++++++------- src/tvheadend.h | 28 ++++++++++++++++++++++++---- src/webui/extjs.c | 8 +------- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index dece14a5..4b6ab6a3 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -695,10 +695,9 @@ htsp_build_event static htsmsg_t * htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) { - htsmsg_t *l, *r = htsmsg_create_map(); + htsmsg_t *r; uint32_t v; const char *name; - int i = 0; if(htsmsg_get_u32(in, "htspversion", &v)) return htsp_error("Missing argument 'htspversion'"); @@ -706,6 +705,8 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) if((name = htsmsg_get_str(in, "clientname")) == NULL) return htsp_error("Missing argument 'clientname'"); + r = htsmsg_create_map(); + tvh_str_update(&htsp->htsp_clientname, htsmsg_get_str(in, "clientname")); tvhlog(LOG_INFO, "htsp", "%s: Welcomed client software: %s (HTSPv%d)", @@ -717,12 +718,7 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) htsmsg_add_bin(r, "challenge", htsp->htsp_challenge, 32); /* Capabilities */ - l = htsmsg_create_list(); - while (tvheadend_capabilities[i]) { - htsmsg_add_str(l, NULL, tvheadend_capabilities[i]); - i++; - } - htsmsg_add_msg(r, "servercapability", l); + htsmsg_add_msg(r, "servercapability", tvheadend_capabilities_list(1)); /* Set version to lowest num */ htsp->htsp_version = MIN(HTSP_PROTO_VERSION, v); diff --git a/src/main.c b/src/main.c index d56c63cb..704f6e4b 100644 --- a/src/main.c +++ b/src/main.c @@ -79,21 +79,20 @@ int htsp_port; int htsp_port_extra; const char *tvheadend_cwd; const char *tvheadend_webroot; - -const char *tvheadend_capabilities[] = { +const tvh_caps_t tvheadend_capabilities[] = { #if ENABLE_CWC - "cwc", + { "cwc", NULL }, #endif #if ENABLE_V4L - "v4l", + { "v4l", NULL }, #endif #if ENABLE_LINUXDVB - "linuxdvb", + { "linuxdvb", NULL }, #endif #if ENABLE_IMAGECACHE - "imagecache", + { "imagecache", &imagecache_enabled }, #endif - NULL + { NULL, NULL } }; static void diff --git a/src/tvheadend.h b/src/tvheadend.h index 9bdaf1f2..b5d54c47 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -31,13 +31,32 @@ #include "queue.h" #include "avg.h" #include "hts_strtab.h" +#include "htsmsg.h" #include "redblack.h" -extern const char *tvheadend_version; -extern const char *tvheadend_cwd; -extern const char *tvheadend_capabilities[]; -extern const char *tvheadend_webroot; +typedef struct { + const char *name; + const uint32_t *enabled; +} tvh_caps_t; +extern const char *tvheadend_version; +extern const char *tvheadend_cwd; +extern const char *tvheadend_webroot; +extern const tvh_caps_t tvheadend_capabilities[]; + +static inline htsmsg_t *tvheadend_capabilities_list(int check) +{ + int i = 0; + htsmsg_t *r = htsmsg_create_list(); + while (tvheadend_capabilities[i].name) { + if (!check || + !tvheadend_capabilities[i].enabled || + *tvheadend_capabilities[i].enabled) + htsmsg_add_str(r, NULL, tvheadend_capabilities[i].name); + i++; + } + return r; +} #define PTS_UNSET INT64_C(0x8000000000000000) @@ -369,6 +388,7 @@ static inline unsigned int tvh_strhash(const char *s, unsigned int mod) #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) void tvh_str_set(char **strp, const char *src); int tvh_str_update(char **strp, const char *src); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 1fc3a8bf..deca304b 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1986,13 +1986,7 @@ static int extjs_capabilities(http_connection_t *hc, const char *remain, void *opaque) { htsbuf_queue_t *hq = &hc->hc_reply; - htsmsg_t *l; - int i = 0; - l = htsmsg_create_list(); - while (tvheadend_capabilities[i]) { - htsmsg_add_str(l, NULL, tvheadend_capabilities[i]); - i++; - } + htsmsg_t *l = tvheadend_capabilities_list(0); htsmsg_json_serialize(l, hq, 0); htsmsg_destroy(l); http_output_content(hc, "text/x-json; charset=UTF-8"); From 4c681bd70ee42d0b0ba40fe62c61eff21f8835fe Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 19:12:09 +0000 Subject: [PATCH 187/503] support: some minor updates to python lib and htspmon. --- lib/py/tvh/htsmsg.py | 15 +++++++++++---- support/htspmon | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/py/tvh/htsmsg.py b/lib/py/tvh/htsmsg.py index 2c291790..dc7d5088 100644 --- a/lib/py/tvh/htsmsg.py +++ b/lib/py/tvh/htsmsg.py @@ -129,8 +129,12 @@ def serialize ( msg ): return int2bin(cnt) + binary_write(msg) # Deserialize an htsmsg -def deserialize0 ( data ): - msg = {} +def deserialize0 ( data, typ = HMF_MAP ): + islist = False + msg = {} + if (typ == HMF_LIST): + islist = True + msg = [] while len(data) > 5: typ = ord(data[0]) nlen = ord(data[1]) @@ -152,10 +156,13 @@ def deserialize0 ( data ): item = (item << 8) | ord(data[i]) i = i - 1 elif typ in [ HMF_LIST, HMF_MAP ]: - item = deserialize0(data[:dlen]) + item = deserialize0(data[:dlen], typ) else: raise Exception('invalid data type %d' % typ) - msg[name] = item + if islist: + msg.append(item) + else: + msg[name] = item data = data[dlen:] return msg diff --git a/support/htspmon b/support/htspmon index dd4380c2..787f45c7 100755 --- a/support/htspmon +++ b/support/htspmon @@ -53,6 +53,10 @@ try: msg = htsp.hello() log.info('connected to %s [%s]' % (msg['servername'], msg['serverversion'])) log.info('using protocol v%d' % htsp._version) + cap = [] + if 'servercapability' in msg: + cap = msg['servercapability'] + log.info('capabilities [%s]' % ','.join(cap)) # Authenticate if opts.user: From c13eb6e607555b29ce8f6cda0d27967a2c8547be Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 19:49:43 +0000 Subject: [PATCH 188/503] htsp: provide webroot in htsp hello message. --- src/htsp_server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index 4b6ab6a3..50ba204f 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -716,6 +716,8 @@ htsp_method_hello(htsp_connection_t *htsp, htsmsg_t *in) htsmsg_add_str(r, "servername", "HTS Tvheadend"); htsmsg_add_str(r, "serverversion", tvheadend_version); htsmsg_add_bin(r, "challenge", htsp->htsp_challenge, 32); + if (tvheadend_webroot) + htsmsg_add_str(r, "webroot", tvheadend_webroot); /* Capabilities */ htsmsg_add_msg(r, "servercapability", tvheadend_capabilities_list(1)); From 0b7ac3ff01ec4eefbcc1583a2707de7ff73a08cd Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 20:15:24 +0000 Subject: [PATCH 189/503] http: fixed mistake since I changed webroot variable. --- src/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http.c b/src/http.c index e87ec543..a0135551 100644 --- a/src/http.c +++ b/src/http.c @@ -612,7 +612,7 @@ http_path_add(const char *path, void *opaque, http_callback_t *callback, if (tvheadend_webroot) { size_t len = strlen(tvheadend_webroot) + strlen(path) + 1; hp->hp_path = tmp = malloc(len); - sprintf(tmp, "%s/%s", tvheadend_webroot, path); + sprintf(tmp, "%s%s", tvheadend_webroot, path); } else hp->hp_path = strdup(path); hp->hp_len = strlen(hp->hp_path); From 4e6f020f4de5c6f8218701ecc6e9f39f2f696f24 Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Sun, 6 Jan 2013 11:19:49 +0100 Subject: [PATCH 190/503] Update documentation resource (capmt) --- docs/docresources/configcapmt.png | Bin 31201 -> 17944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/docresources/configcapmt.png b/docs/docresources/configcapmt.png index 5b11d8b45b612ef09d983aa61a68f4d4058a0d25..7589409255a7602fdb7e7be5ca865b7250475be1 100644 GIT binary patch literal 17944 zcmb5WWmsEL(>9vo?(SM>p}4yimlmhEQ@pslL!iao-HN+Qa48gbin|BRN&7tS^?v7E z=g+rt?PTwjtevb`GqY#zdv+pKm1WRTNKgO(0J@y4)Mo$y4jZ~3hlBuqu3VHBj0+9S>ZtP-iM&)kpVnrn*r=+SCh(QPdPyytm#MM2P zPuDy%NpF35FQ#H9lNy@igga6$?R-dM(1~JVVd0z^^yDO|;tQ?ibA2DV?9%tFX7}Z6 ztkU1wNvM5Z4hJBY1Y-3V!8Wmj`wU2P+MC=v+t;7wxH-Ewu0gssRh6cNNq5g3FTWJl zXRLtd=cyY$+_djV4a*X!X#UgWdr$z)dY^k_v9SNOPkNdFv4KV;*3+++W{kAk71V9 zSa}-{{QDyTKO072zxQeNQwVrqg=6|hRadhhQD5|EQJ4jUkNkR%*`)4XhiTMpB|so0 zCB;ROfMg8%+77RZe+`?W3e&qVi!rl=VvMAi&J!L97d2{s=uaV^MLVD6`0rySrC~P(OA6|g(6!>PvD~>bf>2+v^vA(F5%e4`7t`}H`-WR^p2C|rwZWn!PiVITK4LXs5SWsiu$31y5rlyj+*a zxGmQYy=W5qSh>(_3KB=bALM#G-)mC4xf3TEW=v#+Vf<%|$AHr4ySP~-#$rdTYH+7& z2nLY=6`EMB_|!Y;8gBuY#kY#x9zSadS{vV1b~>bAY;|TROaz};I`mvmiN>v0 z^NbsKapQ%;!t~`nCMjmsgGs1^eA&=o+o~mJ8vTelQjKeuM## zq-#zE0^Lf>ql$~)y13qNzJ_-N=0I+8s6zr0ar~c4{D-tLWt`GaVC&3yGrI(|gQ|X3 zXj*YZOip4P^iq)#7=3FZ{W#{wH{}Q8zl49#lmdM6t8L38gg87A>j|$#Mk}B?7JrCP z`QmMi6sea2zc9r*R69@c3_^X%U2Oo2S>ne&xG6&yy?`as(J!c+YRfkjI?KZW0}t;< zTqH{08*CpD%5UGk84)3f+_z3`rCH|mm-TzAa=WJ;ej)9;bf4O`+-OZG;)p_TjFHe7 zQ_q_9b<~D?o039Hgf=9MT)SkuSZ9tZ@MSXB=%|66yx{T|!LLrt8R+CA=u~(@u#n(j zF1$L~&;kkl@0mx(#@PK|eL9}6#^hL8HD(<=Y&Rj?HKGE6Uf6>!l=0>+Ju?sQPNr2i zrX$*h+`#ylTr${PlH#g_j>ndtf)ModG?Auelzyh@huuBXo4%(Rxrw&xB+1^;h^r_O zWN&9AT>-jFEO0ZI4=O;FFeaW3m;e%s)(iRx%L@~zfMxyTZ^?>qs6mZWX4b5D+KJyZbA11}ez>kg1ac*yKhet-lH8k+uPFEr|q|N0{z4JLRJF^w_qgQi=@6&bmRL`*~ z(OtQ+?ptLl1jW8-(Qsv!1y>(mJox<7W=<6<14tn8Whpl_Hc~V2vL0Potb-TfXMvsR zkCFaXo_XdWI4F|PlQdJ$Hz>-@A!;`&^LfSfv-t2i-q4-kYxN$pgcZZSQR=IagX0R^ z%MI{OC$|%>>0SGRI(0eWI5{E1);_A9AzM5(rA{heY`E&%1{>-|?i}U(^Tmo3O(~q3 zj)$2Q0dOKTk0!fmda7J*`dxU`hO)Y`l6~)S{{)m z#^-Fy>m~T5)?~2gubFiiuzquPuKl(vFH~Paj7aMBX@jDtpLZiF#lYu{ufCn5a_7a> zFT-cM7~B%PGe6n=mz&xqrkM`Imsd`?PWKl2cHhfAa`}$m&JN1z`%=@WOkCPkn%c|+ z9-ZrfUkF|F!xM3$@_S5bsRRW{DdG_An@P(TgZIL8@suh!^bI1nabDY&eB*cw|8Qwc z=w-?%kSkfP#Avz$oHrtW?#wE4Wu69hTi_F&`)VwOdpGP)r&z_bS<*$XrsXUIKgr426onWQ+3(3_{go&^zts9 zM^Cg9t5K^ejJS zW;c_KEnFl7SXC2%%j3zYv9x%B(uVZ&((CkdZ4;-DPNacCgCD0k-nE(ebei0=z`}|%ff~xUJZlnW z0@O@phD_!C9u1@|EjrnEP_MXg4hn)ty;#4SXn8mb-%d_R)0UYe)LGab>`~0ZtYIx1 zSWLnM<-dS1CH9do&LvM--e{E$UT0K~iibuy@CDb#*PRuBIpzT;lg^ z`BTSKYcxX7oi}HwfJ&PUW5PHk}}RScbkE*IH?{5^wcT zc9IdvC9x_Z<{%hWnJ-pj1eGytwA_=p2X$@P51;FEK zbT!o+;c94Z2Cd!?p8}h@EuJE@FC$_c-q%9ImHbc1QbuVlXY~2gc%VY1cyb!Q#w^l-$vA4||h$?Odqrp*D z431@JJ{%(_0WPI0qKVpc2_3|vbw83yCq3I&zVE*iXCn$RCi-i2c+$x2a~c>zAy|y8 ztl`E)j2}KoPs~X6&jy@xyWkZT{cy*ifeum z6Vh6Is{VE#MNOdFErE6&$}}NY_qz6REz- z`UIHqCLjq?y?#8uWo1=&>JZPZQVTXbhls#+zx|YGGx$TEiAxE3y+Pv24*0cZ_E^Y= zdn)kTtUArqsS6ad3~n=X{V|Ai1hNj<4BM7 zS5l>xm!sZ%E7}6{_*G{Eb;q-A4er*d1}9R^DPh$jOq=-(*nwJz4ocUK;?8ZZCPM)+dZyBLN@osVy&S z71i#3%qpV|CX{xrnsy~Kt<9my5H8?1C&Nx>@NJ8+9RAUy8%*0kRmFSuW+BS*^39B9 zmOj%dKRB`caz?_84biCIOqL*}+ zv{qmxkF)fTOxnB5@otZ@>Lsxqe*gN5PE7}Hgc;^NK;vV7x+^E$OL4 z++@EN^}9l=|7+v+&Ec$-jg6%1t+>w~#x-(?wxKPN-37j%=+{3tQikt|&P5i@5CY1w zjndXq8qlQFw3YLBGkkNFz(@(RFo6IRR8+)jHl%J>>Sw6h8gbq*zIzw>kcAZ4#G9rq zHQ%}QKF7n3I!#KV+>Uv@?U%mR_?^=CJIeE+BY01%LzMgit?=1mnhn!IV1K9MqK3~y zTV3IN`*o{K?e=w?{>8gjsG-F^>K>>;)CYA9^B2O}Du+)e2fFCLG|iV?Pwy>Edz&Ce zWo(3lcHzkQv3Ljz;qdY#&rbo96rhoL11pV`f~#~^@yM&$kXj(W#Y*GS1IH38OwXJT z?^(M&_3N$Hx$$YN^UrSv$QU_a#TC3UL&|fhSV=j@P@L-O30Djr02|;@rBnfa)}n86 z#pmp#%`X3OVEm&)T1S7DANTJe$SGWe3&ILim2&l+gqq`f5!< zKm8$ZD!We=hDVspq|~@RRW~{625xjL1W37_U1V2T+4IF3u|{G**n|e#gMIY?Y=Rdp?Q?tYhWBHV7GZ-%8Wn2%yv@0cY zej-1n^RFy_uYS2$?3F5f1_;0tXtTM?djW$k<|)H8FneIgf5nR#Ag^jDeCKs0)=L-z zxpots^$V9=|eX1JHswHdX zOjbGYV_!8_U{cjc4!?J&=qF`8m9VrFq=LV47S`_kDpLL2N1~YZL)3odUqt4DLw%xM zMU2Ay^(46Yw*_c9qdS7v?>+|!?pcuvtyfA&?sv@VSWH5hGc#_KhI@W1L~|8GpA)ni4$$?xJn?q4}gKZ)A@*rZsqqQhPtbHGEe#2H8{`iY?6RU1T9i z^>c`sK>-BvWLoyOG4Rmyr4~S;5McOM8j^(kmjT*X`@9?vI%xKtUn{o+me1;N)j+0& z4*%Dj%Fy2Y2bIB2GlmZ!Z02Rd`R`kJDfj^U*1~}A|HX0gTPgu8VBx(Kw?^!|Uph=gg^ z=XMZ^ee4=LG9F6xB_B-&w&9(vCL!(DNSE5-s%p?5CN<2Fi#xA<0(Y)qNo@YOH6+s# zM|&aYOw%R~3p^DR4a`T;t8<|ybMu}ve|+)l0RK9^cnSt@6P^Y9{z`tI#Gc0Y>wpne z#t^+Q5lcc$p8o(coC7c2$dPKxz%_I{sRMEHS-xk{4KCRv?8N%qQ!T(gVi3xI_B%D1 z+h4CaNsaP_fY4PW48|Q5#)|+0&i>;F_eSXNua-q7-9sD^n`KYDW{sT~fD8Qffc3f> z&X$6X%oI&eDzk56hpQ0Qi|kc6fTM=J*m|F+xg$erkp_ir*%_I73IYRmU-8O^fO5Kp zv`pdKcSpd}LDw7|%#^WE%WTiLhvqSEZVHh4H0?4?rs@`3b`h{6rO0|cB~vm>P(Ql| z)%Nwn<;l6hN4CVBt~-32m7R8R*+){ z+h_{|@0WA>tCtm zyAWO!e#sdUu=8yhxZ`r`+!r5QcBuaxE2aald})yq z!?0XAX6!mkc`*&-r3lIg_?r8V1LC&7ApmG2nI-N6bNbxB&gOng#u?V>Y2y1{NGL z#clft3W?1g@}-DfZ29|L3pM11)d=4VA}5H+(gV4!%&Dht$t))`#cgbuP*6}*H8sPw zw@r|!p@BllPnGPke^RN1N(H#VD+;VTVP55avN^aD#1g~O{~Wk>B<8LvcJ$C9&`lN_ zcz-!QbG~!NzY*BKO`&BsSn3ITR|c=Drx2gQiX=lt}nXuE2Zt(>>l#kSi})VI+w3hDp4PHUItn z!`}La^Cr|`uj4gl-Wp+fDnMeyi>H&2j2q>n3Pn$kP7k3$yw%sQeM?RDm94kb9S^Hh zR3QQJIJ~RE7%cwZVMd1J8+B{%%EvCQikmO5Djc?wNf+J#UXRQ6vCzJP6GXc(H7o2) zez)Ll%hG-!XgkQMDJp^omJt>-?lPjFb~*}VLBr`#EST6>#7&kiqW7#Xb}} zMJ^L_^Qp`{Gq3)TPEYGO#EBwy7p(^?a%0*3q68^Z zfokCW{@$amp36=*$0Tlvw%ts=VtFmvAesQD(Wv{;PtTk z$+vrj_w`KY`9#qDZ6<32s)b3TJIKPP?s_dSK;a#`=apHlU*qLU z{O3M4H0G`o5#tJ09X70zAhR&;|&*c029xiu((}wjNI2&!1Qm=ug!6 z_?i{?c7B^i434fk#35LGYR#CQ_zFhE5;Vrl^M~J=GhUhFFfpQg{#<>73+`t(i@KW3CfoK?RmoT_K&9KL>6VtB<(;gcxDD;9v6Yd#? zA>9jrJusK=*3k0RNxGLF+}mrosX7zh(&DkZXv4(rf>$jrbDQDFsty0UTB32U@0S-D zC`h69$Nh2KFdqs4P10y+d?{5|H=<*NLOUI=#>5WPCi`&$xGMnko8NN!WsBd3`K*76sx66e^tn{uKMZ|$$)>Q!pwyILB|AG8^Pe)=~J8B>oY2yBnm5o(yiL9^NFX$5{Z#BK$ZHHpo zU#UlpDA?$zLP9Qx`DUwnEpJ$zXZ_cME9N(FoL_29xIgD?mb9&#Z4;%5fq!SSfT=D| zlR^OjvEJ=Qv-A|&i3x12Sj63`(7a2$-qoelm4E)#vd(8BS^9vkp|TQfwpb~+*!lq5 zO~_SGyhoL)s3$!N1&LbKupTvmwQWG1#<*eOP}|i)yX}N-IdaP;m}wyt3W4*H0RuTu zRVZZNeKItR%giKG*U&It=*rU8P)}ADeuW~dk?oMY1>A`g#=V(&Vuv)zD%OH6zQjoZ zho-ZW^VOK!!LgZ>bICZ3@b^)wMtqmz6)<_QpvgNiC$}Dr&%qd$xXWmwb(19E)j=)O zz;*|{AYnQP7?Q!8)oh?uWZpEEiXg*gn*4kC}Ey8N~EKKCCleYXNzmmgo?zc#S(a98o7@ky^-^g~NN8PeT z%|FvWTi*A%9~r2x-}A-z`xDVf?)cTOq?OvvVqcq0*{Ms*d3KI;vFh@}?o~^%eVJr< zec37<%kbPWB~%o=J1Xe^+%0(wZ0`EL+NFNqJML$$+i)`*szp=!UY*`Msn(a>^KdKO5BRHQB8Iqgi#xZw)GH{2;{7+YT}y@rChDqodZe@qy9Z zZ^lty#4EHgR{inhsSd)V)SAP1a^6tmYbU;Xaw$c(l3zmg=N`HEpZ@$JWxE&v={vTn zg9UG|kLTfqblzu!TW~S;f1t+A;<&HXnh=Qy3(Z>`TL~O}J2{^pHX}}FLZzl#URn91 zstTZn6L4k97AU4#sivC3II&ZEgH%~o9)tzsDkCa&d4m6Qne5%5ZNuE5*>P_~qSF5z zw!21wGf4I9T5nEd*EMWiPU63ISjt*^=h<*yB3UN*AuvHZqaNX@1KrB0Jk_!l2}VGf z8*%IYDgF!HjQ+)2V6-w7)%OxX$4dGD*Cd6@@kz}5rj+4D>1dk{H~gxTSzpDnwQg9; z4KEm7F+>0ytiV-F2NxRcksDpqZ>?^K;%D|qf_^hoQi^)*ni5(d;9~t! zq~puEq_t?B>BUe^vggpH@6ZLKE$RI;L;?x?;>WE0 z6L4;~5tDK5$9UEiwosyd%lED8f_(5>R7S3a_6)p4LrCKuGnbP zeGoi$H1y5^HXYW$ti8B^Gc^{75x6?yr#Nz-Kd2pZ*2Ca9OmWlDl-D^6ucFD*<}Ijn zTXMObKDu$pSIpIQO=pY28#RW?ZmG$BcP0|lwC4tgNg1+#oYMQAYq1ch^5V@_;US{i zcDL2a>mT;y@EG0Vt7G;+rzRTqx%&sPVA>&N|H_#xumI4*^1;gCYuL;}^!lA9xZNB3 zWPWD(chK{@_ctPlz|-<@3y)lZIwN;tRCI_dA&+0by9mB;9@smy>JT?8@ zldDiWXT&N{vgOqovnL@zv`|r|0Yl)%z2We3FV5$~yiX*j*JzLLmW4@?H3?S%1oEH^ z|G@y+zeh}=2ICzdIv?<|uNS|MgB)gDh*x7~l5}$8QLDrgRyOd7mu{W^RN5Md&#)5Q z;vkOkb~h73Gb%^Hbe6u;oAA`mHeuSE0rGZwu;sO7?@HoH3$|-ZtqM*nzdmF{&EO<` zD5`flS)`VI$L?@trj18llll4kzPhn!itOTAxrA(BBd3KJQlP2#`rvqx&9=dJw&kN6 z)h71&O^$?9sadX@+uJ;7^cyHk=0mVx{lkmp-WfOQZe4oh9(yf69(Y_rfuUH} z?H^WawWF@3yLN7^k&iN*4AlK=)kS!!bXaIElKE(w8G#%ArQ#!3xtA>%8hg2Bb(l-S zTKP{g2%>J{sPizNB^DYR?vo-%MaV&TfQvsC#xsw%sV3(EW2YZ=%#qvb-e0~tK(Li0 zWK>b$LPIFuoE$F4RwJ;!;t7V$eH>R-DE2F=n>2e;;&kOz+hGJ;JiQ#u>rzbz1FJa; z@+Z?yQD!By@43QNXQ9C#@j0X>daJE=^G!I`TEJx^*gS^ z?>lPIv7i!>lYtxq)4@L-uTl~=abb|9-;q8k@FkN7j&?;17BJKXh?%1$f)*(WkpiiT zSt?pOi~9VG%o)+NGUe1o11Im)9C6_c_~HNXYiQ&56vH6u25GB(6?OM4M3G!bP^O7- z$(kJC;P|Z!t#}x|PNX72g9i3we2##q{1E+7Mnnn;q{Nhzem1nkGV&ifMr!EZ(~YfB<n-CXh#@%S8{ra!GCod}0A{6{{F|YD;~QVNDofOk+l@e_iBOYvysDLTiA!|DH*?Hn zD`41$6qb(y9Oy!GLmK{$d&G_%(3mBqpEq^fWTQ>`Zb{U=A4(U!sO^Fh?uMgj+ z`F@z`uxNH*T){eToJRKHSRt|V5@PkqCuw_h z;j?|uMZL8^m#G7!=Kht3#`C%LL}+PU zJ?Sb7ewQ1%zw3+opOz|Oe$`YZd-`;TmQi|KR}ui(nVw>GYMf-a2$VFakB<7QAp-D= z8u<1a{e*iJrTk80+}zxy9pG<#b*rZ3%9bWyIF^*5=s7f1u0Dp{kS<`{%8|I~X1e+s zGCd`Vv0%iSm9uG!v`=)lK;82Vy$| zfC>0+Esf3dJY&VwlTzE)YK5Q%CLA?FxSt`nde}BF4 z5C3kT+qu@0MZXc?-^9I#Eas6Vw;RH;{5fkr z)9uPV>B7Y2-mv9{KIr=LB3^#a{&@l)4obDdwx>k+ixa-|e@J-3s>)>V+a;5y3Q;yF zpvN1kFjHCNY((E;H@tiR{aCKHa+O0z=R)in5gb(%6t$CaE(sf@VbE>ka=AJBse|{Y z^#l085wns9-@l=Yt1Qg4Nlj@%1Nrdl_ec|XKwCAfku02R0E!tS`uJFAhmml+yFZr$b67CCo4*<9cZ+u)r-Il`|3s>=fiUg*Qa`XV8xh| zxR4{E!zb*?*ky%XUV)ikTZ>(Pt+A>{z1f7c$~zSlHp_tf7Hdc%i{kQ4@JJmp91QFT zEH&KTcv^58r=`i`&0%SIxrDSd{OIVYs(}IU>gp=cEC{Pl9mlv(e)x$HJj_}lbC!6Y`v^30T7r3g zJ|+BVzhzfYa*}WW%J!{CaJ_XKhwO>&$eH5%E%BhnWpyBhr@YcucOQK@c)1}3?Hp$|U%c@IlZmO-|N2`+HF0x*BLVc{VLCg!@`l4xk#tm~SeR#K zlsJvmlH_G1Mje-AORFi2f-uulS}vH#a z^w<0%O-aT>;^O$TLXK3-w5H6&X*#9wUs<1Ohu6VNWswhgbETMMU zzU|nm4wO0H^s%%N!E733Yb9$-feCA(@)sy+d@nfwk&puu|d?CwfOR$-Tj*m;@ zz0&~G?OF5YuPcc2JvYX~$NyVwAS&<;E>2TjY?f<0gBR2L?xgoukph&f!>H72#re`~ zKNv&I`P2Q}K^6+;kjpHYnPCzh62$*_TdV;Z)cPC{bR@W#X|)M***>@;D<} z?5l5Q39rG}V&zmg2@gG-L6n}JfXgr%#^(_x3EHSk%^d z8fU7A?_!5}NO^1qR&se$QO0ORK+mKdvL-cAxOENnfy% zMaXAYbc^uZAJhMKBW?$^;uUH3TQpS>YE-$==W0yL75LYy!5ky|L!9R|{cefu3P;5= zhr7bhCnBGN!+PGC8GeGB$m@aWIn>-#eN**_2h6D)YU59P!jYbz_ygoeO}6bJg%*G| z*M#(v?s2eX5{lm=FRp5d)oN__IIF-oJM_*ebPZAgXTAZ$b`>q;I7Kf@p}gYJCh>W9 z7g`*o2|>ldPModO)WDpl%S~-C1SQ+z#=qV?U;B-ivomLTMFrxTKiR9A%~lF&I3!GP zN7l58d6geNwf5lKBDu~_L4912;zNNviqq4O$l%M_>MR~Yimp8nxW$5rM@uv=VFEP0 za|#1!A)`nnP~TRmeiG4~dO1m5H{405EXcQ@yb`_IDp+u*76iQ!dlM3XCD1U74!$CH z-m2-yOtQiwcdl-~7Hk26!i`yj*H4lT)^BL70s)0h`1A1Y)DAORI*}m9FVPkaiJUcJ zM6hyj&`F z(CzE$SxveqdFWX2&Q!{v?k^KfiaQ(TU}wGhZlv*78dx6SC_hOuXPPpNR9Z1c$G5ZJ zfODm|uT(5C_&dXORLSVIXO8}n=7Su$?4Kv4BqwR4gjm4JaPoMPLs3p1+#$S(t?T>tpSstWKm|Nk(aIipzrtx^9@tO@XN@`n9iqEzS2W?-WlG4g+E+G(Q3 zPyiG9;va(|{?oR%-D&=3>^seXZd&>Oo-|VDDJNC>e>haI2R*d-;$KVql_SN*@9b?P zc=!8MDy0Utjcv1`t+wTaBXL)+U-oUD2juBsy%pBWh7=BNjtsL-OCLB-e@vA5Y4e15 z215C=3Wt926~N^Hxo%zXApuMvp0|2x*5r(D{%5LS6`It`yIOwTM9U7 ztcgzaMYm@=xJSP@R{h5~T#kVAoV(55q@FF#&f-0H$cYS_{9Dt}Hg46cZ-8#f!nk@(po{0Bn5HJdQF=h5O~2tV30H;+qTkDLfn%?`JxT(SHB(8`h6D=o zJNl4#tgr(}H?>hH`O>@(o<&kFUptNt4<_u#zk=b)lnaq+=IrnThij`h;&7HQ_e7o* z@GW~1rBBD(ObtFtH=C}^h;ClGQw%iE3$Yu+TJI%e-HX1UUuo6^dCy1?afb&aAO;v= zVW^16pLM|6)pP+lH(oe0H~fEaJL4r1x@^Eh>wn>3xF|bFrstb5L#3lVzo-g~$;ggR zX2Bwh$-hBHriKqmoJK=m4H&#}OwG6in+6-&Dy}auZ*Bhb{mEEuOHv33PDO#{~b%30`*mNl* z6)Ny%|MmlKe)S9-iT5Dz}v6xKvNf z_g5|R(bRT27bzAoDrSMBa?Tgxvld`3(T;S?IOZoCAK%xU0?z5cyXv}Q0gT}qp^xfVjo%mB?G~?@_?)7?{-?PDpLIPgkYlE$35 zA$1cjKRFm*rE~w};^}$WLjBY%{QMpGGT|chS>Nydxxz}9)qVo96E2cS=2I7(;kfka z`rTbFNS3JV79$i7COo~u>B5$~oD?IsZ3}R>a1IKbefqSai`b9F79s{@Kopq?owWnu z4*BtI$DarcL8}X(t1&Y`aPJnTawH<5@gPKYR{qh(ohe8{QpNDT=h*VA3a#K2!dgke zx<>Svr6G_P^OXWM+99^BL$u8mJ=^P2-{&&L;=roVo>2{jp?f|t{YtG+v}3r+O*ZpoEM z{dpvB4w@IWIf2Y0T> zX?y1ezJX{pGof*2+I69kYEkkP^I|``v5GgLeD}wwPB=)@lS&Jn0ejqyCl|Pj7b95-DSA`4Rz zfZ%WgO#ctMS62Cn=zon&DV|QEQl-xYS|B$coNdI*6;0qS{tHW$Dv#LfS;cdA(-Hvw zWoL6?$ciSSlgM#*SmNWFf%bs5ndBXvn}^r*JP!g))ZbQOdl21~%29R_)<@$%Rr2dBwU z`s}}+hpHeF5aRb$+!|`&w2l{5Oy=mZ=yQYV?56IT5tUXtPoFntj*L1UCYU*wAv+V4 zc3$A;yqBnEJBucMKWbz$2hjlH&}bG9IU*bhBqVAh**B8IrRF4_zdBuhw}o6P^1N)eCo|XH_RJ=y0o;M zGQTqK0?md7maFIenj5;`OP^qTJopy%d{d#hs<+0_WB*y~W!3v{f6IB34Zibifl6Tw|( zU+Gv3?}txh;{)&Jg|;qHCa#YO*NO28Tq;F>3_Mh%1oce!u_$${9V#=9I^3<1h#AJGx+E*_N4Z45 zGi4$~jQ(W1;CW8)D2AlGn66PpX5j1;1Wh`po3e`w)T@~JBaI=^N^bvAMw-~BE076V zbV~`GwKRAvhN+9>uNML@9K@zZO5$O>UA`pA$opJ*B7gt_Ej z<-yHB$%K5X8J3BBBKgCZ@bel>P|2)=*shw3N*?#TMd5RN>rI7@-2Sy!W3~oF`eYDF z&Qe+0vLCd&>b}D5arNNN;_-Jc*q#)QhUqL#y8>)*%Bq6&L8sH=m7xQ~pwvYTQ#EPcODwMN}qVVir9JVxIbnifg3 zRtwh^k&xL$d%LeZl=%EERgE3dxqQ#$Oxl^oXiINeC9Sm{lfuSY~%_33&&* zMp=4Alk+mJ8F;D9<8#rANcrCj$xAz{7PeUt=FAv>2djB+N7?mhLP^8 zi{e;W&$;MQ$O}ki)AGLX&Uz_fsymocg{Nr`r8E4# z#WZgppNpHTmg;$6RT4^}V$9%1tuJQ%nA`}F4`Kd7;FC_M1MmUV-JlZIo9zmmtXd(UnR?~yG#=BJncIP;d~uSuF2<~j;Yu) z2rT0t41hZlP~O4x-UuYOe+J(Xf|OLD)7KSxc(zcx_FIgPM%02qRCY~E7X=p}2F`y} z%&5n?%f|r(G;eV!llfqNwC8#IvZhd(L~K^v(bPE;)|W2<_A#)nDMM`A7|Lq42LQ~L z^a9WqpNHNycoN=sizC*lhPQXs8A}X)I|oi^*L}15nEW!5W@mMq6+}(Hy!-{x-ZDNc zWMW$^(DHpTzIxubBU_J5{>sIE?f%lB&fB3E) z@2{SmtRM7u_L?WpyG=j5Ub|-`tW(xsuXMe@U5R+TX*}-)ik-^Di=!vY!83O5Gaa;_ z*uPplvqci_k8ld{`Bq{Wva0?>S!r&1lTHXL`lmFBTERi^KAf2s=xvQHTpH&acx&CP%0~|nEnfOu+g*r3cS@`oClhH6htM$gioFfJzJ-+ z5&gA!se+J0ChUlau#_mzqLiQh|MC$MjRKedCMGnvk2O*N(IeXb9Or{Q1Y!S$v)I^a zeJjNOO;?8Tm;gR{xJGpUJ!WKK0<1O*S9|DE@ES^MA98oPdN8z7r(#-lUUtX}74&k3=v`cj06CD_#AG$0@b0ceKyj z{F|m(DLWgw6^a$%nc*0IoLGNOegor;-pdciD6tlroSg75Z$3taRpFx6IDGe4ft$HY zv5U6?uaILat+LBBT*`DTFl>8-@^uvima)pS>MDYmof;bP(tIyU;YeH4-5gP&* zXi7ldhJU=A1#+Xn(t2b#I$ZLm$ zgp%XBFHcp697cc&ktT1k3low2fpbg0q0`pBR(LnF^xL_2MAxcovN~J*JW@}&@;t~G>CG7MC-3{XD)8ww?I1=BGd^xNyEFT#4D-f*B9C%0>|k~ zJ7Nt2@kii^@x|m}bsXxft8ev)u63TROT`Bf(b!g0h3WZOG_wTsf!8KC?Br| zJ6YFO$t-s+d)O7KSog9U#KZLPh>w2b*x77E(Df(=!Jou^+y}cA9>IQkuedgerE@7D z&|WafS|lZ7dEfI)p0zeSHAbAudewMw8`e$m>eaD3vxr}TrdEbn%Yv+U)Aigz=HlX( zp<9x6`29Gh`9!rHrIDVqmVMUXvvrEa4q+=re(mzqfM9W&b^SGHTEE%_=n=1b% zjz7OM`QSDWsI_fZzhvrM>3Xd#!ZXx-351Qbx0bVbCOGCB+(Dz4{8z8-@5)j*omZ;) zF@)o2ZiEC%6V_K_&x9OWN+2uHY^MfmFGLK%s%g<+6_Fm3U@H~+h51rouTa)jW5*Ig z0i00$oW&6w?5DH_8yno7`zinc{>$J01vod$ks0U;_&XXsiTZzT*5{lwjLc>8{@5Sx z8XK`Vg@yeDW1>$>;yvll0F!_;pq%{QPMUe{e7^s~wV1#MaNd@Df|3`|7h-G@f=U~y z0l%<_MMA7&l9|N~{i@f!)_)S?ZE$kL5I$266ZW{rK`){y;>_33d`9T)_zYmcc zM8=2kZmhZWHvHNyD;1fKq;34*ad4KznEhi)4E@vZ?sfp6(eS$1&ht=rUbd1p%^VZd zg%g`1^0qEVZUO3qfezLUl#qP#*?jUbiCNkW(&I%!JO0}FF{fi96&kicBZH*|YgmXD z9=yhG(IU8FBd@9+lR>j7-|Xv~U??yWg{V4%pmdn7EH!ZXA=W~Y-%VNpBbEOnhHEF6 zd;(Gj4z*`_c`lnM=DDwW@ZF?{dJ?|LRwfM+HBlxt#)YY?RAgQ1OtlfxdEt)-F7_FL z{tdz!T*Z5ajLSQQ8(u++e=~J+0Xoe3!e^$j+jJC)9lR*UtNoILdy6&h+?wfV&cAj0 zs2ZOWOt|iWT3)_gjjk}gvJib@iy6!%Wg+^)c@5Fd%1u+j-KpZ7A9+@tPb=L4Hpg9Z zbpKrkvJIdf4E3y54?PKkB@MBJBF%+7TtcyOT8IJb|FaFx6J+ytREgQ$Ddy76t<}&z zd0ce5&+$%SQ=T9;VQ%M2+iQO|zWeBvHuK{df%LkV@Ef8eb{~d zyK|`Mox;Pw)!PpWtR$Bhs>QKSKlkK#z~+M&t&+SM-$oCH7(Nk@vT4JwKiCNnMhjl*;O+IoDQUIHuA`{Sty^i zTr|HD*yKuYmwau_ed(g$^6cB4YM>eu6dubS2kt3MvO2yd?bxZ6cjTo1?d5vNFIRkA zAC%aDi-r4dHs3P0CY4I=&w5+GzDWV|ttF<(o%= z^_2LPBhkPj=c3iuiqD^_1v}1cy}NMBX_vQCJ31ampSn_*uA%+{m}-`OdfIDZ2R?`+ zcutv`(4&fv=KBvg^V@Ye&zEzH%hSs_Cu8&fw|~OcsDl5`!<8hyIC60+vfRlNulspK zc*oaw>x~K)8{Bk&#Hmo=s|r;op=W33GB5c5y?B4+{ZR1IWD8k7wsue(!eXE5qQd_D z=MpaI&#!25lvr9J3LfY@zTL&dW=ik+D<^O3>82o>8~?1}t&&AdfBb;k7s0?Q2e@J# zR3!X7lWzAJSSTQv8|O_08RKs5lWf%rZMHlLl4OGFG-~#Q8Di8rP|T!g!w=!C$W#e zzJTkA3k!gJ{^OP1Q55&p17#zkY7YVejr@=IH;~kH^si0`2T@5uh!q(4Uj*0_q`G@w zT^J66Dh~YCmX?NA4j}w?hPn=h2EUz69ZY@;i%QBU`@o@rfcyp#72s8HSv|{ecEMg~ z`Q%}!Yg^T9wW6}B}SwVIX$AeOX&JM$};IHi^fw1HNTH^f?z61V# z!@{C%b{^|cZ&aijJF#pq9DRPTi7l^_N@h(d-+5tlx^mz+|F#a(p`Wu%pDitYrOCgD07m{Lk=D zJ!(8`5pRSbMgoR%6&gx#qv5J0PtJu=G`IgAoX;L%o0%)0nJLum6hJX(V@FnJpuStI z5BX<|{6<=@L|MWZu4OFdbC4|Pv|vGP3oG8s8LqWZTLb_?Phlnlzs~bzR-U&JdY4hF zrOEnI3l4`}L|>mUP}k;A(>V?N13PYA(H(1|c{-?2^4~v1!a*=8$iedGu-s7`l%{+M z&AOnvIG2z4x+Rrr4oE9TG!Q;M1* zW3TXaOse&eqJqrZLdk)ByX&5v0rWsAyG2q1K{_gn*-yYEfju!MBOQi_ba%G013V7< z@Bo^2crK~MN-db?65Z|ER8~)aKu1Ca<*IG$6Kz76U2aG{SmPfSSH?xu41?!MyG~Cp z<~Vz#PNY<3x_<@~(abq>9W9@lW*eT(*m922@132sy~A77)@Jv)23Hi;-K0M z$v7WfoMxPtajlP~YmS-BwSw-G`ivuvEiy zh?dv7oK9TT#FEm|+>(|6j!t`?sn2Z;n7%)dJ)ZDy-Szi}+mi^py_us*TAMcMs%%a| zMMF02i&H+UN)G%8`wA+w*>s9IjmBsahGQ#dJ~#j8w_{~2*INbA&u#gueg_A;%7 za*h_l#<6X#w?>?}@G&tlN3D~NOPWJi)}4YYIp+pZ>GPIYbqL=-Lo(iW68acLrenY< zMDtv2lo)6#MYp!q?Lrj!fUV^If2=F?&cB8DhKLF@YKyT?R8;Xnatf9`KNo7?c9CUV zd5t3SWhlxm8VpF}grT^+m!~3k=d*&6=QwM+mXE1`fSmdrPM+uaiK`XCZvo zJ|^~R*@^EAK*(OaUc_74R&^n3GExWfoarB`kEx`xrM)6MUG=lodOmiN>dK<%&DOT; z+}NTBj2X{Iy)5DxH^Al`L3N|=*(K|uKY4JkS+TV0Rv%?F4Udl-+#SudKW@0MkDeYW z!@4(J*YQ{x?nBwE5T%#!H$QXOH#-(vPS^Z_{I+5Cao4Z+;e@R>IJrM?96+hTnE0y; zSM2T(LERt0Etk@_fISbC6VvFV1!BP zXfru0h+r$Hk=A7swrw(OG(RUt*0&{YbNQ>{L&#){r#k@%QY`?ig8$G!0G)ARUT%>R zF$qcB<5YCfB5s66gjkKePXNuvKw_)Y1R^r>SSjgrplC2=Cq4#nte{R{=d!givNwwR z;8mZ_(}gGcan2utw-v~{^w6Bfg)T*csAM`FSJYE4S;odq8 zd9rT#SijidO-zgL@$Jmd%n#Snok$#1=%h!)GvrDxz!M0mC`)40Cp;C$FEsv z*QR%Qq6>e(@(0KJlAPVmk$|^qO&J_W01-knb!;eFhsMNb7-6G^{08*!@u^Y!Xsdze z^NWi}7{AslIPw3KZH#$Fp&{9xE|h0v380V@sXN!^O9@Ui=wcYm`GZK0aBr03jEs4+ zN9CaUZ0ziPUPN|RLNh`ku){W3g%3mS^iF!MA$<36EO@gF8Si)H4{iXDTnU=k`n+)3 zrv1Zn`25gqn^RpL!&)|aH4n;+UCOV_Lg}nYEcLP#{o0 zZjYRIxc8FXSZr2m(eEQB`b)-XPfMLjFsk_U?trGrDS{AUZclfvw0wa;L1;5(Y=T(K z(o;~H&xW7E4yc=$$`f^CFgJN>WY$9oSlAX%m>$DX>*XuSgITQZl^2>w68qG480p0s4ZiF@sr=)4&NX0 zCgyQehpKomo8jo(2*)|eiWKjqR z32j?6{DXoBvA9dgBV$ri=Ayt23Fhoubhkxu)g7VR8K%U|o8(>|x42czw528b(hF+^;|yZ4kJzoK~|9j~%A`AQTef za&l|za`ZwKm7@SAIxnr^*w2M1nk@0db1e5_u#x{vY}#T5m6cP!vU9)dal{Hs@tlBM zGk-bK7jJ#=<0Mb2fNp`6H7uH>xhJtdv;^A=gq4y;SY>F%dStPCGl1UE7f}Z_RiYxt}}y{r1W%M$zEkst-u2BrFAjp$M}9##trgTnw>w`3$|PLboIWj!Qt&NeOi2Pc*aQI?9~{Qq>NXj zdt!sM{|9g8BEXc&xeHtmq_K-z5*4Xy(#JFinw9nRpiRTnU+P;X+X|u#-JAC9I zu`DoF&v5#G&Y0M$#pJ4}^im7uyiC-;rX9AsLaVE*!QtTstM#U58y#uZlDNW#c}X>7 zPEOdYz9)}9(>0{)P1IPS{xc5on{O{BLtN%ag%kTjeMA5NieRqkq0)vW&L|-~9KMt+ zTm2of7BHb(QV-baKXeXLbgeBw1ZAY`3YkR zPkMhFLO70W;kqY`!qsu}`$ZVY#5tk+h2R>>u`%#e{JGkN^%3|ZrA#o!WCi%{R&-Gd z(O3t?R!v?&N?*I!pNYeyVqX|*M)5NT?zUw`dsz6y1lQYz^?E|s&!7m`RCWqLKo9g; zIyF37t|?dT@*O{G*TRG`ksO@u^xjB_$SSZDT!U!XnkwIVMK`mJ)en3B%n9}7( zd(U}pAqcJt9M-HNb6i}Cjpg?-q*5=16*4-(2C53LJ%s>P4c>|@UqP*u8?U0YQo|0n zp`_xS-klo!&Z_k14&2gL^hF@!Og3=M@ED6J-omx-R)$*d{46P+^oK1)_S|iYCc9`F zKhNjik4>{#h^?2GL>%)%saLLuCJ8y$q*YZh+#fH_=j9C)VB)6IdX|03)<==~3f`jA znssfP5xA7x9V5M85To$McM^Cc4V#O1TJWfMW)ei(8G!-1kRGsBLK`+rJQ0sIZy`%A zPU_#hQxGQ-mSFd3U}ZU9KZ)Z?Ak}{UfSrTQh?VBvpXwxQznvABJ$15w^;Beq@!9x5 zeoFr%3>tg$up5Yf#qyH;(l5!GQC>e(4Y+t)nRsK>5UP*Anai>^0PeX;)DB&rs|#O0 z$kTVdPCUajfxk$Z8$Yuf7R@ww+LDI!f*p8OdmCFPUqYDgPXLRLVZc7@Ee0R~3J#E^}pt-uX_a8f-(|vd>4{ zrU2*hL|0`kvHNj!&ik_R{Q?#qNxEWYJR_@b@WCZUce|eHKLfH@E=4O`c~4hawaX@I z>Zvrn{vga^x$0cXo>UfhMg$hij$Mn1snt#(RLmKxO?WP0Te9QIeLpd%_i+$X!jSYu zmPF+Fyg;A&Y>JY6O;zfu8Vyam!*3ZH#lC1x1@j7420ivH%M)mIKJdI;!*1z;9%3Yt z3~3>Bk$(D)-|5niEIS!L8~q)G1VnzT28TyqHyop9a8$sajDmM&ZrFtzXR`77Go+uz zfW2+`8k`vl;?L(qBIO0-hok`1{zQuI`o?EioGgI*Y+LG_*RETX-{?PaDX<7fpZ+rV z-uMJFI7mM123oQWrzdw#N}pc@yqSX zF(SD8XfQ!thQ=?+jqG6JXs^^T*v8wLcZ2fsBsNIPls2}51FfBQHVm!AF%y%_Y2m}h(%5wq2S?6ImEOiOzc zV7WL5_9cSGQ`BDkgFx*t~wZ)Zc!!K>eR3GQDwIJlF0GrB)D2j9oPop zKw9P!Zk}EQlkab-Xu6R8e?1Fe4TuD@Uw?3LgZ{?&4fwO4iFHI!SFl+Ah9#X zVT|-Z*fF7@$Oz#Zvj1E3^D=f$#BRu3noq*Z7jTZq_ICJKL|-pHdjVAg7YizvG?E6n z3LcTWpSFzTi5sOACy=i+Quo=G_V5fJtH*nMgPlapZXxoh%JLJ6zt?m{NzxBjTu%TM z{+$+D`xzBi-QYI>3Tj3bLXyR|QS0Z7wx8~A*hc0BqpBUoZh38sWQ*U@vhMy@6Ho`-u#G(KdyDj{HnNO zxpq%Z^#WH#YW(LEUQVAKcP@LfyqWIjo1$|zUueP3ClEa?9L2{uW?J6XwB_6;^y@zi3hqf z)YN*KXnI$&aA%hfbCy34-is-E%k2&6;z6KEGJ*Ycn?<(RpojTWbHhbCL? z3PuGZNW9_?&ei=Q{CCyME#EJv&{5Kn1cS*8g*piuA5>LUwOqRkH*A2<$%&bYiYh)a zvDSK1i@N3DfkDi!hi@>M^=m1zC!?M%)LN8sV>-X&=RZdCS zj5p4*OPGM0Fcng8T1}2mHti^t$Uj*_tUnTypZC;COT*I_oJywgM0V!ez;^GVf_MVn z{NQdm^|kX<91tkmmY0rMVeUy~5FQD;L*uzMq8>ct!Ht(%m}9DTcpHnOO*enYf|ti&DMpfucCOAT*af}SME9@c5@+JVt;+Xc(W{#w^-Mf*7VCd18^)vki%AB zdP!!Ve6J#}2KMp6<&n*i&=oqsN>`QUNrLd@nLSEr0dr!Rh%!f|;cdpDd)519r__cs zkfL_$ck>d`Lu%4h)cG3VHb(s@*`3I-ngj&&s<@;|(B7DYRUNv+W}+UrtU}^*p!(H7 zBOu_a>k7u8vn_O&6=-m|T)6C}+<@ShK3cA7NA6gt*(rB3gA z7X?K@dBxheJk_?wVCW)Pc)3`qfytkHA@s zAlpR-kD`0yE_TA;OI|K`*9vkkSXGk}bN?oY2=C62V3y!kD{9m-iw8*P6k|>ADd6n3qo1-=PxQpbPx#lt} z25)aKL@z6a!8ot_*VcTbq%QXJvg! zt2IU|&XyQsZ*TM;!5DM_Z9{A^V&Kft6#v+{;QU{06~(i{ zmxjL`EUMExuL~$#ZRRxmHT6~*zI^8}Sh#F*z7lyY-}16dp6PLbFe5qw6VYT-+oyf6 z-ui65%0dm`lH>iUi^>uW&Y@(-(MS%D$K7sUR*OJzgnAIU`6}jcUr3PYRR}|&6B2H` za~9TO$j2x*Q}Qqvl{H=vqHeEGOt#%-TOS{PY>Gt)}@ zb+Wm4^nF_vJpa6I!3UiTm07=>NBAbgM1TqFioz_Z$I43uoQ3P&gU6R8Jn4UDUoKjf2zYZ>NJq8*@>zh`7nBcO1~|S zS%16{-+o#ViQUYeh#UeRVM!bZIdQ38gN1R|La2Y+j%s8!(|+Htmq$;TPqA-y9dcju z*fc_#W57tj6ykp^%Q#m>ZlQX`!EG)I&R^4*oU)sqH&)dRZ!?0i=6XeS_CgZ$SH!1# zi`TLYdDh5i3C81oLP9*=b-kGh0XUZk-;9g9)LX2mTByT2e+|HztOP02OE`NnVn5_> zkX4(i(!DF$hq1EG`HL}gJ(-+)t0lxR(@ct4djeP{Pc);ud|-ogdcFIR7BOhzk`wm# z&pRf@CqePmR31)(-pVF|(%D1XDnkfWp}0GjF;E#Z)%2tylqdT=E;@8Uj~5x1{!JuA zqe{-@@#4c)^1e^uRV**{N}?6aoT)C-yUaK1z7)-COj*h1=)TmI#CnU%!Dh@p0 za3p&VQnYB(us1H21o|t2H{>LvX>s>1a~#;ohC3{FX&r?HQ^07ctB(A^T&sEEqHf;$ zURqY>ZYh7UJnMX-+9>ZWEh)J#%=3aM{kF|C$D{qj_xLy`;234eQJed<3tlXljyZMp zAwi=}k(jx+w~)VL+485eMk2RJMmpcU+qDj1ua{Zc>B=X!p3i1<^~hG$##GdYIm?{4 zEzT10s5X?Q(a^|0SdPpf@n6ZdG1rHErtuaFP_}DCY|4tl!cH)LhTsS%dPc&`*g@1q z8m%EuXN{hE(Z0_5|1Omyv?yBD@Y=vEogmP(+{GzAEF_&mQX^=TFDW zh2^DYO;nSveM89~&zPrV=$2YPB!7IRS~7Y%L{w&6vjMXh@Ps{ZaXm{oh(`N$nM5fZ!aH#LXvwdW3;k1T(~MkceyAtFnDCc|h+_XOLiu4v7V50Be8{9rz_9yC&X zn(Q5?5#~QH;k-Kqx*kznOvwxp|1||uH7FRWwh~9g>*`7v40J;Pr<~#hyluZn#Q&=73Icp7G-lvLQVCYzR!X8;xV=}($)1z~N z?KX-txV&TyJq?{;zIjE&sK$6aj!H0CdH67_%k1#k>7guXafg`GWZgB){pWukEuidx z!P|l94_|#bX;xQr_2MpZnvzbZRiO{Z!uo~9{7Z{EU5;9@=XA86IG4hq{bv5N>y$v# z*wZmX+}9EpQF{n5`47f78#j`~I>WTEHGBPDbYAjv?wMhzq+UB)rqoaT?K@ zZh9JDwAg#BMSS`HE&kQ;oc^r|z+9cJt()!~!k6d8Ul}G-rYl+7=y{2U_*%?_8Ezs2yD> zRZBw?EMCZA3pWk4F4dREXJd)l%6tv7$*<^qkpEzBGw05^K?Gzjd0jWF6dN}*H&|K3gQ^kQ_SIjBUe5Rv4Yh0n zWf5&!yRsI|W}XUmTyU4Vfaz9iu2~z&8_l4Nwts8XbnV`KRSfmV*0`F;T{DElu2%%< zuv3kh_3qy+b8n%?_*RQA+!T-lNDP(LT09rEx{8 z+LSc1B4((3$~5ivmhV~&m|SgDCcCycoDEm)XAhj2N0my$V_Xj#6y;{WrYzCzOG&b$ zVIKHM2sa#px61B*wYS*zQ5>KxOB-2ST4@~=TwKFT>wWQkrf5HNJ#!QnRlU;F>8w7Z z20*Q)Jf?tuAU$FI!4oJ1w!gx=h@m;(HX$IeJ-inBbP*4Sq9_P^em$L_CNVLuPfnUE zg|zlBF5K#MDNLs<3`XbQVgBO+-1?HSV(VszUYuID`)yX)O|7$dR>ZEswZRT1YL9K9 z=0>m!xW7Hwnwgu2L_`n~68go*qrmiS?Tz>iL-kD~i^A5VvXryxGJuhqT(_<6XN1tyM#?69r#b z66LQq1|Zj?SRQ#K3B7yXVg1{4fwf;(V+^Pme9gIOx|Xj zN$o=KTFRV@^>tBumjT;mvj;Uu)yFy2S4z5Ecc(x-U84Pl=u?62S5sh`x5Ydg-hKF0 zpMVs{l(d}C-Pu|Ff=T%W1-j_qBvNMsDfE~FdRF+a&Za8=KX{t?e!eKv0ugb!*KtGj z1@*zeFOO&lDk@Zv2>=irtZEGj4*qlG1OS_*&ucb%^G znyxxu_>z&nd?0#rgZ#H`_*G%M%VBM zxP$(d{JQ(+-zu(=^RZ2){eeA=k9O0;s<3p(FLZxHrb=*Rj|gP(7_FS3;7|toBLDz^ z>Ya7MqgJWX_3YM`)TGQ8L_SCdP2;!vmGR`q2;7t=5YUSo{`Th?-UsHt>ja3XN#o&R zB(xKC0os+4yTVNd$P0Oxvuv0e{s>QEEZ1Ydpg29mscS~Js^)ZSWh_?8c3)t7EpJTw zf8qskza=X-guLyIDrg`bNe;AMjq27?OXVFGAK+5#Af)1LS+*L+f zx*6vHZ}M~|Q02N5e=2o(p2Au_{FQzD6ws|F8!cl)j^w(eF>1MT4pn$nb6a6ZiA5hI zJQ>!#x+xipU};cK(rRmKtB|z}OR7SNdeUCqgh)L)+)WEZKI()Jr#Nk0`)W9wc9nT# z=v=no>mPt`e_-<|fR<>_+JhM%$ONw&D#d3+?A6r;-v;qjIw{v^i49EM?I$XDd`bW8 zm-4zE#pi_MVD3i37;6^IQ>A6bJU+t2-HadGWhp?$%|HLI4B+LZkLmO%x`-Q1nw%_< zkG&Svb4KG2(h%A7(F`}$E8fBcT$S7uXE3(+?uE-V>5_VEeOh^5m0Of-3x5q#lNM(w zI)hqa($$(|bx~4iEMkZKnEAzjsJVxN&4Sku*M)5TyhRFf7OOUee?=d7o@INgQ2N zvd}YGGTE=K%Y+wOyCqm3=#1!wJgf_l_(WvF@{$|?)?wJ~%7vpH%5+_P>otDE&u{4& zcLf_4t9cNsp8{C@T%Ty|@)Fpd7kAZ(#~HDyJ4t|d1u`d)SYz_#157qLVn0`srt~+( zhKR(CvHKuWKhqC4C##f*ucjy1SR*U858uNZjO7V?-e7fteUcnJ4Z|SroUg;|SzDjD zYiYbIle(}TtagpP*G5N?w`#>%<%4W)oeykIWq@qz$w++J3rROGZq0YK_<+0WnXWo3 z<5_TgP%3pa+nF|fmFv?S0q+aPc;6FJlUyJZUSj^%l;GL&6Nc;3C%eclFPaYe_dLl% zm%314`1t0hgwqtq5ycLA>yBe|nl4v_*RvE(RRr%%X?B^fYLv?EZ=Y~Tvt&8bx#B;~3)5L8EAhdnnRqAH&));57L$K>^Sv}~mW(cTA zd&@DQ!=7fcr^`x}mX<<7L!XvQFZC!x^18aR$;!&^?d?@N4iQggJx=^?1R9(EHwT+)nB3+ek=oi2KVxO*Q;Tj~og=Z?^nj2O?X}4=m|#wM^EJpV;6_^i z{ebo_s{weQv8=iIFPEXHO)~$_`f5~`QZCRmNCUUS7-=Oo?OZ(Zwh4ZBxJwr@M()bL zqwiCasUe<8y#Jq42SjEtI0o%xJ*+V%viz=GNW;KyEbySmb0gqE2cXc<=4#oiG)*np z)|R-W#Pw`dE^q)zs+pq3`HaYnJa=ARkwD8xT585yyR<98A>`K)%Dx`9n?K1iG_H5Q z{fM6A=9R=uo-e_j)uzt9^W0eCVeN!%rwAq{00+Ouk)pBEw-J?iTV#mH`O#6Yr1@k^`@~)4wM?BdSs^OfapEw*!XBa<+{Sbx!UC#~}8 z69g;z;lhYCaF>m8<_)`_rO`9E?f&7`9O>)=SC&L*y@OhFu}Zt5KCXSf(DygBd#wxi z_UK^sDx>o^K&}kgN^2aEBe_{6>=@hVmq((lN#?Ciz+k(9pjWYeesx){zG=%}RG1&z z(SU@}CpQui?bg5xSq-1o^SOTBtNZuEdTapVIlPi`Y}n4afh|J{sgKAEC~(OA5(!YA z15=bFcf`fv7a^6~Vkj*Icm0^ncH7hylPA8~jMEQ^{=OiBLFXI5x5f3dce8ucnMc=M z6*}`M$Kx@OZtqC(?Y08v%yeNUAR?xYX6t1}sy^@3^^8A2l;5HBG_@pVuKBu^4u-7; z|L9s*7uTt3dA#ePL2vkH%nD)4ms}tl3jzfY&0fIWx4yi{8>aBl#edvUU87s|ncc)8 zZ+T>Z@LZ|&L#>(&Q%|*FH*Al1AyYT~p2WX1KozmZ#uh3PG;4eqLpb|%u$ISpy{#oA zX}$01jQc1VHK_HYmLfqsmg4y-I}K5PRmHP=B)tad63?_NkJxdI>HJWt%;Q8`^`H8~ z{Xg^vz<>3J&R@tbnT1_X=%o=b_{CLKB+SfTvX99;WxE;xQ8zBQu_uxpgEl!MBjPJ3 z;H-+ARGX?;=<-_J4J@j6SMpvMIoGrSpu!u;KYrzNhjzv6TUQv^HYC`j#WCg~>i6(n zM3C(^2)<5*1j2e_p(;?KLWkx~FD@>&NUF{;`Xh+M`sb1g2nl^LK1g_YVP+2nMQlPQ zhN8B>Hd9$*dafia)XcpaK?pXa%iXGgmbaS&hjgj$e874CeLEms;2&Y${S3F01xC z)a%;#v^XH>Uz<53t}NOAsp7Dd3GzU z(Le-6r;|51Z+hnqr9W27D`6oYsYc`pb_4~qNPW8^5fX#BE9i6vqzhS=B-CqC$$+>* zf8OMtjfo(?aGD~#w$|Lmq?)+@E_-_p6p_Lr&KDCvizKo@zP^!$pYlihlNTKR-(&awP9(4({b_ju~&Had!(E zq-`FSC%)HBNGjApFv%;N-NT=`JiLvs#Iy77YMI*g0*yN01Ge zl4Zgsr?Tp4km8m6W%s%<{ApMk)SxC*9eNMv_OroEzC(X?Y;XT5A#v!nOYmtCByy1y z&&7#Y6z5W#g4T5yVw*9eBLaJU%qaSdS7qTk#zbLcmHk^ z%Iz-ihFHaW_EH<09!Y3d4S#rr zFvIt{Q~22ABE-=9CDN((@p4MkUSvQ^BwO_M?zi?f&HC_Q(iaCGL3rJbd7;G^80DQH z9KYB){kM&=-{qe9kvj_Jihc!v}2=PlHv??^Gch}{MjtBJH=g~ zt5GF=C=eE?j7%Dfg6+@Wl4!<_3zIv-=>v9N&5Ai>c`*Pud8oBEM%eU<+RZ z7ZM1b@meVCyn1qB8}nvsPACf(V$94)OR~^YK7;7F;VO#G5)uI9-jfz5XxV8)i?&R< zra%%=x|y2N>uVDxt!R2lelL=tg={Bu&QHF zI9Ze7*dLK4k}D~6d`P{aV}X(qKNOP(F+vBkv%6bzkNE@(g;70>fU`)nGBF_t$$Q=D zXb_d*(;!^?IDGu4 zM#%72HR$SLTbU=0(*u|NIy;&vGw-5|EhjD zR9#1o6<}*<-%{1}=Pi@$tYPLY)qC*xt@sHx!#Es2JZs*qi?}nAO^|=yD=r*eD5;&A zUo$uDST7b^mf{o33fTwADyaCBwng`0FQ@kmqS>z2l#R81|Nqci04GjX&^yi;X@s59 z8Bo0`&JzkZ38>tjhD2ed2lnd0lWM;FwYsOb6L}E3Ql9Ig68Bu>iUt(u{7HtQM0r^#D0u>YtRj6`S zve58yrW#{m*v0gc{D2V^gX2y5MF$(Z0ScAAUeS?oeF3kZz$TpB%2wRZ9?#~BjKBez znx7FrHqQ5oB6AB(Nvx^7BRMLcbm74GPI$q8CCKKA%c2(XV6gk($p=~u37q|+W zUL2%6=AF~gdb78ub$0oq=*m4<;U9uazKpf?U6E~LPQ3E=9 zF2x?wL3kTae`Ih{}IHS;IdIGAOB zK1UUtvMz6YQicl5RelWq{a}jy@`r{3eKuMt{e5ClQ@hIKg=44h)&7@#1?Ep=cQ|Er zFtkmdTM(c1;Xw!_p3;xysYVUq zgZq-K##~-GSgv}s4Y;58&D*QZ=ovZ@hnlQ$27uBdZx<|w3~QJsp29dURcVvg(mXrY zeyZ&^MI|t3iky#qf}E}NAxTDUv@kwZJrl!Nl?O%F+=)Ebm{V31TQm&V?{5>6Bfs#j z(^Bc$-wt*2)jUu-pQOFsY{|V_7<~gB%bx0-k*s;>QVpKtBL*K#{1AMV`2T{ePm^&} zxT!67m_Q2~X^jmj3+gj5rgGAw6YTxk;pNFQNAKxslTcJt;JR`T*UT-H+`2!RuQ!AA z>&DstbKzqm?c}I=>8{>z)MlqU(jp;Q`+FohQ%Bb_F7&|C@c&kq{+AL3X!3TNyK!im4~GmRXLF zDt_^`#ZUrp9aCiGJ(4;c@r#5rubO*Q%)lm}265-QV}qraVi&lk zXhym?0q9{mQ;q1_ILYDu{lQEI*s{`4;CW$TA$ZjiFlGFtb>rD{=zj}UPO0j$jO2ce zOrULG*SpbPcU+eJe_j?@y|QDTd~bg^ilX<9&v#e|<#}k+dbg34nG@K~J{$P^2vE*J z_dZ*A6K+6BwYmvev-etiyTdG4BV_WV;8BV6`(*X;j@`>tH0+EAAA3;$WF&`s=v*v% z#k!UP`?tWxGtBHVv(uV6r5X_eqE(e-)Bx^>ikTXj^}a5dGf3SKA_?Ijr1?uSxo_ak zfCcZ&-0%X%9gq1`-8coZoGt{Q`i(3dk)NuZb2gN{u`QFY;ko5(i4R@m`&*|GD27To z`xtYn2pk$oWbrUT?7RRx;sn~PhsJ<7erXrF|DT2VXpQV9?A;V&DTHkp5_f}dJk7r` zg6=M{%-NmG1=&-)t~rq`VOce|4Gaoq_r~=mc)Z0Ea< z=8K%t=Mx^!%7mizJxzP09pE>kCTw7`A^civdj^9x^wq1G7AkG?t8P3FgBGczhqUcx z@MKC8|575NviKayqU}_;{S0HkUC|Y;*zl`t+#c-;B<%WxSSZmSX?=*`9T$A9mB%3> zxN9aepuwz^hcZ&31iQ9sRB8>OTpVd3^;M8YnFxTL=O8x~8zx#Pw7rATdrIV;Y=T;R z@LZT*bo&D0Ii?2H9~ahw${bIWgV)pVnrY05h)bV&L!nmPs6s=#iCjvc_lss5p03DY zQLfW(mhnqM44sNH2;i*<%dvCA)js&bw65(DER8I)%+2{0W)? zTL$pPDZ_K3{SNsPN%0*>Y4|O0UOBf6A?qUh*@n;xajqq+>s1(q-5O-{?=-1eX9(5I z;q?)dsDN1l=WACR9XloE2u>8>@o1|fN<5ZtFg)eWAQri0Y#rNLRMuD_7{i`hI)9=1 z(1s`=Ea|#uI_znM$aa~Z{;9jMUJYPG1boi#mkih3wob!z)rk!7Kl)Rv4$QrI>=nw* z_*t;PeE@5FAGi1O#0j-lA$y?ZrHVkel>--l{7zc z?0;HlcU#N{L3(+##sFIkLN*r@cs|HLB4Rrt@XI8=damkF;{;vlh^!UU#s^C&`oPL> zy4nOC0nAq@RGa;Y$+s?ds~7h%fM|mfVK-7lh8K@akuok4Oe@P$qS1uFsYCuH$A_~O zWS7M@dyd$nH#!)<@o@#a~Q~CUR#RR#W$PR_2gSr(;o`M;w+G zw4Pzv_W2uAxL~(US?k_KuUcbUg1G|w*Z*%xF0aatDbizFxZ|ZYvD`r%h0ofr+dlvC z*|@Chhf2Crt^6DvwwFcBclUhgB*OG71L!gy4j-bmq3MQIgrh6Q+<3E-bxD7+$!Si6m_BbWZ*qRiocZHh40DjT zmd!t0oiHUsVMU|Zw*KNs-!ank2$8V%6|FqDd7P4bSPaf#;JLA&?aFDW{&=u#c4e`I zWogn!>6Fd2gIaZZoPZ{vak*{P)qmKlq76y^v!rp3&y|= zlV`t0=PrJp(%F?Q#$^*c* z9`-ehyIgEY?7R(d-n_%sKLgghOyr4R2xWMJD7PQ5j#>rMwE30*GUZQ-P)+1{7~qy4 z-L6`MS6+V1UH-Y?c7QMSkgB!H?NOTrDhot2$>z&Y1gwU4hn1~+L8-^`fuVrX}G`EQ|F9s=E3rTS`Z3kM|kZzoRA@NJK|d# z;u!Bf=yAJkUvlznV}7{F(@*lYZ`Ewt{5nCZt~@Pfe{#4o1*azyx}hm!dG;0q?`E5^|iUP`!XhYDwvoLu|6?gX5NWCW1MgB zNadeW;p_81ox%=agWBmTZ7y;OrzJMacz8}rIYt%w+pMd1(KzO>o+zV%-4bP6c0lIy zmBHcWM|Cb-k>;R$)yY)b1b9 zbE{cXtkBSe*Zj%F@z)1K3;j|egv|YDLhy@14_Bl}f#F7*AMXl^igc7h2c!7l_ga2J zc&vbg%kWq>r#ndDC|n}hW?EJ-)gXlnRC2+o@0f$_D7-!7NVr2E?vS$H^VkoV~Z#r;<~Ue z0b+u7_xc=YSt4$M+GUx>f0WD(C!k_m2rlku12_=e^C~Wx_rbYK9^Xv_MK^@DUI#K^ zLeYiF3oag&_xpTH$CH1L?fQ^tmc-)xn>|9C1G$sZZ|U^0&yPK|%=eLox9#m)j2SgP z-Hx@OiI=66uF6S% z1z=#QT6K#aWSFB)&6o-i&VV+*nHcB5P?%`c$hO9nYXlRAw9xe@u8V3o{r!rwB6}xK{I2PzntnC??*gM+49yoA{11)Fxs4(*NNp%ft%^F zOqHAwA(@upzoBbD2^0ad~- z<-m%ql7X4}(rM+uE zPwl-=qyp1*zp)!@+|8}E!umgTC{dgC&rw` znAkm8O>IDrsJySb#u4d`5&CYG#Hy&CVfIiRY$Ua-)z=sQciXMSU0za$fH_w0j~}#Q zq>NJ)JC26qClvd|=sIIN+D7Fo)rRfm3Q5dVte!_7Mw7(b$qs~~Fge|{nPhvgv1Q{b zs`siLB^bh$2e~Tk*u$tEw-eop20g?EH@vH(AA_N5aMbgQImH$(oClSdQ8}Vrec=zr zQth{qXTna12_bwJ4+Z zx%zj4LGov}%f&=^Q*)5XWi(Y0_XEl0iwkf-D^1B>EuF`ugrM)Rp!*^CATbF;EIhA7 zaLVD}VYRy6Y*JGhKiTZ!GEXiiJ49M~vO~*%U^^63UDJd*PlmkACM_{}FK2M5EW2$) zyQd9t;&d*)TseA-|0gpZi+^`}$Yx4=r0(v+w1=QZCH>uF-?OQ+0P4X)1~P6m@$5@S z4Rp1XsmPb(Q*)M~9?)OCqszQm^hhf)y$wI!yJ7bB^8d`SdL105TmrJG$DMc-cx4o> zI;C+)>LnT%@JOl=rcyKO^Jl3g49cIZURrILJpPgLY-n-3re5{73&tASuuX}cbI+Gk z`S{B12ZlmfQqz;8c`8lZ_Jaltgt&Tn%QeT%YM`^o2)IjPTdQckHT!+3F@Ixe)>~$o zN41Jsz2A4_6Bxjg>|p0p8w=T(e;Gsd`Apd-)v5$ygZj#~W$27=q30@JSrk^=wpDjE zL(ZS4S8|tc?FG-x*cCxE9HgD+s<$j(LKts9<05DI?|g9&;O_Id#`tZJox+~S)3J8q zw{6*vbYM5y<{QFW5bFAUrhfv`G4(Lj)EWyL$a6=ZdX>JM%2$XvZqqNwlzt3UaNh4y z99>@6;LG(Txs0&x3vQ;zv9q@Tx}9AuV<-1FSg~~QJ=lc-9cfi-RgWk}TEdqmJ>2EG z9=e@coaEoZWkqqiN;E6?wmWW5N{A^iCGRmdlWr+zi|>+r+3Rn<&Qu!x7dxl=Cg*R9NXNhe7V zTla!~G`yX|n+f1}UU(KaN`+8IreFprdad30q4bty_FdEaVQV@3qVyPO6iK_cqnSRW z-Hsa&X)b>aM(~z#-S1C+&A$~rfNA`lRg+x^v_4l%?l2k{962F#*jl%yyp%d=pV=1 zs~XCsuIVu_O;w$@sc@zz9v7P+a!8oupOlrAt40!;w*w%;0`$+6czAu&D&pV#p#f*SNGwVq1=(9HBHRgh+2T0bnemp zQBq6}Tsu`2Ago@`^$F~Du~p^9He3e<4fe4Z0>3Yxkqs*=foj!%B?lGZC*l%gBa;!G z)BJ{_#3_<9IUQYP8V`E#WBV-9q-s1z)E6Sul=3l91}umMqRxfbMUdRN(<~oc zj8AF-#rfVR)A$@2C$D=(z2T<3^P8nWQ<`R2ZjIL`Y}g$MY^1&*pV=o-wbDiauW7XQ5#M(dmyaG3)#tk8uZ1wtVfK-9h#?G&%L`=QlzTA@W1e60wyX+ z0rjy(txWv%bZ%)rnC}o*uZ(1~0Qd#$!~qx4=+fM`7V}ALEhjdPPm^c>+qVs-IY0`!=wC(lbv!Z6TBf`wIF6#H>OdD$Z1qQ?s`d@C z63-YoeLAP*<9MrdB{9hu@+R%in1+upe3_Q_cxT;vXa0uc|B^?>M|g7(%n!&5Sqh;Fg&%r#b*RITVPM73ztll#5y2_U{&CuPz* zqaE0(Yo_0;o#uRoI}egS`D{O(h)zDHAfdZgI9Ylz9e6Z8>%Vlm-Rr#iek`l~d?V`~ z9jp-A2?vRXVDw}3n9hp|cNYmMPw@etZEQ?cvM zbZLQlHMKW`@G8*}K{4&^!B;T_Ws ze|_q%IjCgD$ocaTM{+n}Cpfb{{t*{r#EDwjFSla$NgeQ*NjIQ~LLGuFiE^3Z<}q(c zeWdO)zkAQX4OE_%y@~tr9rd4w)FIZm?zn&bdyTCW@yevP4L#!Ta(}0NFXo=;^l~ng z{?9E6we&Q}!5;o1E$)94(qF@3tN)vuZU6y7&ip?wv(H82nj-i&=VS;{8%EY7xW3xl zZ%H3*q6)v| zudjDT17t)nqD#Ga`qljsX8;3mqqH;$@v6V0|9=wI>0%Yy?` zpMBz9A4yvAw?Apj+E*h^)is$XTW4vdmrwHjqHqXKuqK*{Eu5fT3Zw|C#Eh!Soy0c`;%k$JY6VOW2 z_2BKr`5Nxt-oz0X(eJtiastxy59c1a{6!IIo&Q-pDPJQ=48gn1>urR=Dafd%r(Y6o zgaPeu5>`iND90~9hJzRz2;+?t2kuA350(Mgp||9o7Na$dR7x5ZoulOb1>C0@(Ea)X zJ{~0*t*p}W`XxRXOQ&G=L-Jm`fw+tAtMuYFB`-sEC5>9E=dt}9w*8Q8i#BB|<3?jG zDca_m)U1k&Yu=63m$*M#-CGUmYE^X6#bz_VMKT9qrG$95vkItGJSYJGq%Ja`vme_Y zg^G)oTcEFp{7zkU1bWvkoe>PcETcTXUu?rb9p`?BMUfZW;9>H@ccUb z*8WK$)`QPDy}XXzkKfPLvtHPZsD0Y2mfL~8J@%CLRGL(|B(l8x_1zmYZ8UkX@Q$Z( zah(}ojp6Vu2J=GKCDu=#Vnv}e1wTsvW zrH1cz``2?24##yj(KI8k{m{9j% zrYSWixzKgFzrPL71yew!a$}?w$#>23gXw+u?ZStQIlL)eRF8E}#W$@2o)m0U99eZ) za+>dAX~HAUmU0RLS$=n)TQ@)O@U+LpC6DLUH)nmz%DIB{Z?0r?MvqfD#_h`x(;!s2 z9%4xro2S2A8kckS^~PO- zx)}M`u$X3?nH;#Sp'EldU%9#-#%LI;066pag}S3;m_-P`N_EaR(n>GI=(2NVgT z)pgkhMfy^g^~XLQCI}0dkj)!@*K1?4RIzsmGHRq8pDN5VjpM_6LCBbtuPc%!RJz?- z4aZu35G$N`-yB5woa;UE%!4M&pDRgFkKFm6y&VXoN=D5dPT5s&*Pc4MegO=AGdmJj zmXnsx%A91M|2{A2w_eJi`4sQ~CtdCoav;CV&SiL;UTmraXMgs-Q)(h)%)OtuS)}su zC>>1aKdVXNzbW1PufhjE1`ATLo&n9=C?DCd#>zA-VMCW)}n9)Rc)amv6nRP>IYEXyda#u(wUYH9u+VeD~MlBJ74A`i!^$n2P$8#`R;Lk&aJ zM$bEum~3+9&n{$vRa<@HN{BL(IN0Q}p7*uor-nt{oIa|W@e22~7SXa;3^gt{Nnt=k~ib}cJI@#$dv2$#QQVSysrKn zWqju&tc8Wz!9ep*-uyWBf`Sc!W@6(qM78#k;_!O-EBMt41xu>g1{n5S7!xkLcr?3k z_4xn__66|G|G4V29t^C_-g-5^l?1^+Q|e2bdpa2{xAsJ~b>aLhk3%`uW_3c2NzjQ@ zzGdByga5c>Bwu+b)dYOPejX{ONO`u?S2-c{_xK!5ZV}*?5Je2#PiV*G_{+rXJTt8@ zT5o7eK7v@2Tc*g_4E`m>N5hG<>#WZXsKQxd&$J#On#~XZL+EMSH@2_lH|+FYEa-A| z3p?4ZsWvo+T~BYPGE&W?lkcwo8Bm^8beu0$9lc$uznu*Yk@=C4Gnhl=u+nt|u% z*IaCOU(twDA#)Dm7kTg`%#TA(DF4Zu2S#6qg7UW4j4uf^Xf|leIo+i%{xbhIjL1V^ zWqOu6-C38nuJx)~N!s#;6>|gxJL=kcivA2cW%QAo*3kCf6-%d!o=J#=3910L&Pf7dd0%}<9FO_}`#OsDt3W1w#ut-dTV z@k!Wbxy-6+o4--y9|cdq3DMB2o_viGUPwgVH$ELF=_aP3vnr>n%t^I&&8jMvfR?M_ z-v7G)N4i^xNe(uYyKV02@mz=b2|%|=fIWV5It?4or&+mFeT__XUa(m8T+|x=t$LT( z@$8ze+sw5U$T@4}uCVyr?Ln?0*qf+!{#UNvXBQ^=UyTSg+Vj)GD__I-sW**M-;Qc} z%r*?Csb1Lf{sc}^TBpk{XkkqquBt4=^T($;?H3sGr!*O1G}^C!_;%mh-PD9*6wm(ANhPWJ?_O-_k6#}fh_8e1zTWjb>HjT3CQ`*Qm+MnmVq%yEQZpE z*5g!8S02amLula(1@7(c_C7x&0eFpFJY~ki!&1!kV!PFAs%BAGyy0HHlQs07M)J+8aZqm`UP+&XjSDsP=H%CJlYMC7&+m9Jnhzm2SX+0sQr6{R#wO0Qw=uP%cD!$j z$@M~K$PXL^?~%(|!>tL)VrFdy%<8aD@YMFBt7mOZ_j4+07b{&&JO=7iJ!!!m1uuA`G^QETC$fvaP8&$;?!^@TttzyHsg+LGRyESn|%=k@oTygc|tw?bJ)I*rwj z5dWCynLf5S!|hI;0v6j6>5eU{Hz|jIU0h=2ws6EXBqF{@9-LK?+Fn?_uph-hll?@Q zfA_YiW{DmUk*Vaf>PPI!S~UhF3SW%3Z_#W>rUo1yd3i)cL=P9t-+c&$k*_sV)R(=c z>i#M;y*?rGvXfTfaO-f|NBYqI4Ln@BW_;*>0)RR=xgoWjFs%Cl^j;|~=TgjKIA5$B zQH+$Et@pvT!4HAxf#v6LWU*@{;(dS<0Q2brZTM8-OY0SbohPN`>AlK<-xe8A*f#g3 zQNA$nf!l@;V`P)+4yZ%2u?-0D05*^+@G+-%+uCeGe)nfohy_0+Z!Y&P^@{$?<5K2Ohs;T}Srj`yZS19A3a&&2VI z-zfXS(<28~OJ|T@b3*r%Is9ox-Hcvjn75!Up0A|3<*qHp5KIi?dpC#~8C#skcRbGP z^_*Y@pNr@KgT7O!tv|apJB8j!7&5|knLP>YzQ#NTKQPYm(;BuRC1!A&e=C=$ZryT= zJk1ECZx)Ebp^@b<>qohGmC))MuMgF5kAkR}G^VNmKWSWwDwg!cHs^>(=BpZ{hL>lt zH00q-{w*jr4@1LCh=Qm$cK{{Z&WZFxvlvy6h*AurBN86Tpb>i}kBs8B*waX36OK?A z{jm59R|W$!+iT|xwv5?tFC|TXfWLo_=+o4j+PFIhFMjIc&AP~SelPXjIwfAZolu^( zQSp75hhP5t0%tQhEiDKHaycn!jkDM!@oaU z{n2)qP1^4bsL-aC%VIXJ>zn3l-{@vSO3UBtYrC5s&CZ~&Sl>UjMV_$+$j_on$5eNU zRSkY<_x&w5x|fs~p6aQDOQ_;>osEj_cf);F=J{Bz;K$3+AG?+8$1R&kx|avH_aAz$d93UZ9h6NZqCQ{cwTcf^@Xe1Gi6igZQ?9{ zYbZh!G)k788C^f_SW%zu$%b=6vF=b+dWdJUr~{4r#X;`Bji0{i#6DJO8_@B*^9(%J z`3$76wba>SkLJJ{8jQBK^%Qr^1a94B&7Wd7Vu* zLpcEYT0LN~=2HWA$9U-lTwA48aNED~F4pm8j53h3>c$_AY3c__7B{Ug=sAW!;YjLua<7&;YHIBKw--!r~oz_->N?ac% zE{G%1t`_S(D2&8_-Vbkb53XM$g|;c`*tp=p=(*JJ-()Gv*+(^Dj8iG;hy_;&PBuhZ z9oX)!gJyX!+E-&2E%I9KUa#qKWZ~U!>}!E6n`a$m-(W9RzVTU}Q%lX!w=y70M=lwx zgw$<{>d$W;?n;f=u7e!7{#{U%jDM3^)n^9J4!&zJbKKTB^6F2dpDaG|SjTI}4?8U< zZwIm0#KCIiUl&4Z`Skvf7RUGoR~xgLNcaDVIBz>^>!7Cj8u4T@U%Q1wF~avGHUy?^ zG87ldH$&jCEOhlAXUYtMhbTSBe?IV;i1#;W7f6|SQ3e5c$LdqL2VRaOqJJS!6<`r> zLI=?P@Oo5{5Dm7O;TH$dARIRRj?e##5%QKlw2Hj98 z58Mrc{)KG31H3@};9D0L=lH8`c}1WL0VR#t)(?(dp0O&^Wj_(&@Sd0}!8%)d_Qk>`t{6l3URay8HM}{H~e>!!g__5Az(5gG+Z1{HXZsCdv zSZ1)^aCJSA4K7utPcO#Fm>>U`{XR!5u?xsFM$MGxv4`TAgR`1vUPDoJo;4ZJL z%FBg<9Dwm-)Z74t@MjQ(oE=AM{@?6NMVl@ZW}DOCq)X zr`oM&k*k;Q2oV4MjewVKh$5i|wS2Ko*S5>bXTT+ajBEAyY zs34()KD(qtPrNRhi-*Mw!LPZdhzDy`p`o{b)i-%RaY0B=hy?;scO7W_Y%sz2sOrWdO4oa1q!nw z1;{s*&k8j$+rnM5TLXla!Msp&6`vH!#$g*W%2n0Rmyl)o76=;V(@|F@Dz)^D&_Khk z%TjY-q{CkNsg_HlP(_}{jPb5Nr9Mw5{A;2HwR8gIfi@Rkd$6SsAxm;XO|TPmKyJ$m z{z4Rt#qe`2Ur_@E)unCZ=|)OOVclw8mjfKlNTUfhs z*bbX_qUQ)}D)#vCrermCz_y!~&BrB)-ST*Cqw1Q|zi9($OCYKH99Y)W83f(~VcGAr6N@-Ne)40}P%-*>gFcYw90hRe!N zabR5|qbg4pN?voVwtLR%OtEO0xOu}dGrqOO7RXBc7g_u{p}6M`!R#inNQ3GR{cj9F zdiG4^#U_nQwG6z+3CzV<<}muT(T@E@p$l7XW6c>gua)E;{+w9qtE{kj-YUMhlJNvW zA!%JT#-d7?Xb1PU@)EJZd_~^z*-l-cKFY`!dE0XxpBc4wApR5;M@nHUqxkl*Ltph$ z;J8hro8t&fu3%A6Yq$)1t7bBn#L#ar1wHPPp5fOP!BleiT=FaB$SM3mu;E~&M>l8D zL4eV+s^Vo1)kWwbkkD~);Owa)RUJaj&6k9*!O-CHv?9J(>&6MS=oq3gF4FQZFHvi` zb}4r3h^^r}2Ry_Qh=4N`;Ro1H>nvg&OBG9P97v@5EeNsr*PW=r<=rstuB9s1Vu?r_1{E%{=W!#+7O=r6yK zl4~QuEETLT%z8)ap3m#2ihANd*}J?`mAh0o%2`IWT6bsV4(E=P#o*UKJ-->nByOu$ z{Da!b(+Ifdk6AzUYTb0kFt@GUeX7}$({|bflMRn3so9Q%?iNzmjy@WqIljcRqr+*c zhEtA=YHNlPcsV$W;CYUY#H(8eS?hoHr9vRBY^G!?nj?+R7y2av_v)0 zYLlYjJhL2TnU^ggykD2;gj#V+`NNedM4%ZxDF;Q{So^t9b-IU`JJuNf_!Zv8`>b8} zSH}{1k=*xh#jkOx%yU~7V%HD&@96B`M2Y*yJRy5tfO^dy6)|}NLwmZQ++4jVw9c!i ztlo$1qKQg#2xQ@bhEl^hWW=+6+AvO*P=V1uCu@!22N_nAzon$8cvIWiu=Y>UF8t{C zKcD))%2H;0mYANfb-;qBw2vUZkO3iML2tkO{*GxUA08XTkRH5YZEPfi-` zoUzm~ZMl?1qH4?DoL}wfQ^BB<)EOL>@v{+wi~jWgSnz>=Z9XUk;AH6pq6mrhjlBgT zh~9x9mb>*d^gRtQpFw*b=j~>*8L{N)pOLzD5o$+N)NB(%O?sWawuTyFk?&kL-k}$$ zR3Y+`Ydu%R`i@!}noD8K;mqt4-iI+*hfH}{xKYCWk%a>cW-HnmY!HpAYVxdpVP>g6 zEznQ1u-(8vk{}b#ssWGe`xFHlr|tHSSoYPGL$L#Ya05Hy^ZD~@9(7;w(Cebnaa_z{ zEtd38LX#O4kB;PgPAhV`Ny{H;2=`JBFtPo?dyznPmE zerq7%G~r@ne2`DyprG=I^*k0-cf_hEHc}kB8S!D!q6Lho8f`>e@mFc?{A?^>^-}xd z{EEx&mThyFvx)(BN`hnz$A1ZOxoT}g`oi$yDW6Ff@!VkB6zzhusP_o>IsyrfbNrJq z2J|Q_P2a(-3JNv=>%bVc@f#4tAUphpJ6F5#&_K=x8fxf@YQu9ke+hjoX{pj7_{iz2 zQxP0;jjVi%wUale3p_f<%5)Ugu@WSDQ65Zl^)r$ZKagE^?HY^l!}zKl)4)^@pPhxY zcr+uM*e&$)kj7ZFALErDa84O>7;bY18nga9`C=ZUY5t<)hTgu4eh^9N-^?1i2fRWC z=k8VQW@rhfJSw64w}}k(|2p^C$r1n6y?{Dqb_;~PjvH&9s?8#jTiufg=&;_VTT8D` zyr!|iUj(1rK|fY!JLEwZ$`k{SQQMMEX}~*5%ck;$0k;$nilkpO?hdy*`wgc<3neK2 zJj1Lo(|H=|C^`a;i2Wq+GztA&s8q@ zj%~%2w9itHT(}#e= zI;6olk-M9c1xml z!OeWiTh&I-1H~jZf@c(a?oe5MeYM+bwUvvd`caB&{xyou=n{kEQ0b1Dzu2yPVvmu% zua1}I4|nyixcW5ML(Ac%?_=7Ab>H7(#IU|BSFV|XTCp~IoZu*h(GYARWr_+L(xybO zmiI?l85h+eJE^zXm`@Y6wX`+WI}WL~4X_$c1?Y8PZlrtOoYy}jJpHvFII`j5K3 z3Hit~8_(l4wqjehhXQs z9A!=t5Fz~`da(dl^02=l;Z~}?!=9pgQNru_C$tLxN(xg#p)QOSb$Ub&7+aH=!bCa5 zLZR+o%>7$~|Bvcy{aJbJfC(bM8TuI_^LJ(f$>DNr$;`ozL?j2F`{+Wj)j@Bm|5mrt z5nA1K|1Bn+V21EXJo~eO0r??FiLe9Ip&>k{E#Su7b9boqLVxVHCs9Nf=UNg!zdZIuvro1ItE zr+)~iztecKg3i7w!*Ty>@qn%L?bQpkU~lR@`&I+szkiM<_3Peg`PYcS;4R{6l!vU> zRDZ;o%BaalHT+Cv`towfW~SiJ<$uwKOBuR5`yk2cX9exbs>W(vciMI=0@sZpRJxc` zId^I21n@^4tT-Kfna@rAsxRHP#yveMT2odKm-#+DP1{wlEW+1xL@?hWScR*9v0UC~ zznJuAv!GR}nrnim>d|t2bOepAzR5zch9w|WG`7BZ+=CBnIp90^P~%Hb`$S?Xah4^n4k9uo+Y{7$C{5>kg*-n_d9{_N>tN4}W2Rl*n2(rio> z6?QTovWmEdRiZ&1m!;Kc=7}nnbZemf9}Kj4F-k1)+;j{EzQ+A!BP#rza((qrW~E5x z5{%g{>Ga64UH0%&xMFFCdvzUVTQXiqRbWFp02-XQ0#W_>L>8!58xM@cE)kq?VDd7Y zJH-Dj`Rz+1W9Z{=EX_$n~GCQDI>A5`j+wAL51U(+$Ct; zCCh;73uK%}*(nt1xgycI2;9c3zu${H0G7MD-P7J%A_xqyi3zwCdUDnDO;=cn9-Mr2zdNLgC zP~j$=Qo+f;WIH-P(1u)I{st|hCHztR->EFb^VwGV9E9o zFYK4aY2OJGlj-nJ>9xaOVaR+o?iZu)>raT2FOZ41Yhwvo84HE(FCQ@bnkEU3HS2Fpc`$CbDC2GcmgOr z{iyYZf;$4h+znuo@n0AoR9>k%NZpm)3G=a8JqNR4(iNdcIaapmVFf4jMtb+;BO{rm z{m$U8<34<1Z-djv>Xe|Dt`MBe$n2pIqO?UXba0YAd_m-gO-bQ&yDLZ!N1rcl<3}au zD9JsC{Jp`PL&hi?@~ycHHSN8;(Ybg$*bY!vUm7Bp(zH`xM>jq7E-4s0g^f9FQ4Uqb zKTA1V=-m%yvzSq(wZ7V1V}=NyU?U?T_iIzdjs#E~9rs}U=5daIZ-`vq&x^#5QT08E z+);5#=HA`w#JCs;U9?TU3DB7w23Z+5N~^M{TW*0X%rK{^=}DR-MGeAGAfETb5lDWN z^4vARN{a1R#X%f7wj_zI#8Y^M=7*)$sqOo7PlNFQ{qS1neoK2N6TP+8JyapGBuE8h zhQ{BxwwqA_7iK+#qTV#KGF}PfpENKA%GVUzS3%=#NA1u0B9I)&LYc_!o_G{xzua1Nrkrw5OIatrN_hMH^ewuo1*$8`R_YKw}PV3@b zk|;Q>gQo19a#_sD-lQj-^kH2X>|w*XJ;f_P@X>?c^l5caj@Cn!3wzrfjVYU|?`k9u zUa{u3a~PwP_bBp642l7n`}Hpt(fZIj*OSkbn;4Yq6|Q|>%iOEKo8}uaAyKnAWJ}BS zyS?AFYf7UKs9J^@4;#J2Ec?UC*gGy>_^4rq{`%oTL;a_cQNi3kwGLHeeV>HUn&)Vc z&UE>Gj~r^g!g4$DL515iuy~_K-lgofk`DVmNI#kD&%M|=Ah~(Jah|B%QQV8Z-7!4M ztffq^Jv|puRdDj4LGxBy-+8(R#B|G#vtCdQH5Xr$&p&9XfcLhZfm(#C8|OR+*tUHZ zJXq@bZ2L9t86|l}WyA}}C`bk?Rrp*toG~eu`!AmIvku(f@?)I8*u;4I`tX8Xsy%4B zg^{lk<0yxf5A)v`Daol&L~>_-s!65|eN)gU5nn^)nL$E85%i;ZYD6D7WRjaRwgh_G zH#4PKt0G+1?uk7E5nLi6!1smR9S{(#OQiLJ#71BN;}zsg3%yI<%>2ofFG|l_-jEM3 zNogK?eV-3rgW3-Ap_S}Oe5mARC2Ujn@_~HuUlgW`~5Pr1T5CpGu zM%5`tcq5o>F1XAe3N=3wLqV)%lREWoGlqe*qX8@-*d?43ovG(-2CAwT5@mBJZPs+kYXTB|o9_-HI zt3V>M*r(s;j4CeMO%Z zze;tGMG)F~vqXMh)}JLTeMC_kD3fd-L;JG0PDDZI>2xsia|0ml{YE`KCl2wVM?yj# z^%eX_hz}nifWdV(PeAxOB;=;w>4EDf#r$+rKETC4VrrXeXSPn>T&PL7Hynm7f?Or(r|2UiX-wFn}<|MGE}U^x0*u>RjFP>~d6bMTcI;`tL3`|+{!)!HYUUt; zTE1uhk>W(AB4o^hDs$Wvxpa@0<#M`j5y~tCXgC;BJljgp2lPt*u{Phn)*rxP8+e4ML2&vad@j zj3-`du@@KKm@X~wZnF`0`|df9Id7{YYVAU2Q?`^7mmwx`^W=@QzM|DOrX(Kb8|QcQ zVN7;wR7NIFOI1|wKvTJi+08lbdr%G6*~dTOpD*p>^h^))0AYV`O~Re9RbCWZA5Ls;ampnenHau0P++ zs}b*w{Snf1eyJ@@Y;@m^)9j9~Llr|e(~Ku-ZnmfsA52pYAJ>$`TiYx;@k_nF31^^; zXt=~+dhVa<`c}5Cw0)E@&(YO4?QZBxrgJ)P-jkxdDz?MqgxJL@ZlZVAsR*~lRL%-2 zZAnR+?IPH0OY2Z)&Jv4P^I5PC!}8Cr*%u$m)Mie_g|czGoo&OMmQAE+7;e`)R=Yl| z;ZHQb4_jY_(5*(R3!=C%UPU|-jmxbJXBl^dYY_eEp}g{bH-m;nxfv4+y*y~H^8S2P zYzT_;;U6W#D%w1LW*^o6i)D;5K>v(_59^`4q}AhW6~7GWo1`p<&w+^J?UnR--IIL; zbpYEaUjm)7h|l}TZ@nfA zf!UeUy2oh%sbKdj#;J%`+1{xw(h_3m3lYy}$)n^~k@-fLgjL=qx>Jm%FVlvFIO+@;7k# z_tNM@;U9k&a~d1s)nZ~qGhFfUpF From ca68f94304a66723ff2267bad181b41ed6f7691f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 6 Jan 2013 18:49:40 +0100 Subject: [PATCH 191/503] mkv: make sure the first cluster timecode is 'close to zero'. when recording to disk, the subscription is started ~30s before the scheduled event actually starts. This causes the pts time stamps to have an offset of about 30s. This patch moves the start time filter condition from the end of the pipeline to be a configurable property of the tsfix pipe. --- src/dvr/dvr_rec.c | 5 ++--- src/plumbing/tsfix.c | 16 ++++++++++++++-- src/plumbing/tsfix.h | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 6dc560dd..9fbf1e58 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -81,6 +81,7 @@ dvr_rec_subscribe(dvr_entry_t *de) streaming_queue_init(&de->de_sq, 0); de->de_gh = globalheaders_create(&de->de_sq.sq_st); de->de_tsfix = tsfix_create(de->de_gh); + tsfix_set_start_time(de->de_tsfix, de->de_start - (60 * de->de_start_extra)); st = de->de_tsfix; flags = 0; } @@ -419,10 +420,8 @@ dvr_thread(void *aux) switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: - if(started && - dispatch_clock > de->de_start - (60 * de->de_start_extra)) { + if(started) { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); - muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); sm->sm_data = NULL; } diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index dfab483c..5fb1febf 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -55,6 +55,7 @@ typedef struct tsfix { struct tfstream_list tf_streams; int tf_hasvideo; int64_t tf_tsref; + time_t tf_start_time; struct th_pktref_queue tf_ptsq; @@ -309,12 +310,11 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) tfstream_t *tfs = tfs_find(tf, pkt); streaming_msg_free(sm); - if(tfs == NULL) { + if(tfs == NULL || dispatch_clock < tf->tf_start_time) { pkt_ref_dec(pkt); return; } - if(tf->tf_tsref == PTS_UNSET && (!tf->tf_hasvideo || (SCT_ISVIDEO(tfs->tfs_type) && pkt->pkt_frametype == PKT_I_FRAME))) { @@ -387,10 +387,22 @@ tsfix_create(streaming_target_t *output) TAILQ_INIT(&tf->tf_ptsq); tf->tf_output = output; + tf->tf_start_time = dispatch_clock; + streaming_target_init(&tf->tf_input, tsfix_input, tf, 0); return &tf->tf_input; } +/** + * + */ +void tsfix_set_start_time(streaming_target_t *pad, time_t start) +{ + tsfix_t *tf = (tsfix_t *)pad; + + tf->tf_start_time = start; +} + /** * diff --git a/src/plumbing/tsfix.h b/src/plumbing/tsfix.h index 7e6f7bd0..156284fe 100644 --- a/src/plumbing/tsfix.h +++ b/src/plumbing/tsfix.h @@ -23,6 +23,8 @@ streaming_target_t *tsfix_create(streaming_target_t *output); +void tsfix_set_start_time(streaming_target_t *pad, time_t start); + void tsfix_destroy(streaming_target_t *gh); From 55925e7714caa2e379d25ec9aa888f7ba3777e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 6 Jan 2013 18:56:39 +0100 Subject: [PATCH 192/503] dvr: rearrange the pipes so that globalheaders gets the packets before tsfix (and the start time filter). --- src/dvr/dvr_rec.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 9fbf1e58..092b8762 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -79,10 +79,9 @@ dvr_rec_subscribe(dvr_entry_t *de) flags = SUBSCRIPTION_RAW_MPEGTS; } else { streaming_queue_init(&de->de_sq, 0); - de->de_gh = globalheaders_create(&de->de_sq.sq_st); - de->de_tsfix = tsfix_create(de->de_gh); + de->de_tsfix = tsfix_create(&de->de_sq.sq_st); tsfix_set_start_time(de->de_tsfix, de->de_start - (60 * de->de_start_extra)); - st = de->de_tsfix; + st = de->de_gh = globalheaders_create(de->de_tsfix); flags = 0; } From 196780df42317682ea3fe3ff172e6039157a643d Mon Sep 17 00:00:00 2001 From: Denis Pellizzon Date: Thu, 6 Dec 2012 21:48:58 +0100 Subject: [PATCH 193/503] Fix #1497 - Added error checking if 'git describe --dirty' is not supported by the host. --- support/version | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/support/version b/support/version index 8f74a65b..e767cb62 100755 --- a/support/version +++ b/support/version @@ -8,7 +8,13 @@ FILE=$1 # Calculate version if [ -d ".git" ]; then - VER=$(cd $(dirname $0); git describe --dirty --match "v*" 2> /dev/null | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") + VER=$(cd $(dirname $0); git describe --dirty --match "v*" 2> /dev/null) + if [ $? -ne 0 ]; then + # Git describe failed, maybe "--dirty" option is not available + # Adding "--UNKNOWN" postfix to mark this situation + VER=$(cd $(dirname $0); git describe --match "v*" 2> /dev/null)--UNKNOWN + fi + VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") else VER=$(head -1 $(dirname $0)/../debian/changelog | awk '{ print $2 }' | tr -d '()' | cut -d '-' -f 1) fi From 7c85cc101434883122c5f3f1303c4698f94233b1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 20:46:23 +0000 Subject: [PATCH 194/503] muxers: re-org to clean up dirs. --- Makefile | 8 ++++---- src/muxer.c | 4 ++-- src/{ => muxer}/muxer_pass.c | 0 src/{ => muxer}/muxer_pass.h | 0 src/{ => muxer}/muxer_tvh.c | 2 +- src/{ => muxer}/muxer_tvh.h | 0 src/{dvr => muxer/tvh}/ebml.c | 0 src/{dvr => muxer/tvh}/ebml.h | 0 src/{dvr => muxer/tvh}/mkmux.c | 2 +- src/{dvr => muxer/tvh}/mkmux.h | 0 10 files changed, 8 insertions(+), 8 deletions(-) rename src/{ => muxer}/muxer_pass.c (100%) rename src/{ => muxer}/muxer_pass.h (100%) rename src/{ => muxer}/muxer_tvh.c (99%) rename src/{ => muxer}/muxer_tvh.h (100%) rename src/{dvr => muxer/tvh}/ebml.c (100%) rename src/{dvr => muxer/tvh}/ebml.h (100%) rename src/{dvr => muxer/tvh}/mkmux.c (99%) rename src/{dvr => muxer/tvh}/mkmux.h (100%) diff --git a/Makefile b/Makefile index 63629f4a..d8841397 100644 --- a/Makefile +++ b/Makefile @@ -127,8 +127,6 @@ SRCS += src/plumbing/tsfix.c \ SRCS += src/dvr/dvr_db.c \ src/dvr/dvr_rec.c \ src/dvr/dvr_autorec.c \ - src/dvr/ebml.c \ - src/dvr/mkmux.c \ SRCS += src/webui/webui.c \ src/webui/comet.c \ @@ -138,8 +136,10 @@ SRCS += src/webui/webui.c \ src/webui/html.c\ SRCS += src/muxer.c \ - src/muxer_pass.c \ - src/muxer_tvh.c \ + src/muxer/muxer_pass.c \ + src/muxer/muxer_tvh.c \ + src/muxer/tvh/ebml.c \ + src/muxer/tvh/mkmux.c \ # # Optional code diff --git a/src/muxer.c b/src/muxer.c index b0c9d99f..6ec3a8a1 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -21,8 +21,8 @@ #include "tvheadend.h" #include "service.h" #include "muxer.h" -#include "muxer_tvh.h" -#include "muxer_pass.h" +#include "muxer/muxer_tvh.h" +#include "muxer/muxer_pass.h" /** diff --git a/src/muxer_pass.c b/src/muxer/muxer_pass.c similarity index 100% rename from src/muxer_pass.c rename to src/muxer/muxer_pass.c diff --git a/src/muxer_pass.h b/src/muxer/muxer_pass.h similarity index 100% rename from src/muxer_pass.h rename to src/muxer/muxer_pass.h diff --git a/src/muxer_tvh.c b/src/muxer/muxer_tvh.c similarity index 99% rename from src/muxer_tvh.c rename to src/muxer/muxer_tvh.c index 8555ba91..02846ae5 100644 --- a/src/muxer_tvh.c +++ b/src/muxer/muxer_tvh.c @@ -22,8 +22,8 @@ #include "streaming.h" #include "epg.h" #include "channels.h" -#include "dvr/mkmux.h" #include "muxer_tvh.h" +#include "tvh/mkmux.h" typedef struct tvh_muxer { muxer_t; diff --git a/src/muxer_tvh.h b/src/muxer/muxer_tvh.h similarity index 100% rename from src/muxer_tvh.h rename to src/muxer/muxer_tvh.h diff --git a/src/dvr/ebml.c b/src/muxer/tvh/ebml.c similarity index 100% rename from src/dvr/ebml.c rename to src/muxer/tvh/ebml.c diff --git a/src/dvr/ebml.h b/src/muxer/tvh/ebml.h similarity index 100% rename from src/dvr/ebml.h rename to src/muxer/tvh/ebml.h diff --git a/src/dvr/mkmux.c b/src/muxer/tvh/mkmux.c similarity index 99% rename from src/dvr/mkmux.c rename to src/muxer/tvh/mkmux.c index de9d8dff..da6a34ba 100644 --- a/src/dvr/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -27,7 +27,7 @@ #include "tvheadend.h" #include "streaming.h" -#include "dvr.h" +#include "dvr/dvr.h" #include "mkmux.h" #include "ebml.h" diff --git a/src/dvr/mkmux.h b/src/muxer/tvh/mkmux.h similarity index 100% rename from src/dvr/mkmux.h rename to src/muxer/tvh/mkmux.h From cb832436819dad8692332ee3b16f60b5ac6fb905 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 6 Jan 2013 20:47:26 +0000 Subject: [PATCH 195/503] support: minor tweak to version script. --- support/version | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/version b/support/version index e767cb62..7d5c40ea 100755 --- a/support/version +++ b/support/version @@ -11,8 +11,8 @@ if [ -d ".git" ]; then VER=$(cd $(dirname $0); git describe --dirty --match "v*" 2> /dev/null) if [ $? -ne 0 ]; then # Git describe failed, maybe "--dirty" option is not available - # Adding "--UNKNOWN" postfix to mark this situation - VER=$(cd $(dirname $0); git describe --match "v*" 2> /dev/null)--UNKNOWN + # Adding "-unknown" postfix to mark this situation + VER=$(cd $(dirname $0); git describe --match "v*" 2> /dev/null)-unknown fi VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") else From 5f41fde7b63e88f997d9e767c3e1cad7191bdaff Mon Sep 17 00:00:00 2001 From: BtbN Date: Sun, 6 Jan 2013 21:52:28 +0100 Subject: [PATCH 196/503] Add variable frontend/demux to tda_add --- src/dvb/dvb_adapter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 75681b5e..7e85382a 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -445,7 +445,7 @@ check_full_stream(th_dvb_adapter_t *tda) * */ static void -tda_add(int adapter_num) +tda_add(int adapter_num, int frontend_num, int demux_num) { char path[200], fname[256]; int fe, i, r; @@ -453,7 +453,7 @@ tda_add(int adapter_num) char buf[400]; snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num); - snprintf(fname, sizeof(fname), "%s/frontend0", path); + snprintf(fname, sizeof(fname), "%s/frontend%d", path, frontend_num); fe = tvh_open(fname, O_RDWR | O_NONBLOCK, 0); if(fe == -1) { @@ -468,7 +468,7 @@ tda_add(int adapter_num) tda->tda_adapter_num = adapter_num; tda->tda_rootpath = strdup(path); tda->tda_demux_path = malloc(256); - snprintf(tda->tda_demux_path, 256, "%s/demux0", path); + snprintf(tda->tda_demux_path, 256, "%s/demux%d", path, demux_num); tda->tda_fe_path = strdup(fname); tda->tda_fe_fd = -1; tda->tda_dvr_pipe.rd = -1; @@ -651,7 +651,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) /* Initialise hardware */ for(i = 0; i < 32; i++) if ((1 << i) & adapter_mask) - tda_add(i); + tda_add(i, 0, 0); /* Initialise rawts test file */ if(rawfile) From 16c7e553502ff960b20c41935989460dc80d6945 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Mon, 7 Jan 2013 01:34:23 +0400 Subject: [PATCH 197/503] add verimatrix to list of known caid names --- src/psi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psi.c b/src/psi.c index 07f7d3cd..b4e1a685 100644 --- a/src/psi.c +++ b/src/psi.c @@ -902,6 +902,7 @@ static struct strtab caidnametab[] = { { "DRECrypt2", 0x4ae1 }, { "Bulcrypt", 0x4aee }, { "Bulcrypt", 0x5581 }, + { "Verimatrix", 0x5601 }, }; const char * From d19b9b85a1fb0e55d0a4bde2cc909b4c5663517d Mon Sep 17 00:00:00 2001 From: BtbN Date: Sun, 6 Jan 2013 22:28:00 +0100 Subject: [PATCH 198/503] Add new tda_enabled flag and close frontend after getting information so other frontends can be proped --- src/dvb/dvb.h | 4 ++++ src/dvb/dvb_adapter.c | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 80d8c666..5f6cfbb7 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -200,6 +200,8 @@ typedef struct th_dvb_adapter { int tda_table_epollfd; + uint32_t tda_enabled; + const char *tda_rootpath; char *tda_identifier; uint32_t tda_autodiscovery; @@ -342,6 +344,8 @@ void dvb_adapter_stop (th_dvb_adapter_t *tda); void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); +void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled); + void dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on); void dvb_adapter_set_skip_initialscan(th_dvb_adapter_t *tda, int on); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 7e85382a..3d727f12 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -80,6 +80,7 @@ tda_save(th_dvb_adapter_t *tda) lock_assert(&global_lock); + htsmsg_add_u32(m, "enabled", tda->tda_enabled); htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type)); htsmsg_add_str(m, "displayname", tda->tda_displayname); htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery); @@ -124,6 +125,25 @@ dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s) } +/** + * + */ +void +dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled) +{ + if(tda->tda_enabled == enabled) + return; + + lock_assert(&global_lock); + + tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" enabled set to \"%s\"", + tda->tda_displayname, enabled ? "Enabled" : "Disabled"); + + tda->tda_enabled = enabled; + tda_save(tda); +} + + /** * */ @@ -473,6 +493,7 @@ tda_add(int adapter_num, int frontend_num, int demux_num) tda->tda_fe_fd = -1; tda->tda_dvr_pipe.rd = -1; tda->tda_full_mux_rx = -1; + tda->tda_enabled = 0; tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); @@ -482,7 +503,9 @@ tda_add(int adapter_num, int frontend_num, int demux_num) free(tda); return; } - tda->tda_fe_fd = fe; + + close(fe); + fe = -1; tda->tda_type = tda->tda_fe_info->type; @@ -540,6 +563,8 @@ tda_add_from_file(const char *filename) tda->tda_fe_fd = -1; tda->tda_dvr_pipe.rd = -1; + tda->tda_enabled = 1; + tda->tda_type = -1; snprintf(buf, sizeof(buf), "%s", filename); @@ -588,6 +613,11 @@ static void tda_init_input (th_dvb_adapter_t *tda) void dvb_adapter_start ( th_dvb_adapter_t *tda ) { + if(tda->tda_enabled == 0) { + tvhlog(LOG_INFO, "dvb", "Adapter \"%s\" cannot be started - it's disabled", tda->tda_displayname); + return; + } + /* Open front end */ if (tda->tda_fe_fd == -1) { tda->tda_fe_fd = tvh_open(tda->tda_fe_path, O_RDWR | O_NONBLOCK, 0); @@ -642,7 +672,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_t *l, *c; htsmsg_field_t *f; const char *name, *s; - int i, type; + int i, j, type; uint32_t u32; th_dvb_adapter_t *tda; @@ -651,7 +681,8 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) /* Initialise hardware */ for(i = 0; i < 32; i++) if ((1 << i) & adapter_mask) - tda_add(i, 0, 0); + for(j = 0; j < 32; j++) + tda_add(i, j, 0); /* Initialise rawts test file */ if(rawfile) @@ -675,6 +706,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) tda = tda_alloc(); tda->tda_identifier = strdup(f->hmf_name); tda->tda_type = type; + tda->tda_enabled = 0; TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); } else { if(type != tda->tda_type) @@ -684,6 +716,7 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) free(tda->tda_displayname); tda->tda_displayname = strdup(name); + htsmsg_get_u32(c, "enabled", &tda->tda_enabled); htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery); htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan); htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose); From 77b3057f76584212a9aebb09c0cdc0e002fc2c59 Mon Sep 17 00:00:00 2001 From: BtbN Date: Sun, 6 Jan 2013 23:01:22 +0100 Subject: [PATCH 199/503] Add new dvb adapter enabled setting to extjs --- src/webui/extjs_dvb.c | 4 ++++ src/webui/static/app/dvb.js | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index d1ce5b15..0919e5ac 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -145,6 +145,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) if(!strcmp(op, "load")) { r = htsmsg_create_map(); htsmsg_add_str(r, "id", tda->tda_identifier); + htsmsg_add_u32(r, "enabled", tda->tda_enabled); htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached"); htsmsg_add_str(r, "name", tda->tda_displayname); htsmsg_add_u32(r, "automux", tda->tda_autodiscovery); @@ -173,6 +174,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL) dvb_adapter_set_displayname(tda, s); + s = http_arg_get(&hc->hc_req_args, "enabled"); + dvb_adapter_set_enabled(tda, !!s); + s = http_arg_get(&hc->hc_req_args, "automux"); dvb_adapter_set_auto_discovery(tda, !!s); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index c3a3f314..a3808527 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1095,7 +1095,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { var confreader = new Ext.data.JsonReader({ root : 'dvbadapters' - }, [ 'name', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', + }, [ 'name', 'enabled', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', ,'disable_pmt_monitor', 'full_mux_rx', 'idleclose' ]); @@ -1116,6 +1116,10 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { name : 'name', width : 250 }, + new Ext.form.Checkbox({ + fieldLabel : 'Enabled', + name : 'enabled' + }), new Ext.form.Checkbox({ fieldLabel : 'Autodetect muxes', name : 'automux' From 757a06b7d8b638bc64fac583c84373d91a6c0e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 31 Dec 2012 00:07:44 +0100 Subject: [PATCH 200/503] improved/fixed the calculation of the htsp buffer delay --- src/htsp_server.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 50ba204f..2a4b6af7 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2067,7 +2067,7 @@ const static char frametypearray[PKT_NTYPES] = { static void htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) { - htsmsg_t *m, *n; + htsmsg_t *m; htsp_msg_t *hm; htsp_connection_t *htsp = hs->hs_htsp; int64_t ts; @@ -2135,13 +2135,29 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) pthread_mutex_lock(&htsp->htsp_out_mutex); - if(TAILQ_FIRST(&hs->hs_q.hmq_q) == NULL) { - htsmsg_add_s64(m, "delay", 0); - } else if((hm = TAILQ_FIRST(&hs->hs_q.hmq_q)) != NULL && - (n = hm->hm_msg) != NULL && !htsmsg_get_s64(n, "dts", &ts) && - pkt->pkt_dts != PTS_UNSET && ts != PTS_UNSET) { - htsmsg_add_s64(m, "delay", pkt->pkt_dts - ts); + int64_t min_dts = PTS_UNSET; + int64_t max_dts = PTS_UNSET; + TAILQ_FOREACH(hm, &hs->hs_q.hmq_q, hm_link) { + if(!hm->hm_msg) + continue; + if(htsmsg_get_s64(hm->hm_msg, "dts", &ts)) + continue; + if(ts == PTS_UNSET) + continue; + + if(min_dts == PTS_UNSET) + min_dts = ts; + else + min_dts = MIN(ts, min_dts); + + if(max_dts == PTS_UNSET) + max_dts = ts; + else + max_dts = MAX(ts, max_dts); } + + htsmsg_add_s64(m, "delay", max_dts - min_dts); + pthread_mutex_unlock(&htsp->htsp_out_mutex); htsmsg_add_u32(m, "Bdrops", hs->hs_dropstats[PKT_B_FRAME]); From 39a74172ad50b9026979c047ab6377c28a07636a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 6 Jan 2013 22:31:08 +0100 Subject: [PATCH 201/503] detect commercials on swedish TV4 and skip them while recording --- src/dvr/dvr_rec.c | 20 +++++++++++++++++++- src/plumbing/tsfix.c | 29 ++++++++++++++++++++++++++--- src/plumbing/tsfix.h | 2 ++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 092b8762..0efe1223 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -400,6 +400,7 @@ dvr_thread(void *aux) dvr_entry_t *de = aux; streaming_queue_t *sq = &de->de_sq; streaming_message_t *sm; + th_pkt_t *pkt; int run = 1; int started = 0; @@ -417,8 +418,25 @@ dvr_thread(void *aux) pthread_mutex_unlock(&sq->sq_mutex); switch(sm->sm_type) { - case SMT_MPEGTS: + case SMT_PACKET: + pkt = sm->sm_data; + if(pkt->pkt_commercial == COMMERCIAL_YES) { + dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0); + tsfix_set_comm_skip(de->de_tsfix, 1); + break; + } + + dvr_rec_set_state(de, DVR_RS_RUNNING, 0); + tsfix_set_comm_skip(de->de_tsfix, 0); + + if(started) { + muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); + sm->sm_data = NULL; + } + break; + + case SMT_MPEGTS: if(started) { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index 5fb1febf..4fea8751 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -40,6 +40,7 @@ typedef struct tfstream { int64_t tfs_dts_epoch; int64_t tfs_last_dts_in; + int64_t tfs_drops; } tfstream_t; @@ -56,6 +57,7 @@ typedef struct tsfix { int tf_hasvideo; int64_t tf_tsref; time_t tf_start_time; + int tf_comm_skip; struct th_pktref_queue tf_ptsq; @@ -160,6 +162,9 @@ normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) /* Subtract the transport wide start offset */ dts = pkt->pkt_dts - tf->tf_tsref; + /* Subtract dropped packets due to commercial breaks */ + dts -= tfs->tfs_drops; + if(tfs->tfs_last_dts_norm == PTS_UNSET) { if(dts < 0) { /* Early packet with negative time stamp, drop those */ @@ -322,11 +327,11 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) tsfixprintf("reference clock set to %"PRId64"\n", tf->tf_tsref); } + int pdur = pkt->pkt_duration >> pkt->pkt_field; + if(pkt->pkt_dts == PTS_UNSET) { - - int pdur = pkt->pkt_duration >> pkt->pkt_field; - if(tfs->tfs_last_dts_in == PTS_UNSET) { + tfs->tfs_drops += pdur; pkt_ref_dec(pkt); return; } @@ -337,6 +342,13 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) streaming_component_type2txt(tfs->tfs_type), tfs->tfs_last_dts_in, pdur, pkt->pkt_dts); } + + if(tf->tf_comm_skip && pkt->pkt_commercial == COMMERCIAL_YES) { + tfs->tfs_drops += pdur; + pkt_ref_dec(pkt); + return; + } + tfs->tfs_last_dts_in = pkt->pkt_dts; compute_pts(tf, tfs, pkt); @@ -404,6 +416,17 @@ void tsfix_set_start_time(streaming_target_t *pad, time_t start) } +/** + * + */ +void +tsfix_set_comm_skip(streaming_target_t *pad, int bool) { + tsfix_t *tf = (tsfix_t *)pad; + + tf->tf_comm_skip = !!bool; +} + + /** * */ diff --git a/src/plumbing/tsfix.h b/src/plumbing/tsfix.h index 156284fe..210bc88c 100644 --- a/src/plumbing/tsfix.h +++ b/src/plumbing/tsfix.h @@ -25,6 +25,8 @@ streaming_target_t *tsfix_create(streaming_target_t *output); void tsfix_set_start_time(streaming_target_t *pad, time_t start); +void tsfix_set_comm_skip(streaming_target_t *pad, int bool); + void tsfix_destroy(streaming_target_t *gh); From d9b2a2cdbdc5efc8250c71aaa88746232233197c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 7 Jan 2013 11:45:21 +0100 Subject: [PATCH 202/503] cosmetics: removed dead code --- src/service.h | 1 - src/teletext.c | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/service.h b/src/service.h index b6463fc0..700c150e 100644 --- a/src/service.h +++ b/src/service.h @@ -339,7 +339,6 @@ typedef struct service { * Teletext... */ th_commercial_advice_t s_tt_commercial_advice; - int s_tt_rundown_content_length; time_t s_tt_clock; /* Network clock as determined by teletext decoder */ /** diff --git a/src/teletext.c b/src/teletext.c index 2bb07e1e..0dbe6a6d 100644 --- a/src/teletext.c +++ b/src/teletext.c @@ -534,7 +534,7 @@ teletext_rundown_scan(service_t *t, tt_private_t *ttp) { int i; uint8_t *l; - time_t now = t->s_tt_clock, start, stop, last = 0; + time_t now = t->s_tt_clock, start, stop; th_commercial_advice_t ca; if(ttp->ttp_rundown_valid == 0) @@ -555,8 +555,5 @@ teletext_rundown_scan(service_t *t, tt_private_t *ttp) if(start <= now && stop > now) t->s_tt_commercial_advice = ca; - - if(start > now && ca != t->s_tt_commercial_advice && last == 0) - last = start; } } From bc9305ec131cbf2113bdf18c343ab0fa5dcae060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 7 Jan 2013 11:47:51 +0100 Subject: [PATCH 203/503] Only do commercial detection (teletext rundown) on swedish TV4 and TV4 HD. TV4 Guld carry the same teletext pages but thier programming differ. --- src/teletext.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/teletext.c b/src/teletext.c index 0dbe6a6d..4b736cf2 100644 --- a/src/teletext.c +++ b/src/teletext.c @@ -540,6 +540,11 @@ teletext_rundown_scan(service_t *t, tt_private_t *ttp) if(ttp->ttp_rundown_valid == 0) return; + if(t->s_svcname && + strcmp("TV4", t->s_svcname) && + strcmp("TV4 HD", t->s_svcname)) + return; + for(i = 0; i < 23; i++) { l = ttp->ttp_rundown + 40 * i; if((l[1] & 0xf0) != 0x00 || !is_tt_clock(l + 32) || !is_tt_clock(l + 2)) From c6556d9c2ec48721f0d86a5894e555e23b617685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 7 Jan 2013 11:55:37 +0100 Subject: [PATCH 204/503] dvr: record the first commercial tagged packet, incase the detection was to early --- src/dvr/dvr_rec.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 0efe1223..2f30bfb2 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -420,21 +420,20 @@ dvr_thread(void *aux) switch(sm->sm_type) { case SMT_PACKET: - pkt = sm->sm_data; - if(pkt->pkt_commercial == COMMERCIAL_YES) { - dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0); - tsfix_set_comm_skip(de->de_tsfix, 1); - break; - } - + pkt = sm->sm_data; + if(pkt->pkt_commercial == COMMERCIAL_YES) { + dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0); + tsfix_set_comm_skip(de->de_tsfix, 1); + } else { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); tsfix_set_comm_skip(de->de_tsfix, 0); + } - if(started) { - muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); - sm->sm_data = NULL; - } - break; + if(started) { + muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); + sm->sm_data = NULL; + } + break; case SMT_MPEGTS: if(started) { From 33550c716b5ca9ee5c51e41f69d6843dfa143a44 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Mon, 7 Jan 2013 15:01:00 +0400 Subject: [PATCH 205/503] check wrong prefcapid --- src/cwc.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index d8733ec7..79c10008 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1635,11 +1635,20 @@ cwc_table_input(struct th_descrambler *td, struct service *t, tvhlog(LOG_DEBUG, "cwc", "Insert after unexpected reply"); } - if (ct->cs_okchannel == -3 && t->s_prefcapid == st->es_pid) { - ep = calloc(1, sizeof(ecm_pid_t)); - ep->ep_pid = t->s_prefcapid; - LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); - tvhlog(LOG_DEBUG, "cwc", "Insert only one new ECM channel %d for service id %d", t->s_prefcapid, sid); + if (ct->cs_okchannel == -3 && t->s_prefcapid != 0) { + if (t->s_prefcapid == st->es_pid) { + ep = calloc(1, sizeof(ecm_pid_t)); + ep->ep_pid = t->s_prefcapid; + LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); + tvhlog(LOG_DEBUG, "cwc", "Insert only one new ECM channel %d for service id %d", t->s_prefcapid, sid); + } else { + // check if prefcapid wrong + struct elementary_stream *prefca = service_stream_find(t, t->s_prefcapid); + + if (!prefca || prefca->es_type != SCT_CA) { + t->s_prefcapid = 0; + } + } } if (ct->cs_okchannel == -1 || (ct->cs_okchannel == -3 && t->s_prefcapid == 0)) { From b979aae2fb6183314ae71549a4b24560fc3639ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 7 Jan 2013 16:50:30 +0100 Subject: [PATCH 206/503] make skip commercials an option in the webif (default to on) --- docs/html/config_dvr.html | 4 ++++ src/dvr/dvr.h | 1 + src/dvr/dvr_db.c | 6 +++++- src/dvr/dvr_rec.c | 4 +++- src/webui/extjs.c | 4 ++++ src/webui/static/app/dvr.js | 5 ++++- 6 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/html/config_dvr.html b/docs/html/config_dvr.html index 05d6b8cc..2b72e8a0 100644 --- a/docs/html/config_dvr.html +++ b/docs/html/config_dvr.html @@ -74,6 +74,10 @@
If checked, media containers that support metadata will be tagged with the metadata associated with the event being recorded. +
Skip commercials +
If checked, commercials will be dropped from the recordings. At the + moment, commercial detection only works for the swedish channel TV4. +
Post-processor command
Command to run after finishing a recording. The command will be run in background and is executed even if a recording is aborted diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 9cbfacf6..4c2174aa 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -65,6 +65,7 @@ extern struct dvr_entry_list dvrentries; #define DVR_EPISODE_IN_TITLE 0x80 #define DVR_CLEAN_TITLE 0x100 #define DVR_TAG_FILES 0x200 +#define DVR_SKIP_COMMERCIALS 0x400 typedef enum { DVR_PRIO_IMPORTANT, diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 627ef426..1b4edf2d 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1000,6 +1000,9 @@ dvr_init(void) if(!htsmsg_get_u32(m, "tag-files", &u32) && !u32) cfg->dvr_flags &= ~DVR_TAG_FILES; + if(!htsmsg_get_u32(m, "skip-commercials", &u32) && !u32) + cfg->dvr_flags &= ~DVR_SKIP_COMMERCIALS; + tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc")); } @@ -1095,7 +1098,7 @@ dvr_config_create(const char *name) cfg->dvr_config_name = strdup(name); cfg->dvr_retention_days = 31; cfg->dvr_mc = MC_MATROSKA; - cfg->dvr_flags = DVR_TAG_FILES; + cfg->dvr_flags = DVR_TAG_FILES | DVR_SKIP_COMMERCIALS; /* series link support */ cfg->dvr_sl_brand_lock = 1; // use brand linking @@ -1160,6 +1163,7 @@ dvr_save(dvr_config_t *cfg) htsmsg_add_u32(m, "episode-in-title", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE)); htsmsg_add_u32(m, "clean-title", !!(cfg->dvr_flags & DVR_CLEAN_TITLE)); htsmsg_add_u32(m, "tag-files", !!(cfg->dvr_flags & DVR_TAG_FILES)); + htsmsg_add_u32(m, "skip-commercials", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS)); if(cfg->dvr_postproc != NULL) htsmsg_add_str(m, "postproc", cfg->dvr_postproc); diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 2f30bfb2..d933c195 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -398,11 +398,13 @@ static void * dvr_thread(void *aux) { dvr_entry_t *de = aux; + dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); streaming_queue_t *sq = &de->de_sq; streaming_message_t *sm; th_pkt_t *pkt; int run = 1; int started = 0; + int comm_skip = (cfg->dvr_flags & DVR_SKIP_COMMERCIALS); pthread_mutex_lock(&sq->sq_mutex); @@ -423,7 +425,7 @@ dvr_thread(void *aux) pkt = sm->sm_data; if(pkt->pkt_commercial == COMMERCIAL_YES) { dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0); - tsfix_set_comm_skip(de->de_tsfix, 1); + tsfix_set_comm_skip(de->de_tsfix, comm_skip); } else { dvr_rec_set_state(de, DVR_RS_RUNNING, 0); tsfix_set_comm_skip(de->de_tsfix, 0); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index deca304b..88496e6f 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1211,6 +1211,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "episodeInTitle", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE)); htsmsg_add_u32(r, "cleanTitle", !!(cfg->dvr_flags & DVR_CLEAN_TITLE)); htsmsg_add_u32(r, "tagFiles", !!(cfg->dvr_flags & DVR_TAG_FILES)); + htsmsg_add_u32(r, "commSkip", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS)); out = json_single_record(r, "dvrSettings"); @@ -1261,6 +1262,9 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) flags |= DVR_EPISODE_IN_TITLE; if(http_arg_get(&hc->hc_req_args, "tagFiles") != NULL) flags |= DVR_TAG_FILES; + if(http_arg_get(&hc->hc_req_args, "commSkip") != NULL) + flags |= DVR_SKIP_COMMERCIALS; + dvr_flags_set(cfg,flags); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 564eab3a..fa7d4041 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -703,7 +703,7 @@ tvheadend.dvrsettings = function() { }, [ 'storage', 'postproc', 'retention', 'dayDirs', 'channelDirs', 'channelInTitle', 'container', 'dateInTitle', 'timeInTitle', 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs', - 'episodeInTitle', 'cleanTitle', 'tagFiles' ]); + 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip' ]); var confcombo = new Ext.form.ComboBox({ store : tvheadend.configNames, @@ -793,6 +793,9 @@ tvheadend.dvrsettings = function() { }), new Ext.form.Checkbox({ fieldLabel : 'Tag files with metadata', name : 'tagFiles' + }), new Ext.form.Checkbox({ + fieldLabel : 'Skip commercials', + name : 'commSkip' }), { width : 300, fieldLabel : 'Post-processor command', From d18bd918455d7984acc9fa24aba14fbe7d10133a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 7 Jan 2013 19:52:48 +0100 Subject: [PATCH 207/503] dvr: added initial support for libavformat muxing --- Makefile | 4 + configure | 27 ++ src/libav.c | 104 ++++++++ src/libav.h | 31 +++ src/main.c | 7 + src/muxer.c | 48 +++- src/muxer.h | 4 + src/muxer/muxer_libav.c | 508 ++++++++++++++++++++++++++++++++++++ src/muxer/muxer_libav.h | 26 ++ src/webui/extjs.c | 42 +++ src/webui/static/app/dvr.js | 21 +- 11 files changed, 814 insertions(+), 8 deletions(-) create mode 100644 src/libav.c create mode 100644 src/libav.h create mode 100644 src/muxer/muxer_libav.c create mode 100644 src/muxer/muxer_libav.h diff --git a/Makefile b/Makefile index d8841397..d8d6197e 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,10 @@ SRCS-${CONFIG_V4L} += \ # Avahi SRCS-$(CONFIG_AVAHI) += src/avahi.c +# libav +SRCS-$(CONFIG_LIBAV) += src/libav.c \ + src/muxer/muxer_libav.c + # CWC SRCS-${CONFIG_CWC} += src/cwc.c \ src/capmt.c diff --git a/configure b/configure index 765a9898..5a5ce0ee 100755 --- a/configure +++ b/configure @@ -23,6 +23,7 @@ OPTIONS=( "imagecache:auto" "avahi:auto" "zlib:auto" + "libav:auto" "bundle:no" "dvbcsa:no" ) @@ -100,6 +101,32 @@ if enabled_or_auto avahi; then fi fi +# +# libav +# +if enabled_or_auto libav; then + has_libav=true + + if $has_libav && ! check_pkg libavutil ">=50.43.0"; then + has_libav=false + fi + + if $has_libav && ! check_pkg libavformat "<=55.0.0"; then + has_libav=false + fi + + if $has_libav && ! check_pkg libavformat ">=50.43.0"; then + has_libav=false + fi + + if $has_libav; then + enable libav + elif enabled libav; then + die "libav development support not found (use --disable-libav)" + fi +fi + + # # DVB scan # diff --git a/src/libav.c b/src/libav.c new file mode 100644 index 00000000..e867eea4 --- /dev/null +++ b/src/libav.c @@ -0,0 +1,104 @@ +#include "libav.h" + +/** + * + */ +static void +libav_log_callback(void *ptr, int level, const char *fmt, va_list vl) +{ + char message[8192]; + char *nl; + char *l; + + memset(message, 0, sizeof(message)); + vsnprintf(message, sizeof(message), fmt, vl); + + l = message; + + if(level == AV_LOG_DEBUG) + level = LOG_DEBUG; + else if(level == AV_LOG_VERBOSE) + level = LOG_INFO; + else if(level == AV_LOG_INFO) + level = LOG_NOTICE; + else if(level == AV_LOG_WARNING) + level = LOG_WARNING; + else if(level == AV_LOG_ERROR) + level = LOG_ERR; + else if(level == AV_LOG_FATAL) + level = LOG_CRIT; + else if(level == AV_LOG_PANIC) + level = LOG_EMERG; + + while(l < message + sizeof(message)) { + nl = strstr(l, "\n"); + if(nl) + *nl = '\0'; + + if(!strlen(l)) + break; + + tvhlog(level, "libav", "%s", l); + + l += strlen(message); + + if(!nl) + break; + } +} + +/** + * Translate a component type to a libavcodec id + */ +enum CodecID +streaming_component_type2codec_id(streaming_component_type_t type) +{ + enum CodecID codec_id = CODEC_ID_NONE; + + switch(type) { + case SCT_H264: + codec_id = CODEC_ID_H264; + break; + case SCT_MPEG2VIDEO: + codec_id = CODEC_ID_MPEG2VIDEO; + break; + case SCT_AC3: + codec_id = CODEC_ID_AC3; + break; + case SCT_EAC3: + codec_id = CODEC_ID_EAC3; + break; + case SCT_AAC: + codec_id = CODEC_ID_AAC; + break; + case SCT_MPEG2AUDIO: + codec_id = CODEC_ID_MP2; + break; + case SCT_DVBSUB: + codec_id = CODEC_ID_DVB_SUBTITLE; + break; + case SCT_TEXTSUB: + codec_id = CODEC_ID_TEXT; + break; + case SCT_TELETEXT: + codec_id = CODEC_ID_DVB_TELETEXT; + break; + default: + codec_id = CODEC_ID_NONE; + break; + } + + return codec_id; +} + +/** + * + */ +void +libav_init(void) +{ + av_log_set_callback(libav_log_callback); + av_log_set_level(AV_LOG_VERBOSE); + av_register_all(); +} + diff --git a/src/libav.h b/src/libav.h new file mode 100644 index 00000000..c8a6ed7a --- /dev/null +++ b/src/libav.h @@ -0,0 +1,31 @@ +/* + * tvheadend, libav utils + * Copyright (C) 2012 John Törnblom + * + * 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 LIBAV_H_ +#define LIBAV_H_ + + +#include +#include "tvheadend.h" + +enum CodecID streaming_component_type2codec_id(streaming_component_type_t type); + +void libav_init(void); + +#endif + diff --git a/src/main.c b/src/main.c index 704f6e4b..f0a58be5 100644 --- a/src/main.c +++ b/src/main.c @@ -61,6 +61,9 @@ #include "muxes.h" #include "config2.h" #include "imagecache.h" +#if ENABLE_LIBAV +#include "libav.h" +#endif int running; time_t dispatch_clock; @@ -466,6 +469,10 @@ main(int argc, char **argv) * Initialize subsystems */ +#if ENABLE_LIBAV + libav_init(); +#endif + config_init(); imagecache_init(); diff --git a/src/muxer.c b/src/muxer.c index 6ec3a8a1..2de07edf 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -23,7 +23,9 @@ #include "muxer.h" #include "muxer/muxer_tvh.h" #include "muxer/muxer_pass.h" - +#if CONFIG_LIBAV +#include "muxer/muxer_libav.h" +#endif /** * Mime type for containers containing only audio @@ -140,6 +142,45 @@ muxer_container_type2txt(muxer_container_type_t mc) } +/** + * Get a list of supported containers + */ +int +muxer_container_list(htsmsg_t *array) +{ + htsmsg_t *mc; + int c = 0; + + mc = htsmsg_create_map(); + htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MATROSKA)); + htsmsg_add_str(mc, "description", "Matroska"); + htsmsg_add_msg(array, NULL, mc); + c++; + + mc = htsmsg_create_map(); + htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_PASS)); + htsmsg_add_str(mc, "description", "Same as source (pass through)"); + htsmsg_add_msg(array, NULL, mc); + c++; + +#if ENABLE_LIBAV + mc = htsmsg_create_map(); + htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MPEGTS)); + htsmsg_add_str(mc, "description", "MPEG-TS"); + htsmsg_add_msg(array, NULL, mc); + c++; + + mc = htsmsg_create_map(); + htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MPEGPS)); + htsmsg_add_str(mc, "description", "MPEG-PS (DVD)"); + htsmsg_add_msg(array, NULL, mc); + c++; +#endif + + return c; +} + + /** * Convert a container name to a container type */ @@ -194,6 +235,11 @@ muxer_create(muxer_container_type_t mc) if(!m) m = tvh_muxer_create(mc); +#if CONFIG_LIBAV + if(!m) + m = lav_muxer_create(mc); +#endif + if(!m) tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container", muxer_container_type2txt(mc)); diff --git a/src/muxer.h b/src/muxer.h index 766c5dd4..6d57cd7f 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -19,6 +19,8 @@ #ifndef MUXER_H_ #define MUXER_H_ +#include "htsmsg.h" + typedef enum { MC_UNKNOWN = 0, MC_MATROSKA = 1, @@ -65,6 +67,8 @@ muxer_container_type_t muxer_container_mime2type (const char *str); const char* muxer_container_suffix(muxer_container_type_t mc, int video); +int muxer_container_list(htsmsg_t *array); + // Muxer factory muxer_t *muxer_create(muxer_container_type_t mc); diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c new file mode 100644 index 00000000..7837afe6 --- /dev/null +++ b/src/muxer/muxer_libav.c @@ -0,0 +1,508 @@ +/* + * tvheadend, libavformat based muxer + * Copyright (C) 2012 John Törnblom + * + * 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 . + */ + +#include +#include +#include +#include + +#include "tvheadend.h" +#include "streaming.h" +#include "epg.h" +#include "channels.h" +#include "libav.h" +#include "muxer_libav.h" + +typedef struct lav_muxer { + muxer_t; + AVFormatContext *lm_oc; + AVBitStreamFilterContext *lm_h264_filter; + int lm_fd; +} lav_muxer_t; + +#define MUX_BUF_SIZE 4096 + +static const AVRational mpeg_tc = {1, 90000}; + + +/** + * Callback function for libavformat + */ +static int +lav_muxer_write(void *opaque, uint8_t *buf, int buf_size) +{ + int r; + lav_muxer_t *lm = (lav_muxer_t*)opaque; + + r = write(lm->lm_fd, buf, buf_size); + lm->m_errors += (r != buf_size); + + return r; +} + + +/** + * Add a stream to the muxer + */ +static int +lav_muxer_add_stream(lav_muxer_t *lm, + const streaming_start_component_t *ssc) +{ + AVStream *st; + AVCodecContext *c; + + st = avformat_new_stream(lm->lm_oc, NULL); + if (!st) + return -1; + + st->id = ssc->ssc_index; + c = st->codec; + c->codec_id = streaming_component_type2codec_id(ssc->ssc_type); + + switch(lm->m_container) { + case MC_MATROSKA: + st->time_base.num = 1000000; + st->time_base.den = 1; + break; + + case MC_MPEGPS: + c->rc_buffer_size = 224*1024*8; + //Fall-through + case MC_MPEGTS: + st->time_base.num = 90000; + st->time_base.den = 1; + break; + + default: + st->time_base = AV_TIME_BASE_Q; + break; + } + + + + if(ssc->ssc_gh) { + c->extradata_size = pktbuf_len(ssc->ssc_gh); + c->extradata = av_malloc(c->extradata_size); + memcpy(c->extradata, pktbuf_ptr(ssc->ssc_gh), + pktbuf_len(ssc->ssc_gh)); + } + + if(SCT_ISAUDIO(ssc->ssc_type)) { + c->codec_type = AVMEDIA_TYPE_AUDIO; + c->sample_fmt = AV_SAMPLE_FMT_S16; + + c->sample_rate = sri_to_rate(ssc->ssc_sri); + c->channels = ssc->ssc_channels; + + c->time_base.num = 1; + c->time_base.den = c->sample_rate; + + av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); + + } else if(SCT_ISVIDEO(ssc->ssc_type)) { + c->codec_type = AVMEDIA_TYPE_VIDEO; + c->width = ssc->ssc_width; + c->height = ssc->ssc_height; + + c->time_base.num = 1; + c->time_base.den = 25; + + c->sample_aspect_ratio.num = ssc->ssc_aspect_num; + c->sample_aspect_ratio.den = ssc->ssc_aspect_den; + + st->sample_aspect_ratio.num = c->sample_aspect_ratio.num; + st->sample_aspect_ratio.den = c->sample_aspect_ratio.den; + + } else if(SCT_ISSUBTITLE(ssc->ssc_type)) { + c->codec_type = AVMEDIA_TYPE_SUBTITLE; + av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); + } + + if(lm->lm_oc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + + return 0; +} + + +/** + * Check if a container supports a given streaming component + */ +static int +lav_muxer_support_stream(muxer_container_type_t mc, + streaming_component_type_t type) +{ + int ret = 0; + + switch(mc) { + case MC_MATROSKA: + ret |= SCT_ISAUDIO(type); + ret |= SCT_ISVIDEO(type); + ret |= SCT_ISSUBTITLE(type); + break; + + case MC_MPEGTS: + ret |= (type == SCT_MPEG2VIDEO); + ret |= (type == SCT_H264); + + ret |= (type == SCT_MPEG2AUDIO); + ret |= (type == SCT_AC3); + ret |= (type == SCT_AAC); + ret |= (type == SCT_MP4A); + ret |= (type == SCT_EAC3); + + //Some pids lack pts, disable for now + //ret |= (type == SCT_TELETEXT); + ret |= (type == SCT_DVBSUB); + break; + + case MC_MPEGPS: + ret |= (type == SCT_MPEG2VIDEO); + ret |= (type == SCT_MPEG2AUDIO); + ret |= (type == SCT_AC3); + + default: + break; + } + + return ret; +} + + +/** + * Figure out the mime-type for the muxed data stream + */ +static const char* +lav_muxer_mime(muxer_t* m, const struct streaming_start *ss) +{ + int i; + int has_audio; + int has_video; + const streaming_start_component_t *ssc; + + has_audio = 0; + has_video = 0; + + for(i=0; i < ss->ss_num_components; i++) { + ssc = &ss->ss_components[i]; + + if(ssc->ssc_disabled) + continue; + + if(!lav_muxer_support_stream(m->m_container, ssc->ssc_type)) + continue; + + has_video |= SCT_ISVIDEO(ssc->ssc_type); + has_audio |= SCT_ISAUDIO(ssc->ssc_type); + } + + if(has_video) + return muxer_container_type2mime(m->m_container, 1); + else if(has_audio) + return muxer_container_type2mime(m->m_container, 0); + else + return muxer_container_type2mime(MC_UNKNOWN, 0); +} + + +/** + * Init the muxer with streams + */ +static int +lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) +{ + int i; + const streaming_start_component_t *ssc; + AVFormatContext *oc; + lav_muxer_t *lm = (lav_muxer_t*)m; + char app[128]; + + snprintf(app, sizeof(app), "Tvheadend %s", tvheadend_version); + + oc = lm->lm_oc; + + av_dict_set(&oc->metadata, "title", name, 0); + av_dict_set(&oc->metadata, "service_name", name, 0); + av_dict_set(&oc->metadata, "service_provider", app, 0); + + if(lm->m_container == MC_MPEGTS) + lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb"); + + oc->max_delay = 0.7 * AV_TIME_BASE; + + for(i=0; i < ss->ss_num_components; i++) { + ssc = &ss->ss_components[i]; + + if(ssc->ssc_disabled) + continue; + + if(!lav_muxer_support_stream(lm->m_container, ssc->ssc_type)) { + tvhlog(LOG_WARNING, "libav", "%s is not supported in %s", + streaming_component_type2txt(ssc->ssc_type), + muxer_container_type2txt(lm->m_container)); + continue; + } + + if(lav_muxer_add_stream(lm, ssc)) { + tvhlog(LOG_ERR, "libav", "Failed to add %s stream", + streaming_component_type2txt(ssc->ssc_type)); + continue; + } + } + + if(!lm->lm_oc->nb_streams) { + tvhlog(LOG_ERR, "libav", "No supported streams available"); + lm->m_errors++; + return -1; + } else if(avformat_write_header(lm->lm_oc, NULL) < 0) { + tvhlog(LOG_ERR, "libav", "Failed to write %s header", + muxer_container_type2txt(lm->m_container)); + lm->m_errors++; + return -1; + } + + return 0; +} + + +/** + * Handle changes to the streams (usually PMT updates) + */ +static int +lav_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss) +{ + lav_muxer_t *lm = (lav_muxer_t*)m; + + lm->m_errors++; + + return -1; +} + + +/** + * Open the muxer and write the header + */ +static int +lav_muxer_open_stream(muxer_t *m, int fd) +{ + uint8_t *buf; + AVIOContext *pb; + lav_muxer_t *lm = (lav_muxer_t*)m; + + buf = av_malloc(MUX_BUF_SIZE); + pb = avio_alloc_context(buf, MUX_BUF_SIZE, 1, lm, NULL, + lav_muxer_write, NULL); + pb->seekable = 0; + lm->lm_oc->pb = pb; + lm->lm_fd = fd; + + return 0; +} + + +static int +lav_muxer_open_file(muxer_t *m, const char *filename) +{ + AVFormatContext *oc; + lav_muxer_t *lm = (lav_muxer_t*)m; + + oc = lm->lm_oc; + snprintf(oc->filename, sizeof(oc->filename), "%s", filename); + + if(avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) { + tvhlog(LOG_ERR, "libav", "Could not open %s", filename); + lm->m_errors++; + return -1; + } + + return 0; +} + + +/** + * Write a packet to the muxer + */ +static int +lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) +{ + int i; + AVFormatContext *oc; + AVStream *st; + AVPacket packet; + th_pkt_t *pkt = (th_pkt_t*)data; + lav_muxer_t *lm = (lav_muxer_t*)m; + + assert(smt == SMT_PACKET); + + oc = lm->lm_oc; + + if(!oc->nb_streams) { + tvhlog(LOG_ERR, "libav", "No streams to mux"); + lm->m_errors++; + return -1; + } + + for(i=0; inb_streams; i++) { + st = oc->streams[i]; + + if(st->id != pkt->pkt_componentindex) + continue; + + av_init_packet(&packet); + + if(st->codec->codec_id == CODEC_ID_MPEG2VIDEO) + pkt = pkt_merge_header(pkt); + + if(lm->lm_h264_filter && st->codec->codec_id == CODEC_ID_H264) { + av_bitstream_filter_filter(lm->lm_h264_filter, + st->codec, + NULL, + &packet.data, + &packet.size, + pktbuf_ptr(pkt->pkt_payload), + pktbuf_len(pkt->pkt_payload), + pkt->pkt_frametype < PKT_P_FRAME); + } else { + packet.data = pktbuf_ptr(pkt->pkt_payload); + packet.size = pktbuf_len(pkt->pkt_payload); + } + + packet.stream_index = st->index; + + packet.pts = av_rescale_q(pkt->pkt_pts , mpeg_tc, st->time_base); + packet.dts = av_rescale_q(pkt->pkt_dts , mpeg_tc, st->time_base); + packet.duration = av_rescale_q(pkt->pkt_duration, mpeg_tc, st->time_base); + + if(pkt->pkt_frametype < PKT_P_FRAME) + packet.flags |= AV_PKT_FLAG_KEY; + + if (av_interleaved_write_frame(oc, &packet) != 0) { + tvhlog(LOG_WARNING, "libav", "Failed to write frame"); + lm->m_errors++; + return -1; + } + + break; + } + + pkt_ref_dec(pkt); + + return 0; +} + + +/** + * NOP + */ +static int +lav_muxer_write_meta(muxer_t *m, struct epg_broadcast *eb) +{ + return 0; +} + + +/** + * Close the muxer and append trailer to output + */ +static int +lav_muxer_close(muxer_t *m) +{ + int i; + int ret = 0; + lav_muxer_t *lm = (lav_muxer_t*)m; + + if(lm->lm_oc->nb_streams && av_write_trailer(lm->lm_oc) < 0) { + tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer", + muxer_container_type2txt(lm->m_container)); + lm->m_errors++; + ret = -1; + } + + if(lm->lm_h264_filter) + av_bitstream_filter_close(lm->lm_h264_filter); + + for(i=0; ilm_oc->nb_streams; i++) + av_freep(&lm->lm_oc->streams[i]->codec->extradata); + + lm->lm_oc->nb_streams = 0; + + return ret; +} + + +/** + * Free all memory associated with the muxer + */ +static void +lav_muxer_destroy(muxer_t *m) +{ + lav_muxer_t *lm = (lav_muxer_t*)m; + + if(lm->lm_oc && lm->lm_oc->pb) + av_free(lm->lm_oc->pb); + + if(lm->lm_oc) + av_free(lm->lm_oc); + + free(lm); +} + + +/** + * Create a new libavformat based muxer + */ +muxer_t* +lav_muxer_create(muxer_container_type_t mc) +{ + const char *mux_name; + lav_muxer_t *lm; + AVOutputFormat *fmt; + + switch(mc) { + case MC_MPEGPS: + mux_name = "dvd"; + break; + default: + mux_name = muxer_container_type2txt(mc); + break; + } + + fmt = av_guess_format(mux_name, NULL, NULL); + if(!fmt) { + tvhlog(LOG_ERR, "libav", "Can't find the '%s' muxer", mux_name); + return NULL; + } + + lm = calloc(1, sizeof(lav_muxer_t)); + lm->m_open_stream = lav_muxer_open_stream; + lm->m_open_file = lav_muxer_open_file; + lm->m_init = lav_muxer_init; + lm->m_reconfigure = lav_muxer_reconfigure; + lm->m_mime = lav_muxer_mime; + lm->m_write_meta = lav_muxer_write_meta; + lm->m_write_pkt = lav_muxer_write_pkt; + lm->m_close = lav_muxer_close; + lm->m_destroy = lav_muxer_destroy; + lm->m_container = mc; + lm->lm_oc = avformat_alloc_context(); + lm->lm_oc->oformat = fmt; + lm->lm_fd = -1; + + return (muxer_t*)lm; +} + diff --git a/src/muxer/muxer_libav.h b/src/muxer/muxer_libav.h new file mode 100644 index 00000000..a8325b06 --- /dev/null +++ b/src/muxer/muxer_libav.h @@ -0,0 +1,26 @@ +/* + * tvheadend, muxing of packets with libavformat + * Copyright (C) 2012 John Törnblom + * + * 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 LAV_MUXER_H_ +#define LAV_MUXER_H_ + +#include "muxer.h" + +muxer_t* lav_muxer_create(muxer_container_type_t mc); + +#endif diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 88496e6f..b7f98f43 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -736,6 +736,47 @@ skip: } + +/** + * + */ +static int +extjs_dvr_containers(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + const char *op = http_arg_get(&hc->hc_req_args, "op"); + htsmsg_t *out, *array; + + pthread_mutex_lock(&global_lock); + + if(op != NULL && !strcmp(op, "list")) { + + out = htsmsg_create_map(); + array = htsmsg_create_list(); + + if (http_access_verify(hc, ACCESS_RECORDER_ALL)) + goto skip; + + muxer_container_list(array); + +skip: + htsmsg_add_msg(out, "entries", array); + + } else { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_BAD_REQUEST; + } + + pthread_mutex_unlock(&global_lock); + + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); + http_output_content(hc, "text/x-json; charset=UTF-8"); + return 0; + +} + + /** * */ @@ -2018,6 +2059,7 @@ extjs_start(void) http_path_add("/dvrlist_upcoming", NULL, extjs_dvrlist_upcoming, ACCESS_WEB_INTERFACE); http_path_add("/dvrlist_finished", NULL, extjs_dvrlist_finished, ACCESS_WEB_INTERFACE); http_path_add("/dvrlist_failed", NULL, extjs_dvrlist_failed, ACCESS_WEB_INTERFACE); + http_path_add("/dvr_containers", NULL, extjs_dvr_containers, ACCESS_WEB_INTERFACE); http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE); http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE); http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE); diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index fa7d4041..215fdf66 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -14,13 +14,20 @@ tvheadend.dvrprio = new Ext.data.SimpleStore({ [ 'unimportant', 'Unimportant' ] ] }); + //For the container configuration -tvheadend.containers = new Ext.data.SimpleStore({ - fields : [ 'identifier', 'name' ], - id : 0, - data : [ [ 'matroska', 'Matroska' ], [ 'pass', 'TS (Pass-through)' ] ] +tvheadend.containers = new Ext.data.JsonStore({ + autoLoad : true, + root : 'entries', + fields : [ 'name', 'description' ], + id : 'name', + url : 'dvr_containers', + baseParams : { + op : 'list' + } }); + /** * Configuration names */ @@ -743,11 +750,11 @@ tvheadend.dvrsettings = function() { }, new Ext.form.ComboBox({ store : tvheadend.containers, fieldLabel : 'Media container', - mode : 'local', triggerAction : 'all', - displayField : 'name', - valueField : 'identifier', + displayField : 'description', + valueField : 'name', editable : false, + width : 200, hiddenName : 'container' }), new Ext.form.NumberField({ allowNegative : false, From 3e2710d34dbc0965e3a76602ce239dd4f71a96f4 Mon Sep 17 00:00:00 2001 From: xxxnelly Date: Mon, 7 Jan 2013 20:37:25 +0000 Subject: [PATCH 208/503] Adding sky UK mappings for NDS --- src/psi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psi.c b/src/psi.c index b4e1a685..1329377e 100644 --- a/src/psi.c +++ b/src/psi.c @@ -860,7 +860,9 @@ static struct strtab caidnametab[] = { { "Irdeto", 0x0666 }, { "Jerroldgi", 0x0700 }, { "Matra", 0x0800 }, - { "NDS", 0x0900 }, + { "NDS", 0x0900 }, + { "NDS", 0x0960 }, + { "NDS", 0x0963 }, { "Nokia", 0x0A00 }, { "Conax", 0x0B00 }, { "NTL", 0x0C00 }, From ac5db4e22f6a7516ece8faf3413c4e93719ccdb8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 7 Jan 2013 13:42:47 +0000 Subject: [PATCH 209/503] [PR-206] dvb: extend the multi-frontend PR to include cleaner support. Generally this is intended to be used such that a user can select which frontend they wish to enable. However in theory with clever configuration, configure one adapter then the other and enabling close fds and disabling idle scan, it might be possible to use both frontends. But since TVH doesn't do any form of arbitration strange errors might occur if you try to access both frontends at the same time (which for most such adapters is not possible). --- src/dvb/dvb.h | 3 +- src/dvb/dvb_adapter.c | 332 +++++++++++++++++++++++----------------- src/dvb/dvb_multiplex.c | 2 +- 3 files changed, 194 insertions(+), 143 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 5f6cfbb7..1b61f576 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -229,6 +229,7 @@ typedef struct th_dvb_adapter { char *tda_demux_path; + char *tda_dvr_path; pthread_t tda_dvr_thread; th_pipe_t tda_dvr_pipe; @@ -344,7 +345,7 @@ void dvb_adapter_stop (th_dvb_adapter_t *tda); void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); -void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled); +void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on); void dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 3d727f12..6a1b1cc5 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -49,6 +51,7 @@ struct th_dvb_adapter_queue dvb_adapters; struct th_dvb_mux_instance_tree dvb_muxes; static void *dvb_adapter_input_dvr(void *aux); +static void tda_init(th_dvb_adapter_t *tda); /** @@ -81,6 +84,12 @@ tda_save(th_dvb_adapter_t *tda) lock_assert(&global_lock); htsmsg_add_u32(m, "enabled", tda->tda_enabled); + if (tda->tda_fe_path) + htsmsg_add_str(m, "fe_path", tda->tda_fe_path); + if (tda->tda_demux_path) + htsmsg_add_str(m, "dmx_path", tda->tda_demux_path); + if (tda->tda_dvr_path) + htsmsg_add_str(m, "dvr_path", tda->tda_dvr_path); htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type)); htsmsg_add_str(m, "displayname", tda->tda_displayname); htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery); @@ -101,6 +110,31 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_destroy(m); } +/** + * + */ +void +dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on) +{ + if(tda->tda_enabled == on) + return; + + lock_assert(&global_lock); + + tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" %s", + tda->tda_displayname, on ? "Enabled" : "Disabled"); + + tda->tda_enabled = on; + tda_save(tda); + if (!on) { + gtimer_disarm(&tda->tda_mux_scanner_timer); + if (tda->tda_mux_current) + dvb_fe_stop(tda->tda_mux_current, 0); + dvb_adapter_stop(tda); + } else { + tda_init(tda); + } +} /** * @@ -124,26 +158,6 @@ dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s) dvb_adapter_notify(tda); } - -/** - * - */ -void -dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled) -{ - if(tda->tda_enabled == enabled) - return; - - lock_assert(&global_lock); - - tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" enabled set to \"%s\"", - tda->tda_displayname, enabled ? "Enabled" : "Disabled"); - - tda->tda_enabled = enabled; - tda_save(tda); -} - - /** * */ @@ -465,88 +479,113 @@ check_full_stream(th_dvb_adapter_t *tda) * */ static void -tda_add(int adapter_num, int frontend_num, int demux_num) +tda_add(int adapter_num) { - char path[200], fname[256]; - int fe, i, r; + char buf[1024], path[200], fepath[256], dmxpath[256], dvrpath[256]; + int i, j, fd; th_dvb_adapter_t *tda; - char buf[400]; + struct dvb_frontend_info fe_info; + DIR *dirp; + /* Check valid adapter */ snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num); - snprintf(fname, sizeof(fname), "%s/frontend%d", path, frontend_num); - - fe = tvh_open(fname, O_RDWR | O_NONBLOCK, 0); - if(fe == -1) { - if(errno != ENOENT) + dirp = opendir(path); + if (!dirp) + return; + + /* Check each frontend */ + // Note: this algo will fail if there are really exotic variations + // of tuners and demuxers etc... but you can always manually + // override the config + for (i = 0; i < 32; i++) { + + /* Open/Query frontend */ + snprintf(fepath, sizeof(fepath), "%s/frontend%d", path, i); + fd = tvh_open(fepath, O_RDONLY, 0); + if (fd == -1) { + if (errno != ENOENT) + tvhlog(LOG_ALERT, "dvb", + "%s: unable to open (err=%s)", fepath, strerror(errno)); + continue; + } + if (ioctl(fd, FE_GET_INFO, &fe_info)) { tvhlog(LOG_ALERT, "dvb", - "Unable to open %s -- %s", fname, strerror(errno)); - return; + "%s: unable to query (err=%s)", fepath, strerror(errno)); + close(fd); + continue; + } + close(fd); + + /* Find Demux */ + snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, i); + fd = tvh_open(dmxpath, O_RDONLY, 0); + if (fd == -1) { + snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, 0); + fd = tvh_open(dmxpath, O_RDONLY, 0); + } + if (fd == -1) { + tvhlog(LOG_ALERT, "dvb", "%s: unable to find demux", fepath); + continue; + } + close(fd); + + /* Find DVR */ + snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, i); + fd = tvh_open(dvrpath, O_RDONLY, 0); + if (fd == -1) { + snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, 0); + fd = tvh_open(dvrpath, O_RDONLY, 0); + } + if (fd == -1) { + tvhlog(LOG_ALERT, "dvb", "%s: unable to find dvr", fepath); + continue; + } + close(fd); + + /* Create entry */ + tda = tda_alloc(); + tda->tda_adapter_num = adapter_num; + tda->tda_rootpath = strdup(path); + tda->tda_fe_path = strdup(fepath); + tda->tda_demux_path = strdup(dmxpath); + tda->tda_dvr_path = strdup(dvrpath); + tda->tda_fe_fd = -1; + tda->tda_dvr_pipe.rd = -1; + tda->tda_full_mux_rx = -1; + tda->tda_type = fe_info.type; + tda->tda_autodiscovery = tda->tda_type != FE_QPSK; + tda->tda_idlescan = 1; + tda->tda_sat = tda->tda_type == FE_QPSK; + tda->tda_displayname = strdup(fe_info.name); + tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); + memcpy(tda->tda_fe_info, &fe_info, sizeof(struct dvb_frontend_info)); + + /* ID the device (for configuration etc..) */ + // Note: would be better to include frontend dev in name, but that + // would require additional work to migrate config + snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath, + tda->tda_fe_info->name); + for(j = 0; j < strlen(buf); j++) + if(!isalnum((int)buf[j])) + buf[j] = '_'; + tda->tda_identifier = strdup(buf); + + /* Check device connection */ + dvb_adapter_checkspeed(tda); + + /* Adapters known to provide valid SNR */ + if(strcasestr(fe_info.name, "Sony CXD2820R")) + tda->tda_snr_valid = 1; + + /* Store */ + tvhlog(LOG_INFO, "dvb", + "Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name, + hostconnection2str(tda->tda_hostconnection), + tda->tda_snr_valid ? ", Reports valid SNR values" : ""); + TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); } - - tda = tda_alloc(); - - tda->tda_adapter_num = adapter_num; - tda->tda_rootpath = strdup(path); - tda->tda_demux_path = malloc(256); - snprintf(tda->tda_demux_path, 256, "%s/demux%d", path, demux_num); - tda->tda_fe_path = strdup(fname); - tda->tda_fe_fd = -1; - tda->tda_dvr_pipe.rd = -1; - tda->tda_full_mux_rx = -1; - tda->tda_enabled = 0; - - tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); - - if(ioctl(fe, FE_GET_INFO, tda->tda_fe_info)) { - tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter", fname); - close(fe); - free(tda); - return; - } - - close(fe); - fe = -1; - - tda->tda_type = tda->tda_fe_info->type; - - snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath, - tda->tda_fe_info->name); - - r = strlen(buf); - for(i = 0; i < r; i++) - if(!isalnum((int)buf[i])) - buf[i] = '_'; - - tda->tda_identifier = strdup(buf); - - tda->tda_autodiscovery = tda->tda_type != FE_QPSK; - tda->tda_idlescan = 1; - - tda->tda_sat = tda->tda_type == FE_QPSK; - - /* Come up with an initial displayname, user can change it and it will - be overridden by any stored settings later on */ - - tda->tda_displayname = strdup(tda->tda_fe_info->name); - - dvb_adapter_checkspeed(tda); - - - if(strcasestr(tda->tda_fe_info->name, "Sony CXD2820R")) - tda->tda_snr_valid = 1; - - tvhlog(LOG_INFO, "dvb", - "Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name, - hostconnection2str(tda->tda_hostconnection), - tda->tda_snr_valid ? ", Reports valid SNR values" : ""); - - TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); - - gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); } - - /** * */ @@ -592,21 +631,28 @@ tda_add_from_file(const char *filename) } /** - * Initiliase input + * Initiliase */ -static void tda_init_input (th_dvb_adapter_t *tda) +static void tda_init (th_dvb_adapter_t *tda) { - if(tda->tda_type == -1 || check_full_stream(tda)) { - tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath); - dvb_input_raw_setup(tda); - } else { - tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath); - dvb_input_filtered_setup(tda); + /* Disabled - ignore */ + if (!tda->tda_enabled || !tda->tda_rootpath) return; + + /* Initiliase input mode */ + if(!tda->tda_open_service) { + if(tda->tda_type == -1 || check_full_stream(tda)) { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath); + dvb_input_raw_setup(tda); + } else { + tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath); + dvb_input_filtered_setup(tda); + } } + + /* Enable mux scanner */ + gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); } - - /** * */ @@ -641,7 +687,7 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) dvb_adapter_poweroff(tda); /* Don't stop/close */ - if (!tda->tda_idleclose) return; + if (!tda->tda_idleclose && tda->tda_enabled) return; /* Close front end */ if (tda->tda_fe_fd != -1) { @@ -661,6 +707,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) tda->tda_dvr_pipe.rd = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } + + dvb_adapter_notify(tda); } /** @@ -671,8 +719,8 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) { htsmsg_t *l, *c; htsmsg_field_t *f; - const char *name, *s; - int i, j, type; + const char *s; + int i, type; uint32_t u32; th_dvb_adapter_t *tda; @@ -681,54 +729,52 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) /* Initialise hardware */ for(i = 0; i < 32; i++) if ((1 << i) & adapter_mask) - for(j = 0; j < 32; j++) - tda_add(i, j, 0); + tda_add(i); /* Initialise rawts test file */ if(rawfile) tda_add_from_file(rawfile); /* Load configuration */ - l = hts_settings_load("dvbadapters"); - if(l != NULL) { + if ((l = hts_settings_load("dvbadapters")) != NULL) { HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) continue; - name = htsmsg_get_str(c, "displayname"); - if((s = htsmsg_get_str(c, "type")) == NULL || (type = dvb_str_to_adaptertype(s)) < 0) continue; + // TODO: do we really want to do this? useful for debug? if((tda = dvb_adapter_find_by_identifier(f->hmf_name)) == NULL) { /* Not discovered by hardware, create it */ - tda = tda_alloc(); + tda = tda_alloc(); tda->tda_identifier = strdup(f->hmf_name); - tda->tda_type = type; - tda->tda_enabled = 0; + tda->tda_type = type; TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link); } else { if(type != tda->tda_type) continue; /* Something is wrong, ignore */ } - free(tda->tda_displayname); - tda->tda_displayname = strdup(name); + tvh_str_update(&tda->tda_displayname, htsmsg_get_str(c, "displayname")); + tvh_str_update(&tda->tda_dvr_path, htsmsg_get_str(c, "dvr_path")); + tvh_str_update(&tda->tda_demux_path, htsmsg_get_str(c, "dmx_path")); - htsmsg_get_u32(c, "enabled", &tda->tda_enabled); - htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery); - htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan); - htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose); - htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr); - htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan); - htsmsg_get_u32(c, "qmon", &tda->tda_qmon); - htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff); - htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid); - htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version); - htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats); - htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); - htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); + if (htsmsg_get_u32(c, "enabled", &tda->tda_enabled)) + tda->tda_enabled = 1; + htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery); + htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan); + htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose); + htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr); + htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan); + htsmsg_get_u32(c, "qmon", &tda->tda_qmon); + htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff); + htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid); + htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version); + htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats); + htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); + htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor); if (htsmsg_get_s32(c, "full_mux_rx", &tda->tda_full_mux_rx)) if (!htsmsg_get_u32(c, "disable_full_mux_rx", &u32) && u32) @@ -737,12 +783,9 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_destroy(l); } + /* Initialise devices */ TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { - tda_init_input(tda); - if (tda->tda_idlescan || !tda->tda_idleclose) { - close(tda->tda_fe_fd); - tda->tda_fe_fd = -1; - } + tda_init(tda); if(tda->tda_sat) dvb_satconf_init(tda); @@ -766,6 +809,9 @@ dvb_adapter_mux_scanner(void *aux) if(tda->tda_rootpath == NULL) return; // No hardware + if(!tda->tda_enabled) + return; // disabled + // default period gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20); @@ -905,13 +951,10 @@ dvb_adapter_input_dvr(void *aux) uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; - char path[256]; - snprintf(path, sizeof(path), "%s/dvr0", tda->tda_rootpath); - - fd = tvh_open(path, O_RDONLY | O_NONBLOCK, 0); + fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); if(fd == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", path, strerror(errno)); + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", tda->tda_dvr_path, strerror(errno)); return NULL; } @@ -1088,6 +1131,13 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg); + } else { + htsmsg_add_str(m, "currentMux", ""); + htsmsg_add_u32(m, "signal", 0); + htsmsg_add_u32(m, "snr", 0); + htsmsg_add_u32(m, "ber", 0); + htsmsg_add_u32(m, "unc", 0); + htsmsg_add_u32(m, "uncavg", 0); } if(tda->tda_rootpath == NULL) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 1ac5caf1..e129ae63 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -288,7 +288,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source); dvb_mux_save(tdmi); - dvb_adapter_notify(tda); } dvb_service_load(tdmi, identifier); @@ -303,6 +302,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, dvb_mux_add_to_scan_queue(tdmi); } } + dvb_adapter_notify(tda); return tdmi; } From a7da81a394d5eb5d3d7af9dafac58eeff6258ed6 Mon Sep 17 00:00:00 2001 From: Vuolter Date: Mon, 7 Jan 2013 16:24:44 +0100 Subject: [PATCH 210/503] Added webui debug mode switcher --- src/main.c | 15 ++++++++----- src/tvheadend.h | 1 + src/webui/extjs.c | 22 ++++++++++++++----- .../resources/css/ext-all-notheme-min.css | 8 +++++++ 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 src/webui/static/extjs/resources/css/ext-all-notheme-min.css diff --git a/src/main.c b/src/main.c index f0a58be5..6e6a7189 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ /* * TVheadend - * Copyright (C) 2007 - 2010 Andreas Öman + * Copyright (C) 2007 - 2010 Andreas �man * * 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 @@ -78,6 +78,7 @@ int log_debug_to_syslog; int log_debug_to_console; int webui_port; +int webui_debug; int htsp_port; int htsp_port_extra; const char *tvheadend_cwd; @@ -206,14 +207,15 @@ usage(const char *argv0) " it will allow world-wide administrative access\n" " to your Tvheadend installation until you edit\n" " the access-control from within the Tvheadend UI\n"); - printf(" -s Log debug to syslog\n"); - printf(" -w WebUI access port [default 9981]\n"); + printf(" -w Web interface access port [default 9981]\n"); printf(" -e HTSP access port [default 9982]\n"); - printf(" -W WebUI context path [default /]\n"); + printf(" -W Web interface context path [default /]\n"); printf("\n"); printf("Development options\n"); printf("\n"); printf(" -d Log debug to console\n"); + printf(" -s Log debug to syslog\n"); + printf(" -x Run web interface in debug mode\n"); printf(" -j Statically join the given transport id\n"); printf(" -r Read the given transport stream file and present\n" " found services as channels\n"); @@ -306,7 +308,7 @@ main(int argc, char **argv) // make sure the timezone is set tzset(); - while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:R:W:")) != -1) { + while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdxr:j:sw:e:E:R:W:")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; @@ -360,6 +362,9 @@ main(int argc, char **argv) break; case 's': log_debug_to_syslog = 1; + break; + case 'x': + webui_debug = 1; break; case 'C': createdefault = 1; diff --git a/src/tvheadend.h b/src/tvheadend.h index b5d54c47..8c566440 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -65,6 +65,7 @@ extern pthread_mutex_t ffmpeg_lock; extern pthread_mutex_t fork_lock; extern int webui_port; +extern int webui_debug; extern int htsp_port; typedef struct source_info { diff --git a/src/webui/extjs.c b/src/webui/extjs.c index b7f98f43..ce8f6ebc 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -87,12 +87,22 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) #define EXTJSPATH "static/extjs" - htsbuf_qprintf(hq, "\n" - "\n" - "\n" - "\n" - "\n" - "\n"); + htsbuf_qprintf(hq, "\n"); + + if(webui_debug) { + htsbuf_qprintf(hq, "\n" + "\n" + "\n"); + tvhlog(LOG_INFO, "webui", "Running web interface in debug mode"); + } + else { + htsbuf_qprintf(hq, "\n" + "\n" + "\n"); + } + + htsbuf_qprintf(hq, "\n" + "\n"); extjs_exec(hq, "Ext.BLANK_IMAGE_URL = " "'"EXTJSPATH"/resources/images/default/s.gif';"); diff --git a/src/webui/static/extjs/resources/css/ext-all-notheme-min.css b/src/webui/static/extjs/resources/css/ext-all-notheme-min.css new file mode 100644 index 00000000..517c95a4 --- /dev/null +++ b/src/webui/static/extjs/resources/css/ext-all-notheme-min.css @@ -0,0 +1,8 @@ +/*! + * Ext JS Library 3.4.0 + * Copyright(c) 2006-2011 Sencha Inc. + * licensing@sencha.com + * http://www.sencha.com/license + */html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0}img,body,html{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%}q:before,q:after{content:''}.ext-forced-border-box,.ext-forced-border-box *{-moz-box-sizing:border-box;-ms-box-sizing:border-box;-webkit-box-sizing:border-box}.ext-el-mask{z-index:100;position:absolute;top:0;left:0;opacity:.50;filter:alpha(opacity=50);width:100%;height:100%}.ext-el-mask-msg{z-index:20001;position:absolute;top:0;left:0;border:1px solid;background:repeat-x 0 -16px;padding:2px}.ext-el-mask-msg div{padding:5px 10px 5px 10px;border:1px solid;cursor:wait}.ext-shim{position:absolute;visibility:hidden;left:0;top:0;overflow:hidden}.ext-ie .ext-shim{filter:alpha(opacity=0)}.ext-ie6 .ext-shim{margin-left:5px;margin-top:3px}.x-mask-loading div{padding:5px 10px 5px 25px;background:no-repeat 5px 5px;line-height:16px}.x-hidden,.x-hide-offsets{position:absolute!important;left:-10000px;top:-10000px;visibility:hidden}.x-hide-display{display:none!important}.x-hide-nosize,.x-hide-nosize *{height:0!important;width:0!important;visibility:hidden!important;border:none!important}.x-hide-visibility{visibility:hidden!important}.x-masked{overflow:hidden!important}.x-masked-relative{position:relative!important}.x-masked select,.x-masked object,.x-masked embed{visibility:hidden}.x-layer{visibility:hidden}.x-unselectable,.x-unselectable *{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-repaint{background-color:transparent;outline:0}.x-item-disabled{cursor:default;opacity:.6;filter:alpha(opacity=60)}.x-item-disabled *{cursor:default!important}.x-form-radio-group .x-item-disabled{filter:none}.x-splitbar-proxy{position:absolute;visibility:hidden;z-index:20001;line-height:1px;font-size:1px;overflow:hidden}.x-splitbar-h,.x-splitbar-proxy-h{cursor:e-resize;cursor:col-resize}.x-splitbar-v,.x-splitbar-proxy-v{cursor:s-resize;cursor:row-resize}.x-color-palette{width:150px;height:92px;cursor:pointer}.x-color-palette a{border:1px solid;float:left;padding:2px;text-decoration:none;outline:0 none;cursor:pointer}.x-color-palette a:hover,.x-color-palette a.x-color-palette-sel{border:1px solid}.x-color-palette em{display:block;border:1px solid}.x-color-palette em span{cursor:pointer;display:block;height:10px;line-height:10px;width:10px}.x-ie-shadow{display:none;position:absolute;overflow:hidden;left:0;top:0}.x-shadow{display:none;position:absolute;overflow:hidden;left:0;top:0}.x-shadow *{overflow:hidden}.x-shadow *{padding:0;border:0;margin:0;clear:none}.x-shadow .xstc,.x-shadow .xsbc{height:6px;float:left}.x-shadow .xstl,.x-shadow .xstr,.x-shadow .xsbl,.x-shadow .xsbr{width:6px;height:6px;float:left}.x-shadow .xsc{width:100%}.x-shadow .xsml,.x-shadow .xsmr{width:6px;float:left;height:100%}.x-shadow .xsmc{float:left;height:100%;background-color:transparent}.x-shadow .xst,.x-shadow .xsb{height:6px;overflow:hidden;width:100%}.x-shadow .xsml{background:transparent repeat-y 0 0}.x-shadow .xsmr{background:transparent repeat-y -6px 0}.x-shadow .xstl{background:transparent no-repeat 0 0}.x-shadow .xstc{background:transparent repeat-x 0 -30px}.x-shadow .xstr{background:transparent repeat-x 0 -18px}.x-shadow .xsbl{background:transparent no-repeat 0 -12px}.x-shadow .xsbc{background:transparent repeat-x 0 -36px}.x-shadow .xsbr{background:transparent repeat-x 0 -6px}.loading-indicator{background:no-repeat left;padding-left:20px;line-height:16px;margin:3px}.x-text-resize{position:absolute;left:-1000px;top:-1000px;visibility:hidden}.x-drag-overlay{width:100%;height:100%;display:none;position:absolute;left:0;top:0;background-image:url(../images/default/s.gif);z-index:20000}.x-clear{clear:both;height:0;overflow:hidden;line-height:0;font-size:0}.x-spotlight{z-index:8999;position:absolute;top:0;left:0;opacity:.50;filter:alpha(opacity=50);width:0;height:0}#x-history-frame{position:absolute;top:-1px;left:0;width:1px;height:1px;visibility:hidden}#x-history-field{position:absolute;top:0;left:-1px;width:1px;height:1px;visibility:hidden}.x-resizable-handle{position:absolute;z-index:100;font-size:1px;line-height:6px;overflow:hidden;filter:alpha(opacity=0);opacity:0}.x-resizable-handle-east{width:6px;cursor:e-resize;right:0;top:0;height:100%}.ext-ie .x-resizable-handle-east{margin-right:-1px}.x-resizable-handle-south{width:100%;cursor:s-resize;left:0;bottom:0;height:6px}.ext-ie .x-resizable-handle-south{margin-bottom:-1px}.x-resizable-handle-west{width:6px;cursor:w-resize;left:0;top:0;height:100%}.x-resizable-handle-north{width:100%;cursor:n-resize;left:0;top:0;height:6px}.x-resizable-handle-southeast{width:6px;cursor:se-resize;right:0;bottom:0;height:6px;z-index:101}.x-resizable-handle-northwest{width:6px;cursor:nw-resize;left:0;top:0;height:6px;z-index:101}.x-resizable-handle-northeast{width:6px;cursor:ne-resize;right:0;top:0;height:6px;z-index:101}.x-resizable-handle-southwest{width:6px;cursor:sw-resize;left:0;bottom:0;height:6px;z-index:101}.x-resizable-over .x-resizable-handle,.x-resizable-pinned .x-resizable-handle{filter:alpha(opacity=100);opacity:1}.x-resizable-over .x-resizable-handle-east,.x-resizable-pinned .x-resizable-handle-east,.x-resizable-over .x-resizable-handle-west,.x-resizable-pinned .x-resizable-handle-west{background-position:left}.x-resizable-over .x-resizable-handle-south,.x-resizable-pinned .x-resizable-handle-south,.x-resizable-over .x-resizable-handle-north,.x-resizable-pinned .x-resizable-handle-north{background-position:top}.x-resizable-over .x-resizable-handle-southeast,.x-resizable-pinned .x-resizable-handle-southeast{background-position:top left}.x-resizable-over .x-resizable-handle-northwest,.x-resizable-pinned .x-resizable-handle-northwest{background-position:bottom right}.x-resizable-over .x-resizable-handle-northeast,.x-resizable-pinned .x-resizable-handle-northeast{background-position:bottom left}.x-resizable-over .x-resizable-handle-southwest,.x-resizable-pinned .x-resizable-handle-southwest{background-position:top right}.x-resizable-proxy{border:1px dashed;position:absolute;overflow:hidden;display:none;left:0;top:0;z-index:50000}.x-resizable-overlay{width:100%;height:100%;display:none;position:absolute;left:0;top:0;z-index:200000;opacity:0;filter:alpha(opacity=0)}.x-tab-panel{overflow:hidden}.x-tab-panel-header,.x-tab-panel-footer{border:1px solid;overflow:hidden}.x-tab-panel-header{border:1px solid;padding-bottom:2px}.x-tab-panel-footer{border:1px solid;padding-top:2px}.x-tab-strip-wrap{width:100%;overflow:hidden;position:relative}ul.x-tab-strip{display:block;width:5000px}ul.x-tab-strip-top{padding-top:1px;background:repeat-x bottom;border-bottom:1px solid}ul.x-tab-strip-bottom{padding-bottom:1px;background:repeat-x top;border-top:1px solid;border-bottom:0 none}.x-tab-panel-header-plain .x-tab-strip-top{background:transparent!important;padding-top:0!important}.x-tab-panel-header-plain{background:transparent!important;border-width:0!important;padding-bottom:0!important}.x-tab-panel-header-plain .x-tab-strip-spacer,.x-tab-panel-footer-plain .x-tab-strip-spacer{border:1px solid;height:2px;font-size:1px;line-height:1px}.x-tab-panel-header-plain .x-tab-strip-spacer{border-top:0 none}.x-tab-panel-footer-plain .x-tab-strip-spacer{border-bottom:0 none}.x-tab-panel-footer-plain .x-tab-strip-bottom{background:transparent!important;padding-bottom:0!important}.x-tab-panel-footer-plain{background:transparent!important;border-width:0!important;padding-top:0!important}.ext-border-box .x-tab-panel-header-plain .x-tab-strip-spacer,.ext-border-box .x-tab-panel-footer-plain .x-tab-strip-spacer{height:3px}ul.x-tab-strip li{float:left;margin-left:2px}ul.x-tab-strip li.x-tab-edge{float:left;margin:0!important;padding:0!important;border:0 none!important;font-size:1px!important;line-height:1px!important;overflow:hidden;background:transparent!important;width:1px}.x-tab-strip a,.x-tab-strip span,.x-tab-strip em{display:block}.x-tab-strip a{text-decoration:none!important;outline:0;cursor:pointer}.x-tab-strip-inner{overflow:hidden;text-overflow:ellipsis}.x-tab-strip span.x-tab-strip-text{white-space:nowrap;cursor:pointer;padding:4px 0}.x-tab-strip-top .x-tab-with-icon .x-tab-right{padding-left:6px}.x-tab-strip .x-tab-with-icon span.x-tab-strip-text{padding-left:20px;background-position:0 3px;background-repeat:no-repeat}.x-tab-strip-active,.x-tab-strip-active a.x-tab-right{cursor:default}.x-tab-strip-active span.x-tab-strip-text{cursor:default}.x-tab-strip-disabled .x-tabs-text{cursor:default}.x-tab-panel-body{overflow:hidden}.x-tab-panel-bwrap{overflow:hidden}.ext-ie .x-tab-strip .x-tab-right{position:relative}.x-tab-strip-top .x-tab-strip-active .x-tab-right{margin-bottom:-1px}.ext-ie8 .x-tab-strip li{position:relative}.ext-border-box .ext-ie8 .x-tab-strip-top .x-tab-right{top:1px}.ext-ie8 .x-tab-strip-top{padding-top:1}.ext-border-box .ext-ie8 .x-tab-strip-top{padding-top:0}.ext-ie8 .x-tab-strip .x-tab-strip-closable a.x-tab-strip-close{top:3px}.ext-border-box .ext-ie8 .x-tab-strip .x-tab-strip-closable a.x-tab-strip-close{top:4px}.ext-ie8 .x-tab-strip-bottom .x-tab-right{top:0}.x-tab-strip-top .x-tab-strip-active .x-tab-right span.x-tab-strip-text{padding-bottom:5px}.x-tab-strip-bottom .x-tab-strip-active .x-tab-right{margin-top:-1px}.x-tab-strip-bottom .x-tab-strip-active .x-tab-right span.x-tab-strip-text{padding-top:5px}.x-tab-strip-top .x-tab-right{background:transparent no-repeat 0 -51px;padding-left:10px}.x-tab-strip-top .x-tab-left{background:transparent no-repeat right -351px;padding-right:10px}.x-tab-strip-top .x-tab-strip-inner{background:transparent repeat-x 0 -201px}.x-tab-strip-top .x-tab-strip-over .x-tab-right{background-position:0 -101px}.x-tab-strip-top .x-tab-strip-over .x-tab-left{background-position:right -401px}.x-tab-strip-top .x-tab-strip-over .x-tab-strip-inner{background-position:0 -251px}.x-tab-strip-top .x-tab-strip-active .x-tab-right{background-position:0 0}.x-tab-strip-top .x-tab-strip-active .x-tab-left{background-position:right -301px}.x-tab-strip-top .x-tab-strip-active .x-tab-strip-inner{background-position:0 -151px}.x-tab-strip-bottom .x-tab-right{background:no-repeat bottom right}.x-tab-strip-bottom .x-tab-left{background:no-repeat bottom left}.x-tab-strip-bottom .x-tab-strip-active .x-tab-right{background:no-repeat bottom right}.x-tab-strip-bottom .x-tab-strip-active .x-tab-left{background:no-repeat bottom left}.x-tab-strip-bottom .x-tab-left{margin-right:3px;padding:0 10px}.x-tab-strip-bottom .x-tab-right{padding:0}.x-tab-strip .x-tab-strip-close{display:none}.x-tab-strip-closable{position:relative}.x-tab-strip-closable .x-tab-left{padding-right:19px}.x-tab-strip .x-tab-strip-closable a.x-tab-strip-close{opacity:.6;background-repeat:no-repeat;display:block;width:11px;height:11px;position:absolute;top:3px;right:3px;cursor:pointer;z-index:2}.x-tab-strip .x-tab-strip-active a.x-tab-strip-close{opacity:.8}.x-tab-strip .x-tab-strip-closable a.x-tab-strip-close:hover{opacity:1}.x-tab-panel-body{border:1px solid}.x-tab-panel-body-top{border-top:0 none}.x-tab-panel-body-bottom{border-bottom:0 none}.x-tab-scroller-left{background:transparent no-repeat -18px 0;border-bottom:1px solid;width:18px;position:absolute;left:0;top:0;z-index:10;cursor:pointer}.x-tab-scroller-left-over{background-position:0 0}.x-tab-scroller-left-disabled{background-position:-18px 0;opacity:.5;filter:alpha(opacity=50);cursor:default}.x-tab-scroller-right{background:transparent no-repeat 0 0;border-bottom:1px solid;width:18px;position:absolute;right:0;top:0;z-index:10;cursor:pointer}.x-tab-scroller-right-over{background-position:-18px 0}.x-tab-scroller-right-disabled{background-position:0 0;opacity:.5;filter:alpha(opacity=50);cursor:default}.x-tab-scrolling-bottom .x-tab-scroller-left,.x-tab-scrolling-bottom .x-tab-scroller-right{margin-top:1px}.x-tab-scrolling .x-tab-strip-wrap{margin-left:18px;margin-right:18px}.x-tab-scrolling{position:relative}.x-tab-panel-bbar .x-toolbar{border:1px solid;border-top:0 none;overflow:hidden;padding:2px}.x-tab-panel-tbar .x-toolbar{border:1px solid;border-top:0 none;overflow:hidden;padding:2px}.x-form-field{margin:0}.ext-webkit *:focus{outline:none!important}.x-form-text,textarea.x-form-field{padding:1px 3px;background:repeat-x 0 0;border:1px solid}textarea.x-form-field{padding:2px 3px}.x-form-text,.ext-ie .x-form-file{height:22px;line-height:18px;vertical-align:middle}.ext-ie6 .x-form-text,.ext-ie7 .x-form-text{margin:-1px 0;height:22px;line-height:18px}.x-quirks .ext-ie9 .x-form-text{height:22px;padding-top:3px;padding-bottom:0}.x-quirks .ext-ie9 .x-input-wrapper .x-form-text,.x-quirks .ext-ie9 .x-form-field-trigger-wrap .x-form-text{margin-top:-1px;margin-bottom:-1px}.x-quirks .ext-ie9 .x-input-wrapper .x-form-element{margin-bottom:-1px}.ext-ie6 .x-form-field-wrap .x-form-file-btn,.ext-ie7 .x-form-field-wrap .x-form-file-btn{top:-1px}.ext-ie6 textarea.x-form-field,.ext-ie7 textarea.x-form-field{margin:-1px 0}.ext-strict .x-form-text{height:18px}.ext-safari.ext-mac textarea.x-form-field{margin-bottom:-2px}.ext-gecko .x-form-text,.ext-ie8 .x-form-text{padding-top:2px;padding-bottom:0}.ext-ie6 .x-form-composite .x-form-text.x-box-item,.ext-ie7 .x-form-composite .x-form-text.x-box-item{margin:0!important}textarea{resize:none}.x-form-select-one{height:20px;line-height:18px;vertical-align:middle;border:1px solid}.x-form-check-wrap{line-height:18px;height:auto}.ext-ie .x-form-check-wrap input{width:15px;height:15px}.x-form-check-wrap input{vertical-align:bottom}.x-editor .x-form-check-wrap{padding:3px}.x-editor .x-form-checkbox{height:13px}.x-form-check-group-label{border-bottom:1px solid;margin-bottom:5px;padding-left:3px!important;float:none!important}.x-form-field-wrap .x-form-trigger{width:17px;height:21px;border:0;background:transparent no-repeat 0 0;cursor:pointer;border-bottom:1px solid;position:absolute;top:0}.x-form-field-wrap .x-form-date-trigger,.x-form-field-wrap .x-form-clear-trigger,.x-form-field-wrap .x-form-search-trigger{cursor:pointer}.x-form-field-wrap .x-form-twin-triggers .x-form-trigger{position:static;top:auto;vertical-align:top}.x-form-field-wrap{position:relative;left:0;top:0;text-align:left;white-space:nowrap}.ext-strict .ext-ie8 .x-toolbar-cell .x-form-field-trigger-wrap .x-form-trigger{right:0}.x-form-field-wrap .x-form-trigger-over{background-position:-17px 0}.x-form-field-wrap .x-form-trigger-click{background-position:-34px 0}.x-trigger-wrap-focus .x-form-trigger{background-position:-51px 0}.x-trigger-wrap-focus .x-form-trigger-over{background-position:-68px 0}.x-trigger-wrap-focus .x-form-trigger-click{background-position:-85px 0}.x-trigger-wrap-focus .x-form-trigger{border-bottom:1px solid}.x-item-disabled .x-form-trigger-over{background-position:0 0!important;border-bottom:1px solid}.x-item-disabled .x-form-trigger-click{background-position:0 0!important;border-bottom:1px solid}.x-trigger-noedit{cursor:pointer}.x-form-focus,textarea.x-form-focus{border:1px solid}.x-form-invalid,textarea.x-form-invalid{background:repeat-x bottom;border:1px solid}.x-form-inner-invalid,textarea.x-form-inner-invalid{background:repeat-x bottom}.x-editor{visibility:hidden;padding:0;margin:0}.x-form-grow-sizer{left:-10000px;padding:8px 3px;position:absolute;visibility:hidden;top:-10000px;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}.x-form-grow-sizer p{margin:0!important;border:0 none!important;padding:0!important}.x-form-item{display:block;margin-bottom:4px}.x-form-item label.x-form-item-label{display:block;float:left;width:100px;padding:3px;padding-left:0;clear:left;z-index:2;position:relative}.x-form-element{padding-left:105px;position:relative}.x-form-invalid-msg{padding:2px;padding-left:18px;background:transparent no-repeat 0 2px;line-height:16px;width:200px}.x-form-label-left label.x-form-item-label{text-align:left}.x-form-label-right label.x-form-item-label{text-align:right}.x-form-label-top .x-form-item label.x-form-item-label{width:auto;float:none;clear:none;display:inline;margin-bottom:4px;position:static}.x-form-label-top .x-form-element{padding-left:0;padding-top:4px}.x-form-label-top .x-form-item{padding-bottom:4px}.x-small-editor .x-form-text{height:20px;line-height:16px;vertical-align:middle}.ext-ie6 .x-small-editor .x-form-text,.ext-ie7 .x-small-editor .x-form-text{margin-top:-1px!important;margin-bottom:-1px!important;height:20px!important;line-height:16px!important}.ext-strict .x-small-editor .x-form-text{height:16px!important}.ext-ie6 .x-small-editor .x-form-text,.ext-ie7 .x-small-editor .x-form-text{height:20px;line-height:16px}.ext-border-box .x-small-editor .x-form-text{height:20px}.x-small-editor .x-form-select-one{height:20px;line-height:16px;vertical-align:middle}.x-small-editor .x-form-num-field{text-align:right}.x-small-editor .x-form-field-wrap .x-form-trigger{height:19px}.ext-webkit .x-small-editor .x-form-text{padding-top:3px;font-size:100%}.ext-strict .ext-webkit .x-small-editor .x-form-text{height:14px!important}.x-form-clear{clear:both;height:0;overflow:hidden;line-height:0;font-size:0}.x-form-clear-left{clear:left;height:0;overflow:hidden;line-height:0;font-size:0}.ext-ie6 .x-form-check-wrap input,.ext-border-box .x-form-check-wrap input{margin-top:3px}.x-form-cb-label{position:relative;margin-left:4px;top:2px}.ext-ie .x-form-cb-label{top:1px}.ext-ie6 .x-form-cb-label,.ext-border-box .x-form-cb-label{top:3px}.x-form-display-field{padding-top:2px}.ext-gecko .x-form-display-field,.ext-strict .ext-ie7 .x-form-display-field{padding-top:1px}.ext-ie .x-form-display-field{padding-top:3px}.ext-strict .ext-ie8 .x-form-display-field{padding-top:0}.x-form-column{float:left;padding:0;margin:0;width:48%;overflow:hidden}.x-form .x-form-btns-ct .x-btn{float:right;clear:none}.x-form .x-form-btns-ct .x-form-btns td{border:0;padding:0}.x-form .x-form-btns-ct .x-form-btns-right table{float:right;clear:none}.x-form .x-form-btns-ct .x-form-btns-left table{float:left;clear:none}.x-form .x-form-btns-ct .x-form-btns-center{text-align:center}.x-form .x-form-btns-ct .x-form-btns-center table{margin:0 auto}.x-form .x-form-btns-ct table td.x-form-btn-td{padding:3px}.x-form .x-form-btns-ct .x-btn-focus .x-btn-left{background-position:0 -147px}.x-form .x-form-btns-ct .x-btn-focus .x-btn-right{background-position:0 -168px}.x-form .x-form-btns-ct .x-btn-focus .x-btn-center{background-position:0 -189px}.x-form .x-form-btns-ct .x-btn-click .x-btn-center{background-position:0 -126px}.x-form .x-form-btns-ct .x-btn-click .x-btn-right{background-position:0 -84px}.x-form .x-form-btns-ct .x-btn-click .x-btn-left{background-position:0 -63px}.x-form-invalid-icon{width:16px;height:18px;visibility:hidden;position:absolute;left:0;top:0;display:block;background:transparent no-repeat 0 2px}.x-fieldset{border:1px solid;padding:10px;margin-bottom:10px;display:block}.ext-webkit .x-fieldset-header{padding-top:1px}.ext-ie .x-fieldset legend{margin-bottom:10px}.ext-strict .ext-ie9 .x-fieldset legend.x-fieldset-header{padding-top:1px}.ext-ie .x-fieldset{padding-top:0;padding-bottom:10px}.x-fieldset legend .x-tool-toggle{margin-right:3px;margin-left:0;float:left!important}.x-fieldset legend input{margin-right:3px;float:left!important;height:13px;width:13px}fieldset.x-panel-collapsed{padding-bottom:0!important;border-width:1px 1px 0 1px!important;border-left-color:transparent;border-right-color:transparent}.ext-ie6 fieldset.x-panel-collapsed{padding-bottom:0!important;border-width:1px 0 0 0!important;margin-left:1px;margin-right:1px}fieldset.x-panel-collapsed .x-fieldset-bwrap{visibility:hidden;position:absolute;left:-1000px;top:-1000px}.x-fieldset-noborder{border:0 none transparent}.x-fieldset-noborder legend{margin-left:-3px}.ext-ie .x-fieldset-noborder legend{position:relative;margin-bottom:23px}.ext-ie .x-fieldset-noborder legend span{position:absolute;left:16px}.ext-gecko .x-window-body .x-form-item{outline:0;overflow:auto}.ext-mac.ext-gecko .x-window-body .x-form-item{overflow:hidden}.ext-gecko .x-form-item{outline:0}.x-hide-label label.x-form-item-label{display:none}.x-hide-label .x-form-element{padding-left:0!important}.x-form-label-top .x-hide-label label.x-form-item-label{display:none}.x-fieldset{overflow:hidden}.x-fieldset-bwrap{overflow:hidden}.x-fieldset-body{overflow:hidden}.x-btn{cursor:pointer;white-space:nowrap}.x-btn button{border:0 none;background-color:transparent;padding-left:3px;padding-right:3px;cursor:pointer;margin:0;overflow:visible;width:auto;outline:0 none}* html .ext-ie .x-btn button{width:1px}.ext-gecko .x-btn button,.ext-webkit .x-btn button{padding-left:0;padding-right:0}.ext-gecko .x-btn button::-moz-focus-inner{padding:0}.ext-ie .x-btn button{padding-top:2px}.x-btn td{padding:0!important}.x-btn-text{cursor:pointer;white-space:nowrap;padding:0}.x-btn-noicon .x-btn-small .x-btn-text{height:16px}.x-btn-noicon .x-btn-medium .x-btn-text{height:24px}.x-btn-noicon .x-btn-large .x-btn-text{height:32px}.x-btn-icon .x-btn-text{background-position:center;background-repeat:no-repeat}.x-btn-icon .x-btn-small .x-btn-text{height:16px;width:16px}.x-btn-icon .x-btn-medium .x-btn-text{height:24px;width:24px}.x-btn-icon .x-btn-large .x-btn-text{height:32px;width:32px}.x-btn-text-icon .x-btn-icon-small-left .x-btn-text{background-position:0 center;background-repeat:no-repeat;padding-left:18px;height:16px}.x-btn-text-icon .x-btn-icon-medium-left .x-btn-text{background-position:0 center;background-repeat:no-repeat;padding-left:26px;height:24px}.x-btn-text-icon .x-btn-icon-large-left .x-btn-text{background-position:0 center;background-repeat:no-repeat;padding-left:34px;height:32px}.x-btn-text-icon .x-btn-icon-small-top .x-btn-text{background-position:center 0;background-repeat:no-repeat;padding-top:18px}.x-btn-text-icon .x-btn-icon-medium-top .x-btn-text{background-position:center 0;background-repeat:no-repeat;padding-top:26px}.x-btn-text-icon .x-btn-icon-large-top .x-btn-text{background-position:center 0;background-repeat:no-repeat;padding-top:34px}.x-btn-text-icon .x-btn-icon-small-right .x-btn-text{background-position:right center;background-repeat:no-repeat;padding-right:18px;height:16px}.x-btn-text-icon .x-btn-icon-medium-right .x-btn-text{background-position:right center;background-repeat:no-repeat;padding-right:26px;height:24px}.x-btn-text-icon .x-btn-icon-large-right .x-btn-text{background-position:right center;background-repeat:no-repeat;padding-right:34px;height:32px}.x-btn-text-icon .x-btn-icon-small-bottom .x-btn-text{background-position:center bottom;background-repeat:no-repeat;padding-bottom:18px}.x-btn-text-icon .x-btn-icon-medium-bottom .x-btn-text{background-position:center bottom;background-repeat:no-repeat;padding-bottom:26px}.x-btn-text-icon .x-btn-icon-large-bottom .x-btn-text{background-position:center bottom;background-repeat:no-repeat;padding-bottom:34px}.x-btn-tr i,.x-btn-tl i,.x-btn-mr i,.x-btn-ml i,.x-btn-br i,.x-btn-bl i{font-size:1px;line-height:1px;width:3px;display:block;overflow:hidden}.x-btn-tr i,.x-btn-tl i,.x-btn-br i,.x-btn-bl i{height:3px}.x-btn-tl{width:3px;height:3px;background:no-repeat 0 0}.x-btn-tr{width:3px;height:3px;background:no-repeat -3px 0}.x-btn-tc{height:3px;background:repeat-x 0 -6px}.x-btn-ml{width:3px;background:no-repeat 0 -24px}.x-btn-mr{width:3px;background:no-repeat -3px -24px}.x-btn-mc{background:repeat-x 0 -1096px;vertical-align:middle;text-align:center;padding:0 5px;cursor:pointer;white-space:nowrap}.ext-strict .ext-ie6 .x-btn-mc,.ext-strict .ext-ie7 .x-btn-mc{height:100%}.x-btn-bl{width:3px;height:3px;background:no-repeat 0 -3px}.x-btn-br{width:3px;height:3px;background:no-repeat -3px -3px}.x-btn-bc{height:3px;background:repeat-x 0 -15px}.x-btn-over .x-btn-tl{background-position:-6px 0}.x-btn-over .x-btn-tr{background-position:-9px 0}.x-btn-over .x-btn-tc{background-position:0 -9px}.x-btn-over .x-btn-ml{background-position:-6px -24px}.x-btn-over .x-btn-mr{background-position:-9px -24px}.x-btn-over .x-btn-mc{background-position:0 -2168px}.x-btn-over .x-btn-bl{background-position:-6px -3px}.x-btn-over .x-btn-br{background-position:-9px -3px}.x-btn-over .x-btn-bc{background-position:0 -18px}.x-btn-click .x-btn-tl,.x-btn-menu-active .x-btn-tl,.x-btn-pressed .x-btn-tl{background-position:-12px 0}.x-btn-click .x-btn-tr,.x-btn-menu-active .x-btn-tr,.x-btn-pressed .x-btn-tr{background-position:-15px 0}.x-btn-click .x-btn-tc,.x-btn-menu-active .x-btn-tc,.x-btn-pressed .x-btn-tc{background-position:0 -12px}.x-btn-click .x-btn-ml,.x-btn-menu-active .x-btn-ml,.x-btn-pressed .x-btn-ml{background-position:-12px -24px}.x-btn-click .x-btn-mr,.x-btn-menu-active .x-btn-mr,.x-btn-pressed .x-btn-mr{background-position:-15px -24px}.x-btn-click .x-btn-mc,.x-btn-menu-active .x-btn-mc,.x-btn-pressed .x-btn-mc{background-position:0 -3240px}.x-btn-click .x-btn-bl,.x-btn-menu-active .x-btn-bl,.x-btn-pressed .x-btn-bl{background-position:-12px -3px}.x-btn-click .x-btn-br,.x-btn-menu-active .x-btn-br,.x-btn-pressed .x-btn-br{background-position:-15px -3px}.x-btn-click .x-btn-bc,.x-btn-menu-active .x-btn-bc,.x-btn-pressed .x-btn-bc{background-position:0 -21px}.x-btn-disabled *{cursor:default!important}.x-btn-mc em.x-btn-arrow{display:block;background:transparent no-repeat right center;padding-right:10px}.x-btn-mc em.x-btn-split{display:block;background:transparent no-repeat right center;padding-right:14px}.x-btn-mc em.x-btn-arrow-bottom{display:block;background:transparent no-repeat center bottom;padding-bottom:14px}.x-btn-mc em.x-btn-split-bottom{display:block;background:transparent no-repeat center bottom;padding-bottom:14px}.x-btn-as-arrow .x-btn-mc em{display:block;background-color:transparent;padding-bottom:14px}.x-btn-group{padding:1px}.x-btn-group-header{padding:2px;text-align:center}.x-btn-group-tc{background:transparent repeat-x 0 0;overflow:hidden}.x-btn-group-tl{background:transparent no-repeat 0 0;padding-left:3px}.x-btn-group-tr{background:transparent no-repeat right 0;padding-right:3px}.x-btn-group-bc{background:transparent repeat-x 0 bottom}.x-btn-group-bl{background:transparent no-repeat 0 bottom;padding-left:3px}.x-btn-group-br{background:transparent no-repeat right bottom;padding-right:3px}.x-btn-group-mc{border:0 none;padding:1px 0 0 0;margin:0}.x-btn-group-mc .x-btn-group-body{background-color:transparent;border:0 none}.x-btn-group-ml{background:transparent repeat-y 0 0;padding-left:3px}.x-btn-group-mr{background:transparent repeat-y right 0;padding-right:3px}.x-btn-group-bc .x-btn-group-footer{padding-bottom:6px}.x-panel-nofooter .x-btn-group-bc{height:3px;font-size:0;line-height:0}.x-btn-group-bwrap{overflow:hidden}.x-btn-group-body{overflow:hidden}.x-btn-group-notitle .x-btn-group-tc{background:transparent repeat-x 0 0;overflow:hidden;height:2px}.x-toolbar{border-style:solid;border-width:0 0 1px 0;display:block;padding:2px;background:repeat-x top left;position:relative;left:0;top:0;overflow:hidden}.x-toolbar-left{width:100%}.x-toolbar .x-item-disabled .x-btn-icon{opacity:.35;filter:alpha(opacity=35)}.x-toolbar td{vertical-align:middle}.x-toolbar td,.x-toolbar span,.x-toolbar input,.x-toolbar div,.x-toolbar select,.x-toolbar label{white-space:nowrap}.x-toolbar .x-item-disabled{cursor:default;opacity:.6;filter:alpha(opacity=60)}.x-toolbar .x-item-disabled *{cursor:default}.x-toolbar .x-toolbar-cell{vertical-align:middle}.x-toolbar .x-btn-tl,.x-toolbar .x-btn-tr,.x-toolbar .x-btn-tc,.x-toolbar .x-btn-ml,.x-toolbar .x-btn-mr,.x-toolbar .x-btn-mc,.x-toolbar .x-btn-bl,.x-toolbar .x-btn-br,.x-toolbar .x-btn-bc{background-position:500px 500px}.x-toolbar .x-btn-over .x-btn-tl{background-position:-6px 0}.x-toolbar .x-btn-over .x-btn-tr{background-position:-9px 0}.x-toolbar .x-btn-over .x-btn-tc{background-position:0 -9px}.x-toolbar .x-btn-over .x-btn-ml{background-position:-6px -24px}.x-toolbar .x-btn-over .x-btn-mr{background-position:-9px -24px}.x-toolbar .x-btn-over .x-btn-mc{background-position:0 -2168px}.x-toolbar .x-btn-over .x-btn-bl{background-position:-6px -3px}.x-toolbar .x-btn-over .x-btn-br{background-position:-9px -3px}.x-toolbar .x-btn-over .x-btn-bc{background-position:0 -18px}.x-toolbar .x-btn-click .x-btn-tl,.x-toolbar .x-btn-menu-active .x-btn-tl,.x-toolbar .x-btn-pressed .x-btn-tl{background-position:-12px 0}.x-toolbar .x-btn-click .x-btn-tr,.x-toolbar .x-btn-menu-active .x-btn-tr,.x-toolbar .x-btn-pressed .x-btn-tr{background-position:-15px 0}.x-toolbar .x-btn-click .x-btn-tc,.x-toolbar .x-btn-menu-active .x-btn-tc,.x-toolbar .x-btn-pressed .x-btn-tc{background-position:0 -12px}.x-toolbar .x-btn-click .x-btn-ml,.x-toolbar .x-btn-menu-active .x-btn-ml,.x-toolbar .x-btn-pressed .x-btn-ml{background-position:-12px -24px}.x-toolbar .x-btn-click .x-btn-mr,.x-toolbar .x-btn-menu-active .x-btn-mr,.x-toolbar .x-btn-pressed .x-btn-mr{background-position:-15px -24px}.x-toolbar .x-btn-click .x-btn-mc,.x-toolbar .x-btn-menu-active .x-btn-mc,.x-toolbar .x-btn-pressed .x-btn-mc{background-position:0 -3240px}.x-toolbar .x-btn-click .x-btn-bl,.x-toolbar .x-btn-menu-active .x-btn-bl,.x-toolbar .x-btn-pressed .x-btn-bl{background-position:-12px -3px}.x-toolbar .x-btn-click .x-btn-br,.x-toolbar .x-btn-menu-active .x-btn-br,.x-toolbar .x-btn-pressed .x-btn-br{background-position:-15px -3px}.x-toolbar .x-btn-click .x-btn-bc,.x-toolbar .x-btn-menu-active .x-btn-bc,.x-toolbar .x-btn-pressed .x-btn-bc{background-position:0 -21px}.x-toolbar div.xtb-text{padding:2px 2px 0;line-height:16px;display:block}.x-toolbar .xtb-sep{background-position:center;background-repeat:no-repeat;display:block;font-size:1px;height:16px;width:4px;overflow:hidden;cursor:default;margin:0 2px 0;border:0}.x-toolbar .xtb-spacer{width:2px}.x-tbar-page-number{width:30px;height:14px}.ext-ie .x-tbar-page-number{margin-top:2px}.x-paging-info{position:absolute;top:5px;right:8px}.x-toolbar-ct{width:100%}.x-toolbar-right td{text-align:center}.x-panel-tbar,.x-panel-bbar,.x-window-tbar,.x-window-bbar,.x-tab-panel-tbar,.x-tab-panel-bbar,.x-plain-tbar,.x-plain-bbar{overflow:hidden}.x-toolbar-more .x-btn-small .x-btn-text{height:16px;width:12px}.x-toolbar-more em.x-btn-arrow{display:inline;background-color:transparent;padding-right:0}.x-toolbar-more .x-btn-mc em.x-btn-arrow{background-image:none}div.x-toolbar-no-items{color:gray!important;padding:5px 10px!important}.ext-border-box .x-toolbar-cell .x-form-text{margin-bottom:-1px!important}.ext-border-box .x-toolbar-cell .x-form-field-wrap .x-form-text{margin:0!important}.ext-ie .x-toolbar-cell .x-form-field-wrap{height:21px}.ext-ie .x-toolbar-cell .x-form-text{position:relative;top:-1px}.ext-strict .ext-ie8 .x-toolbar-cell .x-form-field-trigger-wrap .x-form-text,.ext-strict .ext-ie .x-toolbar-cell .x-form-text{top:0}.x-toolbar-right td .x-form-field-trigger-wrap{text-align:left}.x-toolbar-cell .x-form-checkbox,.x-toolbar-cell .x-form-radio{margin-top:5px}.x-toolbar-cell .x-form-cb-label{vertical-align:bottom;top:1px}.ext-ie .x-toolbar-cell .x-form-checkbox,.ext-ie .x-toolbar-cell .x-form-radio{margin-top:4px}.ext-ie .x-toolbar-cell .x-form-cb-label{top:0}.x-grid3{position:relative;overflow:hidden}.x-grid-panel .x-panel-body{overflow:hidden!important}.x-grid-panel .x-panel-mc .x-panel-body{border:1px solid}.x-grid3 table{table-layout:fixed}.x-grid3-viewport{overflow:hidden}.x-grid3-hd-row td,.x-grid3-row td,.x-grid3-summary-row td{outline:0;-moz-user-focus:normal}.x-grid3-row td,.x-grid3-summary-row td{line-height:13px;vertical-align:top;padding-left:1px;padding-right:1px;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-grid3-cell{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-grid3-hd-row td{line-height:15px;vertical-align:middle;border-left:1px solid;border-right:1px solid}.x-grid3-hd-row .x-grid3-marker-hd{padding:3px}.x-grid3-row .x-grid3-marker{padding:3px}.x-grid3-cell-inner,.x-grid3-hd-inner{overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;padding:3px 3px 3px 5px;white-space:nowrap}.x-action-col-cell .x-grid3-cell-inner{padding-top:1px;padding-bottom:1px}.x-action-col-icon{cursor:pointer}.x-grid3-hd-inner{position:relative;cursor:inherit;padding:4px 3px 4px 5px}.x-grid3-row-body{white-space:normal}.x-grid3-body-cell{outline:0 none}.ext-ie .x-grid3-cell-inner,.ext-ie .x-grid3-hd-inner{width:100%}.ext-strict .x-grid3-cell-inner,.ext-strict .x-grid3-hd-inner{width:auto}.x-grid-row-loading{background:no-repeat center center}.x-grid-page{overflow:hidden}.x-grid3-row{cursor:default;border:1px solid;width:100%}.x-grid3-row-over{border:1px solid;background:repeat-x left top}.x-grid3-resize-proxy{width:1px;left:0;cursor:e-resize;cursor:col-resize;position:absolute;top:0;height:100px;overflow:hidden;visibility:hidden;border:0 none;z-index:7}.x-grid3-resize-marker{width:1px;left:0;position:absolute;top:0;height:100px;overflow:hidden;visibility:hidden;border:0 none;z-index:7}.x-grid3-focus{position:absolute;left:0;top:0;width:1px;height:1px;line-height:1px;font-size:1px;outline:0 none;-moz-user-select:text;-khtml-user-select:text;-webkit-user-select:ignore}.x-grid3-header{background:repeat-x 0 bottom;cursor:default;padding:1px 0 0 0}.x-grid3-header-pop{border-left:1px solid;float:right;clear:none}.x-grid3-header-pop-inner{border-left:1px solid;width:14px;height:19px;background:transparent no-repeat center center}.ext-ie .x-grid3-header-pop-inner{width:15px}.ext-strict .x-grid3-header-pop-inner{width:14px}.x-grid3-header-inner{overflow:hidden;float:left}.x-grid3-header-offset{padding-left:1px;text-align:left}td.x-grid3-hd-over,td.sort-desc,td.sort-asc,td.x-grid3-hd-menu-open{border-left:1px solid;border-right:1px solid}td.x-grid3-hd-over .x-grid3-hd-inner,td.sort-desc .x-grid3-hd-inner,td.sort-asc .x-grid3-hd-inner,td.x-grid3-hd-menu-open .x-grid3-hd-inner{background:repeat-x left bottom}.x-grid3-sort-icon{background-repeat:no-repeat;display:none;height:4px;width:13px;margin-left:3px;vertical-align:middle}.sort-asc .x-grid3-sort-icon,.sort-desc .x-grid3-sort-icon{display:inline}.ext-strict .ext-ie .x-grid3-header-inner,.ext-strict .ext-ie6 .x-grid3-hd{position:relative}.ext-strict .ext-ie6 .x-grid3-hd-inner{position:static}.x-grid3-scroller{overflow:auto;position:relative}.x-grid3-cell-text,.x-grid3-hd-text{display:block;padding:3px 5px 3px 5px;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-grid3-split{background-position:center;background-repeat:no-repeat;cursor:e-resize;cursor:col-resize;display:block;font-size:1px;height:16px;overflow:hidden;position:absolute;top:2px;width:6px;z-index:3}.x-dd-drag-proxy .x-grid3-hd-inner{background:repeat-x left bottom;width:120px;padding:3px;border:1px solid;overflow:hidden}.col-move-top,.col-move-bottom{width:9px;height:9px;position:absolute;top:0;line-height:1px;font-size:1px;overflow:hidden;visibility:hidden;z-index:20000;background:transparent no-repeat left top}.x-grid3-row-selected{border:1px dotted}.x-grid3-locked td.x-grid3-row-marker,.x-grid3-locked .x-grid3-row-selected td.x-grid3-row-marker{background:repeat-x 0 bottom!important;vertical-align:middle!important;padding:0;border-top:1px solid;border-bottom:none!important;border-right:1px solid!important;text-align:center}.x-grid3-locked td.x-grid3-row-marker div,.x-grid3-locked .x-grid3-row-selected td.x-grid3-row-marker div{padding:0 4px;text-align:center}.x-grid3-dirty-cell{background:transparent no-repeat 0 0}.x-grid3-topbar,.x-grid3-bottombar{overflow:hidden;display:none;position:relative}.x-grid3-topbar .x-toolbar{border-right:0 none}.x-grid3-bottombar .x-toolbar{border-right:0 none;border-bottom:0 none;border-top:1px solid}.x-props-grid .x-grid3-cell{padding:1px}.x-props-grid .x-grid3-td-name .x-grid3-cell-inner{background:transparent repeat-y -16px!important;padding-left:12px}.x-props-grid .x-grid3-body .x-grid3-td-name{padding:1px;padding-right:0;border:0 none;border-right:1px solid}.x-grid3-col-dd{border:0 none;padding:0;background-color:transparent}.x-dd-drag-ghost .x-grid3-dd-wrap{padding:1px 3px 3px 1px}.x-grid3-hd{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-grid3-hd-btn{display:none;position:absolute;width:14px;background:no-repeat left center;right:0;top:0;z-index:2;cursor:pointer}.x-grid3-hd-over .x-grid3-hd-btn,.x-grid3-hd-menu-open .x-grid3-hd-btn{display:block}a.x-grid3-hd-btn:hover{background-position:-14px center}.x-grid3-body .x-grid3-td-expander{background:transparent repeat-y right}.x-grid3-body .x-grid3-td-expander .x-grid3-cell-inner{padding:0!important;height:100%}.x-grid3-row-expander{width:100%;height:18px;background-position:4px 2px;background-repeat:no-repeat;background-color:transparent}.x-grid3-row-collapsed .x-grid3-row-expander{background-position:4px 2px}.x-grid3-row-expanded .x-grid3-row-expander{background-position:-21px 2px}.x-grid3-row-collapsed .x-grid3-row-body{display:none!important}.x-grid3-row-expanded .x-grid3-row-body{display:block!important}.x-grid3-body .x-grid3-td-checker{background:transparent repeat-y right}.x-grid3-body .x-grid3-td-checker .x-grid3-cell-inner,.x-grid3-header .x-grid3-td-checker .x-grid3-hd-inner{padding:0!important;height:100%}.x-grid3-row-checker,.x-grid3-hd-checker{width:100%;height:23px;background-position:2px 2px;background-repeat:no-repeat;background-color:transparent}.x-grid3-row .x-grid3-row-checker{background-position:2px 6px}.x-grid3-row-selected .x-grid3-row-checker,.x-grid3-hd-checker-on .x-grid3-hd-checker,.x-grid3-row-checked .x-grid3-row-checker{background-position:-23px 6px}.x-grid3-hd-checker{background-position:2px 3px}.ext-border-box .x-grid3-hd-checker{background-position:2px 3px}.x-grid3-hd-checker-on .x-grid3-hd-checker{background-position:-23px 3px}.ext-border-box .x-grid3-hd-checker-on .x-grid3-hd-checker{background-position:-23px 3px}.x-grid3-body .x-grid3-td-numberer{background:transparent repeat-y right}.x-grid3-body .x-grid3-td-numberer .x-grid3-cell-inner{padding:3px 5px 0 0!important;text-align:right}.x-grid3-body .x-grid3-td-row-icon{background:transparent repeat-y right;vertical-align:top;text-align:center}.x-grid3-body .x-grid3-td-row-icon .x-grid3-cell-inner{padding:0!important;background-position:center center;background-repeat:no-repeat;width:16px;height:16px;margin-left:2px;margin-top:3px}.x-grid3-body .x-grid3-row-selected .x-grid3-td-numberer,.x-grid3-body .x-grid3-row-selected .x-grid3-td-checker,.x-grid3-body .x-grid3-row-selected .x-grid3-td-expander{background:transparent repeat-y right}.x-grid3-body .x-grid3-check-col-td .x-grid3-cell-inner{padding:1px 0 0 0!important}.x-grid3-check-col{width:100%;height:16px;background-position:center center;background-repeat:no-repeat;background-color:transparent}.x-grid3-check-col-on{width:100%;height:16px;background-position:center center;background-repeat:no-repeat;background-color:transparent}.x-grid-group-hd{border-bottom:2px solid;cursor:pointer;padding-top:6px}.x-grid-group-hd div.x-grid-group-title{background:transparent no-repeat 3px 3px;padding:4px 4px 4px 17px}.x-grid-group-collapsed .x-grid-group-body{display:none}.ext-ie6 .x-grid3 .x-editor .x-form-text,.ext-ie7 .x-grid3 .x-editor .x-form-text{position:relative;top:-1px}.ext-ie .x-props-grid .x-editor .x-form-text{position:static;top:0}.x-grid-empty{padding:10px}.ext-ie7 .x-grid-panel .x-panel-bbar{position:relative}.ext-ie7 .x-grid-panel .x-panel-mc .x-panel-bbar{position:static}.ext-ie6 .x-grid3-header{position:relative}.ext-webkit .x-grid-panel .x-panel-bwrap{-webkit-user-select:none}.ext-webkit .x-tbar-page-number{-webkit-user-select:ignore}.x-grid-with-col-lines .x-grid3-row td.x-grid3-cell{padding-right:0;border-right:1px solid}.x-pivotgrid .x-grid3-header-offset table{width:100%;border-collapse:collapse}.x-pivotgrid .x-grid3-header-offset table td{padding:4px 3px 4px 5px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:11px;line-height:13px;font-family:tahoma}.x-pivotgrid .x-grid3-row-headers{display:block;float:left}.x-pivotgrid .x-grid3-row-headers table{height:100%;width:100%;border-collapse:collapse}.x-pivotgrid .x-grid3-row-headers table td{height:18px;padding:2px 7px 0 0;text-align:right;text-overflow:ellipsis;font-size:11px;font-family:tahoma}.ext-gecko .x-pivotgrid .x-grid3-row-headers table td{height:21px}.x-grid3-header-title{top:0;left:0;position:absolute;text-align:center;vertical-align:middle;font-family:tahoma;font-size:11px;padding:1px;display:table-cell}.x-grid3-header-title span{position:absolute;top:50%;left:0;width:100%;margin-top:-6px}.x-dd-drag-proxy{position:absolute;left:0;top:0;visibility:hidden;z-index:15000}.x-dd-drag-ghost{opacity:.85;filter:alpha(opacity=85);border:1px solid;padding:3px;padding-left:20px;white-space:nowrap}.x-dd-drag-repair .x-dd-drag-ghost{opacity:.4;filter:alpha(opacity=40);border:0 none;padding:0;background-color:transparent}.x-dd-drag-repair .x-dd-drop-icon{visibility:hidden}.x-dd-drop-icon{position:absolute;top:3px;left:3px;display:block;width:16px;height:16px;background-color:transparent;background-position:center;background-repeat:no-repeat;z-index:1}.x-view-selector{position:absolute;left:0;top:0;width:0;border:1px dotted;opacity:.5;filter:alpha(opacity=50)}.ext-strict .ext-ie .x-tree .x-panel-bwrap{position:relative;overflow:hidden}.x-tree-icon,.x-tree-ec-icon,.x-tree-elbow-line,.x-tree-elbow,.x-tree-elbow-end,.x-tree-elbow-plus,.x-tree-elbow-minus,.x-tree-elbow-end-plus,.x-tree-elbow-end-minus{border:0 none;height:18px;margin:0;padding:0;vertical-align:top;width:16px;background-repeat:no-repeat}.x-tree-node-collapsed .x-tree-node-icon,.x-tree-node-expanded .x-tree-node-icon,.x-tree-node-leaf .x-tree-node-icon{border:0 none;height:18px;margin:0;padding:0;vertical-align:top;width:16px;background-position:center;background-repeat:no-repeat}.ext-ie .x-tree-node-indent img,.ext-ie .x-tree-node-icon,.ext-ie .x-tree-ec-icon{vertical-align:middle!important}.ext-strict .ext-ie8 .x-tree-node-indent img,.ext-strict .ext-ie8 .x-tree-node-icon,.ext-strict .ext-ie8 .x-tree-ec-icon{vertical-align:top!important}input.x-tree-node-cb{margin-left:1px;height:19px;vertical-align:bottom}.ext-ie input.x-tree-node-cb{margin-left:0;margin-top:1px;width:16px;height:16px;vertical-align:middle}.ext-strict .ext-ie8 input.x-tree-node-cb{margin:1px 1px;height:14px;vertical-align:bottom}.ext-strict .ext-ie8 input.x-tree-node-cb+a{vertical-align:bottom}.ext-opera input.x-tree-node-cb{height:14px;vertical-align:middle}.x-tree-noicon .x-tree-node-icon{width:0;height:0}.x-tree-no-lines .x-tree-elbow{background-color:transparent}.x-tree-no-lines .x-tree-elbow-end{background-color:transparent}.x-tree-no-lines .x-tree-elbow-line{background-color:transparent}.x-tree-arrows .x-tree-elbow{background-color:transparent}.x-tree-arrows .x-tree-elbow-plus{background:transparent no-repeat 0 0}.x-tree-arrows .x-tree-elbow-minus{background:transparent no-repeat -16px 0}.x-tree-arrows .x-tree-elbow-end{background-color:transparent}.x-tree-arrows .x-tree-elbow-end-plus{background:transparent no-repeat 0 0}.x-tree-arrows .x-tree-elbow-end-minus{background:transparent no-repeat -16px 0}.x-tree-arrows .x-tree-elbow-line{background-color:transparent}.x-tree-arrows .x-tree-ec-over .x-tree-elbow-plus{background-position:-32px 0}.x-tree-arrows .x-tree-ec-over .x-tree-elbow-minus{background-position:-48px 0}.x-tree-arrows .x-tree-ec-over .x-tree-elbow-end-plus{background-position:-32px 0}.x-tree-arrows .x-tree-ec-over .x-tree-elbow-end-minus{background-position:-48px 0}.x-tree-elbow-plus,.x-tree-elbow-minus,.x-tree-elbow-end-plus,.x-tree-elbow-end-minus{cursor:pointer}.ext-ie ul.x-tree-node-ct{font-size:0;line-height:0}.x-tree-node{white-space:nowrap}.x-tree-node-el{line-height:18px;cursor:pointer}.x-tree-node a,.x-dd-drag-ghost a{text-decoration:none;-khtml-user-select:none;-moz-user-select:none;-webkit-user-select:ignore;-kthml-user-focus:normal;-moz-user-focus:normal;outline:0 none}.x-tree-node a span,.x-dd-drag-ghost a span{text-decoration:none;padding:1px 3px 1px 2px}.x-tree-node .x-tree-node-disabled .x-tree-node-icon{opacity:.5;filter:alpha(opacity=50)}.x-tree-node .x-tree-node-inline-icon{background-color:transparent}.x-tree-node a:hover,.x-dd-drag-ghost a:hover{text-decoration:none}.x-tree-node div.x-tree-drag-insert-below{border-bottom:1px dotted}.x-tree-node div.x-tree-drag-insert-above{border-top:1px dotted}.x-tree-dd-underline .x-tree-node div.x-tree-drag-insert-below{border-bottom:0 none}.x-tree-dd-underline .x-tree-node div.x-tree-drag-insert-above{border-top:0 none}.x-tree-dd-underline .x-tree-node div.x-tree-drag-insert-below a{border-bottom:2px solid}.x-tree-dd-underline .x-tree-node div.x-tree-drag-insert-above a{border-top:2px solid}.x-tree-node .x-tree-drag-append a span{border:1px dotted}.x-dd-drag-ghost .x-tree-node-indent,.x-dd-drag-ghost .x-tree-ec-icon{display:none!important}.x-date-picker{border:1px solid;border-top:0 none;position:relative}.x-date-picker a{outline:0 none}.x-date-inner,.x-date-inner td,.x-date-inner th{border-collapse:separate}.x-date-middle,.x-date-left,.x-date-right{background:repeat-x 0 -83px;overflow:hidden}.x-date-middle .x-btn-tc,.x-date-middle .x-btn-tl,.x-date-middle .x-btn-tr,.x-date-middle .x-btn-mc,.x-date-middle .x-btn-ml,.x-date-middle .x-btn-mr,.x-date-middle .x-btn-bc,.x-date-middle .x-btn-bl,.x-date-middle .x-btn-br{background:transparent!important;vertical-align:middle}.x-date-middle .x-btn-mc em.x-btn-arrow{background:transparent no-repeat right 0}.x-date-right,.x-date-left{width:18px}.x-date-right{text-align:right}.x-date-middle{padding-top:2px;padding-bottom:2px;width:130px}.x-date-right a,.x-date-left a{display:block;width:16px;height:16px;background-position:center;background-repeat:no-repeat;cursor:pointer;opacity:.6;filter:alpha(opacity=60)}.x-date-right a:hover,.x-date-left a:hover{opacity:1;filter:alpha(opacity=100)}.x-item-disabled .x-date-right a:hover,.x-item-disabled .x-date-left a:hover{opacity:.6;filter:alpha(opacity=60)}.x-date-right a{margin-right:2px;text-decoration:none!important}.x-date-left a{margin-left:2px;text-decoration:none!important}table.x-date-inner{width:100%;table-layout:fixed}.ext-webkit table.x-date-inner{width:175px}.x-date-inner th{width:25px}.x-date-inner th{background:repeat-x left top;text-align:right!important;border-bottom:1px solid;cursor:default;padding:0;border-collapse:separate}.x-date-inner th span{display:block;padding:2px;padding-right:7px}.x-date-inner td{border:1px solid;text-align:right;padding:0}.x-date-inner a{padding:2px 5px;display:block;text-decoration:none;text-align:right}.x-date-inner .x-date-active{cursor:pointer;color:black}.x-date-inner .x-date-selected a{background:repeat-x left top;border:1px solid;padding:1px 4px}.x-date-inner .x-date-today a{border:1px solid;padding:1px 4px}.x-date-inner .x-date-prevday a,.x-date-inner .x-date-nextday a{text-decoration:none!important}.x-date-bottom{padding:4px;border-top:1px solid;background:repeat-x left top}.x-date-inner a:hover,.x-date-inner .x-date-disabled a:hover{text-decoration:none!important}.x-item-disabled .x-date-inner a:hover{background:0}.x-date-inner .x-date-disabled a{cursor:default}.x-date-menu .x-menu-item{padding:1px 24px 1px 4px;white-space:nowrap}.x-date-menu .x-menu-item .x-menu-item-icon{width:10px;height:10px;margin-right:5px;background-position:center -4px!important}.x-date-mp{position:absolute;left:0;top:0;display:none}.x-date-mp td{padding:2px;font:normal 11px arial,helvetica,tahoma,sans-serif}td.x-date-mp-month,td.x-date-mp-year,td.x-date-mp-ybtn{border:0 none;text-align:center;vertical-align:middle;width:25%}.x-date-mp-ok{margin-right:3px}.x-date-mp-btns button{text-decoration:none;text-align:center;text-decoration:none!important;border:1px solid;padding:1px 3px 1px;cursor:pointer}.x-date-mp-btns{background:repeat-x left top}.x-date-mp-btns td{border-top:1px solid;text-align:center}td.x-date-mp-month a,td.x-date-mp-year a{display:block;padding:2px 4px;text-decoration:none;text-align:center}td.x-date-mp-month a:hover,td.x-date-mp-year a:hover{text-decoration:none;cursor:pointer}td.x-date-mp-sel a{padding:1px 3px;background:repeat-x left top;border:1px solid}.x-date-mp-ybtn a{overflow:hidden;width:15px;height:15px;cursor:pointer;background:transparent no-repeat;display:block;margin:0 auto}.x-date-mp-ybtn a.x-date-mp-next{background-position:0 -120px}.x-date-mp-ybtn a.x-date-mp-next:hover{background-position:-15px -120px}.x-date-mp-ybtn a.x-date-mp-prev{background-position:0 -105px}.x-date-mp-ybtn a.x-date-mp-prev:hover{background-position:-15px -105px}.x-date-mp-ybtn{text-align:center}td.x-date-mp-sep{border-right:1px solid}.x-tip{position:absolute;top:0;left:0;visibility:hidden;z-index:20002;border:0 none}.x-tip .x-tip-close{height:15px;float:right;width:15px;margin:0 0 2px 2px;cursor:pointer;display:none}.x-tip .x-tip-tc{background:transparent no-repeat 0 -62px;padding-top:3px;overflow:hidden}.x-tip .x-tip-tl{background:transparent no-repeat 0 0;padding-left:6px;overflow:hidden}.x-tip .x-tip-tr{background:transparent no-repeat right 0;padding-right:6px;overflow:hidden}.x-tip .x-tip-bc{background:transparent no-repeat 0 -121px;height:3px;overflow:hidden}.x-tip .x-tip-bl{background:transparent no-repeat 0 -59px;padding-left:6px}.x-tip .x-tip-br{background:transparent no-repeat right -59px;padding-right:6px}.x-tip .x-tip-mc{border:0 none}.x-tip .x-tip-ml{background:no-repeat 0 -124px;padding-left:6px}.x-tip .x-tip-mr{background:transparent no-repeat right -124px;padding-right:6px}.ext-ie .x-tip .x-tip-header,.ext-ie .x-tip .x-tip-tc{font-size:0;line-height:0}.ext-border-box .x-tip .x-tip-header,.ext-border-box .x-tip .x-tip-tc{line-height:1px}.x-tip .x-tip-header-text{padding:0;margin:0 0 2px 0}.x-tip .x-tip-body{margin:0!important;line-height:14px;padding:0}.x-tip .x-tip-body .loading-indicator{margin:0}.x-tip-draggable .x-tip-header,.x-tip-draggable .x-tip-header-text{cursor:move}.x-form-invalid-tip .x-tip-tc{background:repeat-x 0 -12px;padding-top:6px}.x-form-invalid-tip .x-tip-bc{background:repeat-x 0 -18px;height:6px}.x-form-invalid-tip .x-tip-bl{background:no-repeat 0 -6px}.x-form-invalid-tip .x-tip-br{background:no-repeat right -6px}.x-form-invalid-tip .x-tip-body{padding:2px}.x-form-invalid-tip .x-tip-body{padding-left:24px;background:transparent no-repeat 2px 2px}.x-tip-anchor{position:absolute;width:9px;height:10px;overflow:hidden;background:transparent no-repeat 0 0}.x-tip-anchor-bottom{background-position:-9px 0}.x-tip-anchor-right{background-position:-18px 0;width:10px}.x-tip-anchor-left{background-position:-28px 0;width:10px}.x-menu{z-index:15000;background:repeat-y}.x-menu-floating{border:1px solid}.x-menu a{text-decoration:none!important}.ext-ie .x-menu{overflow:hidden}.x-menu-list{padding:2px;background-color:transparent;border:0 none;overflow:hidden;overflow-y:hidden}.ext-strict .ext-ie .x-menu-list{position:relative}.x-menu li{line-height:100%}.x-menu li.x-menu-sep-li{font-size:1px;line-height:1px}.x-menu-list-item{white-space:nowrap;display:block;padding:1px}.x-menu-item{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore}.x-menu-item-arrow{background:transparent no-repeat right}.x-menu-sep{display:block;font-size:1px;line-height:1px;margin:2px 3px;border-bottom:1px solid;overflow:hidden}.x-menu-focus{position:absolute;left:-1px;top:-1px;width:1px;height:1px;line-height:1px;font-size:1px;outline:0 none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore;overflow:hidden;display:block}a.x-menu-item{cursor:pointer;display:block;line-height:16px;outline-color:-moz-use-text-color;outline-style:none;outline-width:0;padding:3px 21px 3px 27px;position:relative;text-decoration:none;white-space:nowrap}.x-menu-item-active{background-repeat:repeat-x;background-position:left bottom;border-style:solid;border-width:1px 0;margin:0 1px;padding:0}.x-menu-item-active a.x-menu-item{border-style:solid;border-width:0 1px;margin:0 -1px}.x-menu-item-icon{border:0 none;height:16px;padding:0;vertical-align:top;width:16px;position:absolute;left:3px;top:3px;margin:0;background-position:center}.ext-ie .x-menu-item-icon{left:-24px}.ext-strict .x-menu-item-icon{left:3px}.ext-ie6 .x-menu-item-icon{left:-24px}.ext-ie .x-menu-item-icon{vertical-align:middle}.x-menu-check-item .x-menu-item-icon{background:transparent no-repeat center}.x-menu-group-item .x-menu-item-icon{background-color:transparent}.x-menu-item-checked .x-menu-group-item .x-menu-item-icon{background:transparent no-repeat center}.x-date-menu .x-menu-list{padding:0}.x-menu-date-item{padding:0}.x-menu .x-color-palette,.x-menu .x-date-picker{margin-left:26px;margin-right:4px}.x-menu .x-date-picker{border:1px solid;margin-top:2px;margin-bottom:2px}.x-menu-plain .x-color-palette,.x-menu-plain .x-date-picker{margin:0;border:0 none}.x-date-menu{padding:0!important}.ext-strict .ext-ie6 .x-menu-sep-li{padding:3px 4px}.ext-strict .ext-ie6 .x-menu-sep{margin:0;height:1px}.ext-webkit .x-menu-sep{height:1px}.ext-ie .x-date-menu{height:199px}.ext-strict .ext-ie .x-date-menu,.ext-border-box .ext-ie8 .x-date-menu{height:197px}.ext-strict .ext-ie7 .x-date-menu{height:195px}.ext-strict .ext-ie8 .x-date-menu{height:auto}.x-cycle-menu .x-menu-item-checked{border:1px dotted!important;padding:0}.x-menu .x-menu-scroller{width:100%;background-repeat:no-repeat;background-position:center;height:8px;line-height:8px;cursor:pointer;margin:0;padding:0}.x-menu .x-menu-scroller-active{height:6px;line-height:6px}.x-menu-list-item-indent{padding-left:27px}.x-box-tl{background:transparent no-repeat 0 0}.x-box-tc{height:8px;background:transparent repeat-x 0 0;overflow:hidden}.x-box-tr{background:transparent no-repeat right -8px}.x-box-ml{background:transparent repeat-y 0;padding-left:4px;overflow:hidden}.x-box-mc{background:repeat-x 0 -16px;padding:4px 10px}.x-box-mc h3{margin:0 0 4px 0}.x-box-mr{background:transparent repeat-y right;padding-right:4px;overflow:hidden}.x-box-bl{background:transparent no-repeat 0 -16px}.x-box-bc{background:transparent repeat-x 0 -8px;height:8px;overflow:hidden}.x-box-br{background:transparent no-repeat right -24px}.x-box-tl,.x-box-bl{padding-left:8px;overflow:hidden}.x-box-tr,.x-box-br{padding-right:8px;overflow:hidden}.x-combo-list{border:1px solid;overflow:hidden}.x-combo-list-inner{overflow:auto;position:relative;overflow-x:hidden}.x-combo-list-hd{border-bottom:1px solid;padding:3px}.x-resizable-pinned .x-combo-list-inner{border-bottom:1px solid}.x-combo-list-item{padding:2px;border:1px solid;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.x-combo-list .x-combo-selected{border:1px dotted!important;cursor:pointer}.x-combo-list .x-toolbar{border-top:1px solid;border-bottom:0 none}.x-panel{border-style:solid;border-width:0}.x-panel-header{overflow:hidden;padding:5px 3px 4px 5px;border:1px solid;line-height:15px;background:transparent repeat-x 0 -1px}.x-panel-body{border:1px solid;border-top:0 none;overflow:hidden;position:relative}.x-panel-bbar .x-toolbar,.x-panel-tbar .x-toolbar{border:1px solid;border-top:0 none;overflow:hidden;padding:2px}.x-panel-tbar-noheader .x-toolbar,.x-panel-mc .x-panel-tbar .x-toolbar{border-top:1px solid;border-bottom:0 none}.x-panel-body-noheader,.x-panel-mc .x-panel-body{border-top:1px solid}.x-panel-header{overflow:hidden}.x-panel-tl .x-panel-header{padding:5px 0 4px 0;border:0 none;background:transparent no-repeat}.x-panel-tl .x-panel-icon,.x-window-tl .x-panel-icon{padding-left:20px!important;background-repeat:no-repeat;background-position:0 4px}.x-panel-inline-icon{width:16px;height:16px;background-repeat:no-repeat;background-position:0 0;vertical-align:middle;margin-right:4px;margin-top:-1px;margin-bottom:-1px}.x-panel-tc{background:transparent repeat-x 0 0;overflow:hidden}.ext-strict .ext-ie7 .x-panel-tc{overflow:visible}.x-panel-tl{background:transparent no-repeat 0 0;padding-left:6px;border-bottom:1px solid}.x-panel-tr{background:transparent no-repeat right 0;padding-right:6px}.x-panel-bc{background:transparent repeat-x 0 bottom}.x-panel-bl{background:transparent no-repeat 0 bottom;padding-left:0}.x-panel-br{background:transparent no-repeat right bottom;padding-right:0}.x-panel-mc{border:0 none;padding:0;margin:0;padding-top:6px}.x-panel-mc .x-panel-body{background-color:transparent;border:0 none}.x-panel-ml{background:repeat-y 0 0;padding-left:6px}.x-panel-mr{background:transparent repeat-y right 0;padding-right:6px}.x-panel-bc .x-panel-footer{padding-bottom:6px}.x-panel-nofooter .x-panel-bc,.x-panel-nofooter .x-window-bc{height:6px;font-size:0;line-height:0}.x-panel-bwrap{overflow:hidden;left:0;top:0}.x-panel-body{overflow:hidden}.x-panel-collapsed .x-resizable-handle{display:none}.ext-gecko .x-panel-animated div{overflow:hidden!important}.x-plain-body{overflow:hidden}.x-plain-bbar .x-toolbar{overflow:hidden;padding:2px}.x-plain-tbar .x-toolbar{overflow:hidden;padding:2px}.x-plain-bwrap{overflow:hidden}.x-plain{overflow:hidden}.x-tool{overflow:hidden;width:15px;height:15px;float:right;cursor:pointer;background:transparent no-repeat;margin-left:2px}.x-tool-toggle{background-position:0 -60px}.x-tool-toggle-over{background-position:-15px -60px}.x-panel-collapsed .x-tool-toggle{background-position:0 -75px}.x-panel-collapsed .x-tool-toggle-over{background-position:-15px -75px}.x-tool-close{background-position:0 -0}.x-tool-close-over{background-position:-15px 0}.x-tool-minimize{background-position:0 -15px}.x-tool-minimize-over{background-position:-15px -15px}.x-tool-maximize{background-position:0 -30px}.x-tool-maximize-over{background-position:-15px -30px}.x-tool-restore{background-position:0 -45px}.x-tool-restore-over{background-position:-15px -45px}.x-tool-gear{background-position:0 -90px}.x-tool-gear-over{background-position:-15px -90px}.x-tool-prev{background-position:0 -105px}.x-tool-prev-over{background-position:-15px -105px}.x-tool-next{background-position:0 -120px}.x-tool-next-over{background-position:-15px -120px}.x-tool-pin{background-position:0 -135px}.x-tool-pin-over{background-position:-15px -135px}.x-tool-unpin{background-position:0 -150px}.x-tool-unpin-over{background-position:-15px -150px}.x-tool-right{background-position:0 -165px}.x-tool-right-over{background-position:-15px -165px}.x-tool-left{background-position:0 -180px}.x-tool-left-over{background-position:-15px -180px}.x-tool-down{background-position:0 -195px}.x-tool-down-over{background-position:-15px -195px}.x-tool-up{background-position:0 -210px}.x-tool-up-over{background-position:-15px -210px}.x-tool-refresh{background-position:0 -225px}.x-tool-refresh-over{background-position:-15px -225px}.x-tool-plus{background-position:0 -240px}.x-tool-plus-over{background-position:-15px -240px}.x-tool-minus{background-position:0 -255px}.x-tool-minus-over{background-position:-15px -255px}.x-tool-search{background-position:0 -270px}.x-tool-search-over{background-position:-15px -270px}.x-tool-save{background-position:0 -285px}.x-tool-save-over{background-position:-15px -285px}.x-tool-help{background-position:0 -300px}.x-tool-help-over{background-position:-15px -300px}.x-tool-print{background-position:0 -315px}.x-tool-print-over{background-position:-15px -315px}.x-tool-expand{background-position:0 -330px}.x-tool-expand-over{background-position:-15px -330px}.x-tool-collapse{background-position:0 -345px}.x-tool-collapse-over{background-position:-15px -345px}.x-tool-resize{background-position:0 -360px}.x-tool-resize-over{background-position:-15px -360px}.x-tool-move{background-position:0 -375px}.x-tool-move-over{background-position:-15px -375px}.x-panel-ghost{z-index:12000;overflow:hidden;position:absolute;left:0;top:0;opacity:.65;filter:alpha(opacity=65)}.x-panel-ghost ul{margin:0;padding:0;overflow:hidden;font-size:0;line-height:0;border:1px solid;border-top:0 none;display:block}.x-panel-ghost *{cursor:move!important}.x-panel-dd-spacer{border:2px dashed}.x-panel-btns{padding:5px;overflow:hidden}.x-panel-btns td.x-toolbar-cell{padding:3px}.x-panel-btns .x-btn-focus .x-btn-left{background-position:0 -147px}.x-panel-btns .x-btn-focus .x-btn-right{background-position:0 -168px}.x-panel-btns .x-btn-focus .x-btn-center{background-position:0 -189px}.x-panel-btns .x-btn-over .x-btn-left{background-position:0 -63px}.x-panel-btns .x-btn-over .x-btn-right{background-position:0 -84px}.x-panel-btns .x-btn-over .x-btn-center{background-position:0 -105px}.x-panel-btns .x-btn-click .x-btn-center{background-position:0 -126px}.x-panel-btns .x-btn-click .x-btn-right{background-position:0 -84px}.x-panel-btns .x-btn-click .x-btn-left{background-position:0 -63px}.x-panel-fbar td,.x-panel-fbar span,.x-panel-fbar input,.x-panel-fbar div,.x-panel-fbar select,.x-panel-fbar label{white-space:nowrap}.x-panel-reset .x-panel-body html,.x-panel-reset .x-panel-body address,.x-panel-reset .x-panel-body blockquote,.x-panel-reset .x-panel-body body,.x-panel-reset .x-panel-body dd,.x-panel-reset .x-panel-body div,.x-panel-reset .x-panel-body dl,.x-panel-reset .x-panel-body dt,.x-panel-reset .x-panel-body fieldset,.x-panel-reset .x-panel-body form,.x-panel-reset .x-panel-body frame,frameset,.x-panel-reset .x-panel-body h1,.x-panel-reset .x-panel-body h2,.x-panel-reset .x-panel-body h3,.x-panel-reset .x-panel-body h4,.x-panel-reset .x-panel-body h5,.x-panel-reset .x-panel-body h6,.x-panel-reset .x-panel-body noframes,.x-panel-reset .x-panel-body ol,.x-panel-reset .x-panel-body p,.x-panel-reset .x-panel-body ul,.x-panel-reset .x-panel-body center,.x-panel-reset .x-panel-body dir,.x-panel-reset .x-panel-body hr,.x-panel-reset .x-panel-body menu,.x-panel-reset .x-panel-body pre{display:block}.x-panel-reset .x-panel-body li{display:list-item}.x-panel-reset .x-panel-body head{display:none}.x-panel-reset .x-panel-body table{display:table}.x-panel-reset .x-panel-body tr{display:table-row}.x-panel-reset .x-panel-body thead{display:table-header-group}.x-panel-reset .x-panel-body tbody{display:table-row-group}.x-panel-reset .x-panel-body tfoot{display:table-footer-group}.x-panel-reset .x-panel-body col{display:table-column}.x-panel-reset .x-panel-body colgroup{display:table-column-group}.x-panel-reset .x-panel-body td,.x-panel-reset .x-panel-body th{display:table-cell}.x-panel-reset .x-panel-body caption{display:table-caption}.x-panel-reset .x-panel-body th{font-weight:bolder;text-align:center}.x-panel-reset .x-panel-body caption{text-align:center}.x-panel-reset .x-panel-body body{margin:8px}.x-panel-reset .x-panel-body h1{font-size:2em;margin:.67em 0}.x-panel-reset .x-panel-body h2{font-size:1.5em;margin:.75em 0}.x-panel-reset .x-panel-body h3{font-size:1.17em;margin:.83em 0}.x-panel-reset .x-panel-body h4,.x-panel-reset .x-panel-body p,.x-panel-reset .x-panel-body blockquote,.x-panel-reset .x-panel-body ul,.x-panel-reset .x-panel-body fieldset,.x-panel-reset .x-panel-body form,.x-panel-reset .x-panel-body ol,.x-panel-reset .x-panel-body dl,.x-panel-reset .x-panel-body dir,.x-panel-reset .x-panel-body menu{margin:1.12em 0}.x-panel-reset .x-panel-body h5{font-size:.83em;margin:1.5em 0}.x-panel-reset .x-panel-body h6{font-size:.75em;margin:1.67em 0}.x-panel-reset .x-panel-body h1,.x-panel-reset .x-panel-body h2,.x-panel-reset .x-panel-body h3,.x-panel-reset .x-panel-body h4,.x-panel-reset .x-panel-body h5,.x-panel-reset .x-panel-body h6,.x-panel-reset .x-panel-body b,.x-panel-reset .x-panel-body strong{font-weight:bolder}.x-panel-reset .x-panel-body blockquote{margin-left:40px;margin-right:40px}.x-panel-reset .x-panel-body i,.x-panel-reset .x-panel-body cite,.x-panel-reset .x-panel-body em,.x-panel-reset .x-panel-body var,.x-panel-reset .x-panel-body address{font-style:italic}.x-panel-reset .x-panel-body pre,.x-panel-reset .x-panel-body tt,.x-panel-reset .x-panel-body code,.x-panel-reset .x-panel-body kbd,.x-panel-reset .x-panel-body samp{font-family:monospace}.x-panel-reset .x-panel-body pre{white-space:pre}.x-panel-reset .x-panel-body button,.x-panel-reset .x-panel-body textarea,.x-panel-reset .x-panel-body input,.x-panel-reset .x-panel-body select{display:inline-block}.x-panel-reset .x-panel-body big{font-size:1.17em}.x-panel-reset .x-panel-body small,.x-panel-reset .x-panel-body sub,.x-panel-reset .x-panel-body sup{font-size:.83em}.x-panel-reset .x-panel-body sub{vertical-align:sub}.x-panel-reset .x-panel-body sup{vertical-align:super}.x-panel-reset .x-panel-body table{border-spacing:2px}.x-panel-reset .x-panel-body thead,.x-panel-reset .x-panel-body tbody,.x-panel-reset .x-panel-body tfoot{vertical-align:middle}.x-panel-reset .x-panel-body td,.x-panel-reset .x-panel-body th{vertical-align:inherit}.x-panel-reset .x-panel-body s,.x-panel-reset .x-panel-body strike,.x-panel-reset .x-panel-body del{text-decoration:line-through}.x-panel-reset .x-panel-body hr{border:1px inset}.x-panel-reset .x-panel-body ol,.x-panel-reset .x-panel-body ul,.x-panel-reset .x-panel-body dir,.x-panel-reset .x-panel-body menu,.x-panel-reset .x-panel-body dd{margin-left:40px}.x-panel-reset .x-panel-body ul,.x-panel-reset .x-panel-body menu,.x-panel-reset .x-panel-body dir{list-style-type:disc}.x-panel-reset .x-panel-body ol{list-style-type:decimal}.x-panel-reset .x-panel-body ol ul,.x-panel-reset .x-panel-body ul ol,.x-panel-reset .x-panel-body ul ul,.x-panel-reset .x-panel-body ol ol{margin-top:0;margin-bottom:0}.x-panel-reset .x-panel-body u,.x-panel-reset .x-panel-body ins{text-decoration:underline}.x-panel-reset .x-panel-body br:before{content:"\A"}.x-panel-reset .x-panel-body :before,.x-panel-reset .x-panel-body:after{white-space:pre-line}.x-panel-reset .x-panel-body center{text-align:center}.x-panel-reset .x-panel-body :link,.x-panel-reset .x-panel-body:visited{text-decoration:underline}.x-panel-reset .x-panel-body :focus{outline:invert dotted thin}.x-panel-reset .x-panel-body BDO[DIR="ltr"]{direction:ltr;unicode-bidi:bidi-override}.x-panel-reset .x-panel-body BDO[DIR="rtl"]{direction:rtl;unicode-bidi:bidi-override}.x-window .x-window-handle{opacity:0;filter:alpha(opacity=0)}.x-window-proxy{border:1px solid;z-index:12000;overflow:hidden;position:absolute;left:0;top:0;display:none;opacity:.5;filter:alpha(opacity=50)}.x-window-header{overflow:hidden}.x-window-bwrap{z-index:1;position:relative;left:0;top:0}.x-window-tl .x-window-header{padding:5px 0 4px 0}.x-window-header-text{cursor:pointer}.x-window-tc{background:transparent repeat-x 0 0;overflow:hidden}.x-window-tl{background:transparent no-repeat 0 0;padding-left:6px;z-index:1;position:relative}.x-window-tr{background:transparent no-repeat right 0;padding-right:6px}.x-window-bc{background:transparent repeat-x 0 bottom}.x-window-bc .x-window-footer{padding-bottom:6px;font-size:0;line-height:0}.x-window-bl{background:transparent no-repeat 0 bottom;padding-left:6px}.x-window-br{background:transparent no-repeat right bottom;padding-right:6px}.x-window-mc{border:1px solid;padding:0;margin:0}.x-window-ml{background:transparent repeat-y 0 0;padding-left:6px}.x-window-mr{background:transparent repeat-y right 0;padding-right:6px}.x-window-body{overflow:hidden}.x-window-bwrap{overflow:hidden}.x-window-maximized .x-window-bl,.x-window-maximized .x-window-br,.x-window-maximized .x-window-ml,.x-window-maximized .x-window-mr,.x-window-maximized .x-window-tl,.x-window-maximized .x-window-tr{padding:0}.x-window-maximized .x-window-footer{padding-bottom:0}.x-window-maximized .x-window-tc{padding-left:3px;padding-right:3px}.x-window-maximized .x-window-mc{border-left:0 none;border-right:0 none}.x-window-tbar .x-toolbar,.x-window-bbar .x-toolbar{border-left:0 none;border-right:0 none}.x-window-bbar .x-toolbar{border-top:1px solid;border-bottom:0 none}.x-window-draggable,.x-window-draggable .x-window-header-text{cursor:move}.x-window-maximized .x-window-draggable,.x-window-maximized .x-window-draggable .x-window-header-text{cursor:default}.x-window-body{background-color:transparent}.x-panel-ghost .x-window-tl{border-bottom:1px solid}.x-panel-collapsed .x-window-tl{border-bottom:1px solid}.x-window-maximized-ct{overflow:hidden}.x-window-maximized .x-window-handle{display:none}.x-window-sizing-ghost ul{border:0 none!important}.x-dlg-focus{outline:0 none;width:0;height:0;overflow:hidden;position:absolute;top:0;left:0}.ext-webkit .x-dlg-focus{width:1px;height:1px}.x-dlg-mask{z-index:10000;display:none;position:absolute;top:0;left:0;opacity:.50;filter:alpha(opacity=50)}body.ext-ie6.x-body-masked select{visibility:hidden}body.ext-ie6.x-body-masked .x-window select{visibility:visible}.x-window-plain .x-window-mc{border:1px solid}.x-window-plain .x-window-body{border:1px solid;background:transparent!important}.x-html-editor-wrap{border:1px solid}.x-html-editor-tb .x-btn-text{background:transparent no-repeat}.x-html-editor-tb .x-edit-bold,.x-menu-item img.x-edit-bold{background-position:0 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-italic,.x-menu-item img.x-edit-italic{background-position:-16px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-underline,.x-menu-item img.x-edit-underline{background-position:-32px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-forecolor,.x-menu-item img.x-edit-forecolor{background-position:-160px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-backcolor,.x-menu-item img.x-edit-backcolor{background-position:-176px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-justifyleft,.x-menu-item img.x-edit-justifyleft{background-position:-112px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-justifycenter,.x-menu-item img.x-edit-justifycenter{background-position:-128px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-justifyright,.x-menu-item img.x-edit-justifyright{background-position:-144px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-insertorderedlist,.x-menu-item img.x-edit-insertorderedlist{background-position:-80px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-insertunorderedlist,.x-menu-item img.x-edit-insertunorderedlist{background-position:-96px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-increasefontsize,.x-menu-item img.x-edit-increasefontsize{background-position:-48px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-decreasefontsize,.x-menu-item img.x-edit-decreasefontsize{background-position:-64px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-sourceedit,.x-menu-item img.x-edit-sourceedit{background-position:-192px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tb .x-edit-createlink,.x-menu-item img.x-edit-createlink{background-position:-208px 0;background-image:url(../images/default/editor/tb-sprite.gif)}.x-html-editor-tip .x-tip-bd .x-tip-bd-inner{padding:5px;padding-bottom:1px}.x-html-editor-tb .x-toolbar{position:static!important}.x-panel-noborder .x-panel-body-noborder{border-width:0}.x-panel-noborder .x-panel-header-noborder{border-width:0 0 1px;border-style:solid}.x-panel-noborder .x-panel-tbar-noborder .x-toolbar{border-width:0 0 1px;border-style:solid}.x-panel-noborder .x-panel-bbar-noborder .x-toolbar{border-width:1px 0 0 0;border-style:solid}.x-window-noborder .x-window-mc{border-width:0}.x-window-plain .x-window-body-noborder{border-width:0}.x-tab-panel-noborder .x-tab-panel-body-noborder{border-width:0}.x-tab-panel-noborder .x-tab-panel-header-noborder{border-width:0 0 1px 0}.x-tab-panel-noborder .x-tab-panel-footer-noborder{border-width:1px 0 0 0}.x-tab-panel-bbar-noborder .x-toolbar{border-width:1px 0 0 0;border-style:solid}.x-tab-panel-tbar-noborder .x-toolbar{border-width:0 0 1px;border-style:solid}.x-border-layout-ct{position:relative}.x-border-panel{position:absolute;left:0;top:0}.x-tool-collapse-south{background-position:0 -195px}.x-tool-collapse-south-over{background-position:-15px -195px}.x-tool-collapse-north{background-position:0 -210px}.x-tool-collapse-north-over{background-position:-15px -210px}.x-tool-collapse-west{background-position:0 -180px}.x-tool-collapse-west-over{background-position:-15px -180px}.x-tool-collapse-east{background-position:0 -165px}.x-tool-collapse-east-over{background-position:-15px -165px}.x-tool-expand-south{background-position:0 -210px}.x-tool-expand-south-over{background-position:-15px -210px}.x-tool-expand-north{background-position:0 -195px}.x-tool-expand-north-over{background-position:-15px -195px}.x-tool-expand-west{background-position:0 -165px}.x-tool-expand-west-over{background-position:-15px -165px}.x-tool-expand-east{background-position:0 -180px}.x-tool-expand-east-over{background-position:-15px -180px}.x-tool-expand-north,.x-tool-expand-south{float:right;margin:3px}.x-tool-expand-east,.x-tool-expand-west{float:none;margin:3px 2px}.x-accordion-hd .x-tool-toggle{background-position:0 -255px}.x-accordion-hd .x-tool-toggle-over{background-position:-15px -255px}.x-panel-collapsed .x-accordion-hd .x-tool-toggle{background-position:0 -240px}.x-panel-collapsed .x-accordion-hd .x-tool-toggle-over{background-position:-15px -240px}.x-accordion-hd{padding-top:4px;padding-bottom:3px;border-top:0 none;background:transparent repeat-x 0 -9px}.x-layout-collapsed{position:absolute;left:-10000px;top:-10000px;visibility:hidden;width:20px;height:20px;overflow:hidden;border:1px solid;z-index:20}.ext-border-box .x-layout-collapsed{width:22px;height:22px}.x-layout-collapsed-over{cursor:pointer}.x-layout-collapsed-west .x-layout-collapsed-tools,.x-layout-collapsed-east .x-layout-collapsed-tools{position:absolute;top:0;left:0;width:20px;height:20px}.x-layout-split{position:absolute;height:5px;width:5px;line-height:1px;font-size:1px;z-index:3;background-color:transparent}.ext-strict .ext-ie6 .x-layout-split{background-color:#fff!important;filter:alpha(opacity=1)}.x-layout-split-h{background-image:url(../images/default/s.gif);background-position:left}.x-layout-split-v{background-image:url(../images/default/s.gif);background-position:top}.x-column-layout-ct{overflow:hidden}.x-column{float:left;padding:0;margin:0;overflow:hidden}.x-column-inner{overflow:hidden}.x-layout-mini{position:absolute;top:0;left:0;display:block;width:5px;height:35px;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}.x-layout-mini-over,.x-layout-collapsed-over .x-layout-mini{opacity:1;filter:none}.x-layout-split-west .x-layout-mini{top:48%}.x-layout-split-east .x-layout-mini{top:48%}.x-layout-split-north .x-layout-mini{left:48%;height:5px;width:35px}.x-layout-split-south .x-layout-mini{left:48%;height:5px;width:35px}.x-layout-cmini-west .x-layout-mini{top:48%}.x-layout-cmini-east .x-layout-mini{top:48%}.x-layout-cmini-north .x-layout-mini{left:48%;height:5px;width:35px}.x-layout-cmini-south .x-layout-mini{left:48%;height:5px;width:35px}.x-layout-cmini-west,.x-layout-cmini-east{border:0 none;width:5px!important;padding:0;background-color:transparent}.x-layout-cmini-north,.x-layout-cmini-south{border:0 none;height:5px!important;padding:0;background-color:transparent}.x-viewport,.x-viewport body{margin:0;padding:0;border:0 none;overflow:hidden;height:100%}.x-abs-layout-item{position:absolute;left:0;top:0}.ext-ie input.x-abs-layout-item,.ext-ie textarea.x-abs-layout-item{margin:0}.x-box-layout-ct{overflow:hidden}.x-box-inner{overflow:hidden;position:relative;left:0;top:0}.x-box-item{position:absolute;left:0;top:0}.x-progress-wrap{border:1px solid;overflow:hidden}.x-progress-inner{height:18px;background:repeat-x;position:relative}.x-progress-bar{height:18px;float:left;width:0;background:repeat-x left center;border-top:1px solid;border-bottom:1px solid;border-right:1px solid}.x-progress-text{padding:1px 5px;overflow:hidden;position:absolute;left:0;text-align:center}.x-progress-text-back{line-height:16px}.ext-ie .x-progress-text-back{line-height:15px}.ext-strict .ext-ie7 .x-progress-text-back{width:100%}.x-list-header{background:repeat-x 0 bottom;cursor:default;height:22px}.x-list-header-inner div{display:block;float:left;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}.x-list-header-inner div em{display:block;border-left:1px solid;padding:4px 4px;overflow:hidden;-moz-user-select:none;-khtml-user-select:none;line-height:14px}.x-list-body{overflow:auto;overflow-x:hidden;overflow-y:auto;float:left;width:100%}.x-list-body dt{display:block;float:left;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.x-list-body dt em{display:block;padding:3px 4px;overflow:hidden;-moz-user-select:none;-khtml-user-select:none}.x-list-resizer{border-left:1px solid;border-right:1px solid;position:absolute;left:0;top:0}.x-list-header-inner em.sort-asc{background:transparent no-repeat center 0;border-style:solid;border-width:0 1px 1px;padding-bottom:3px}.x-list-header-inner em.sort-desc{background:transparent no-repeat center -23px;border-style:solid;border-width:0 1px 1px;padding-bottom:3px}.x-slider-inner{position:relative;left:0;top:0;overflow:visible}.x-slider-focus{position:absolute;left:0;top:0;width:1px;height:1px;line-height:1px;font-size:1px;outline:0 none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:ignore;display:block;overflow:hidden}.x-slider-horz{padding-left:7px;background:transparent no-repeat 0 -22px}.x-slider-horz .x-slider-end{padding-right:7px;background:transparent no-repeat right -44px}.x-slider-horz .x-slider-inner{background:transparent repeat-x 0 0;height:22px}.x-slider-horz .x-slider-thumb{width:14px;height:15px;position:absolute;left:0;top:3px;background:transparent no-repeat 0 0}.x-slider-horz .x-slider-thumb-over{background-position:-14px -15px}.x-slider-horz .x-slider-thumb-drag{background-position:-28px -30px}.x-slider-vert{padding-top:7px;background:transparent no-repeat -44px 0;width:22px}.x-slider-vert .x-slider-end{padding-bottom:7px;background:transparent no-repeat -22px bottom}.x-slider-vert .x-slider-inner{background:transparent repeat-y 0 0}.x-slider-vert .x-slider-thumb{width:15px;height:14px;position:absolute;left:3px;bottom:0;background:transparent no-repeat 0 0}.x-slider-vert .x-slider-thumb-over{background-position:-15px -14px}.x-slider-vert .x-slider-thumb-drag{background-position:-30px -28px}.x-window-dlg .x-window-body{border:0 none!important;padding:5px 10px;overflow:hidden!important}.x-window-dlg .x-window-mc{border:0 none!important}.x-window-dlg .ext-mb-input{margin-top:4px;width:95%}.x-window-dlg .ext-mb-textarea{margin-top:4px}.x-window-dlg .x-progress-wrap{margin-top:4px}.ext-ie .x-window-dlg .x-progress-wrap{margin-top:6px}.x-window-dlg .x-msg-box-wait{background:transparent no-repeat left;display:block;width:300px;padding-left:18px;line-height:18px}.x-window-dlg .ext-mb-icon{float:left;width:47px;height:32px}.x-window-dlg .x-dlg-icon .ext-mb-content{margin-left:47px}.x-window-dlg .ext-mb-info,.x-window-dlg .ext-mb-warning,.x-window-dlg .ext-mb-question,.x-window-dlg .ext-mb-error{background:transparent no-repeat top left}.ext-gecko2 .ext-mb-fix-cursor{overflow:auto} + + \ No newline at end of file From 60801bb1a83b06957fcbe3512037f915ebc0296a Mon Sep 17 00:00:00 2001 From: Vuolter Date: Mon, 7 Jan 2013 17:30:54 +0100 Subject: [PATCH 211/503] Theme forgotten --- src/webui/extjs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index ce8f6ebc..7291e66b 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -101,7 +101,8 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) "\n"); } - htsbuf_qprintf(hq, "\n" + htsbuf_qprintf(hq, "\n" + "\n" "\n"); extjs_exec(hq, "Ext.BLANK_IMAGE_URL = " From 22eaf61c40b2a4b857701c51c15b1627bc2a89de Mon Sep 17 00:00:00 2001 From: bowman-gh Date: Tue, 8 Jan 2013 12:13:49 +0100 Subject: [PATCH 212/503] Fill out the private descriptor fields correctly (tsid and onid) --- src/capmt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/capmt.c diff --git a/src/capmt.c b/src/capmt.c old mode 100644 new mode 100755 index cac49c14..ea306ecd --- a/src/capmt.c +++ b/src/capmt.c @@ -719,7 +719,9 @@ capmt_table_input(struct th_descrambler *td, struct service *t, uint16_t sid = t->s_dvb_service_id; uint16_t ecmpid = st->es_pid; - uint16_t transponder = 0; + uint16_t transponder = t->s_dvb_mux_instance->tdmi_transport_stream_id; + uint16_t onid = t->s_dvb_mux_instance->tdmi_network_id; + /* don't do too much requests */ if (current_caid == total_caids && caid != ct->ct_caid_last) @@ -757,9 +759,9 @@ capmt_table_input(struct th_descrambler *td, struct service *t, capmt_descriptor_t prd = { .cad_type = CAPMT_DESC_PRIVATE, .cad_length = 0x08, - .cad_data = { 0x00, 0x00, 0x00, 0x00, - sid >> 8, sid & 0xFF, - transponder >> 8, transponder & 0xFF + .cad_data = { 0x00, 0x00, 0x00, 0x00, // enigma namespace goes here + transponder >> 8, transponder & 0xFF, + onid >> 8, onid & 0xFF, }}; memcpy(&buf[pos], &prd, prd.cad_length + 2); pos += prd.cad_length + 2; From ea9f91e6abbeaf055f8fb04cde1aa903e46879e4 Mon Sep 17 00:00:00 2001 From: bowman-gh Date: Tue, 8 Jan 2013 12:56:04 +0100 Subject: [PATCH 213/503] Fill out the private descriptor fields correctly (tsid and onid) --- src/capmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/capmt.c b/src/capmt.c index ea306ecd..7f88377d 100755 --- a/src/capmt.c +++ b/src/capmt.c @@ -761,7 +761,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, .cad_length = 0x08, .cad_data = { 0x00, 0x00, 0x00, 0x00, // enigma namespace goes here transponder >> 8, transponder & 0xFF, - onid >> 8, onid & 0xFF, + onid >> 8, onid & 0xFF }}; memcpy(&buf[pos], &prd, prd.cad_length + 2); pos += prd.cad_length + 2; From ddcee0e750a6564adec9c95b66a72fad7a070dc3 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 8 Jan 2013 13:30:11 +0000 Subject: [PATCH 214/503] [PR-210] some tidying up of the webui debug option. --- src/webui/extjs.c | 35 ++++++++++------------------------- src/webui/webui.c | 3 +++ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 7291e66b..f1bd1e47 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -86,34 +86,19 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) htsbuf_queue_t *hq = &hc->hc_reply; #define EXTJSPATH "static/extjs" - htsbuf_qprintf(hq, "\n"); - if(webui_debug) { - htsbuf_qprintf(hq, "\n" - "\n" - "\n"); - tvhlog(LOG_INFO, "webui", "Running web interface in debug mode"); - } - else { - htsbuf_qprintf(hq, "\n" - "\n" - "\n"); - } - - htsbuf_qprintf(hq, "\n" + htsbuf_qprintf(hq, "\n" + "\n" + "\n" + "\n" "\n" - "\n"); - - extjs_exec(hq, "Ext.BLANK_IMAGE_URL = " - "'"EXTJSPATH"/resources/images/default/s.gif';"); - -#if 0 - htsbuf_qprintf(hq, - ""); -#endif - + "\n", + webui_debug ? "-debug" : "", + webui_debug ? "-debug" : "", + webui_debug ? "" : "-min"); + + extjs_exec(hq, "Ext.BLANK_IMAGE_URL = " "'"EXTJSPATH"/resources/images/default/s.gif';"); /** * Load extjs extensions diff --git a/src/webui/webui.c b/src/webui/webui.c index 69ccadf0..90ffc6f4 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -939,6 +939,9 @@ int page_statedump(http_connection_t *hc, const char *remain, void *opaque); void webui_init(void) { + if (webui_debug) + tvhlog(LOG_INFO, "webui", "Running web interface in debug mode"); + http_path_add("", NULL, page_root2, ACCESS_WEB_INTERFACE); http_path_add("/", NULL, page_root, ACCESS_WEB_INTERFACE); From 93fe5b9f7259e0c261fe6b9f38ba6c8e67626806 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 8 Jan 2013 13:34:14 +0000 Subject: [PATCH 215/503] channels: do not delete channel when last refd service is removed. --- src/service.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/service.c b/src/service.c index c745527a..b0ad8fc2 100644 --- a/src/service.c +++ b/src/service.c @@ -463,7 +463,6 @@ service_destroy(service_t *t) { elementary_stream_t *st; th_subscription_t *s; - channel_t *ch = t->s_ch; if(t->s_dtor != NULL) t->s_dtor(t); @@ -506,11 +505,6 @@ service_destroy(service_t *t) avgstat_flush(&t->s_rate); service_unref(t); - - if(ch != NULL) { - if(LIST_FIRST(&ch->ch_services) == NULL) - channel_delete(ch); - } } From 2c85228c5d22c3f7d3a6c3e32a69e2172c8a0a94 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 8 Jan 2013 16:22:53 +0000 Subject: [PATCH 216/503] main: rewrite of command line processing. --- debian/tvheadend.default | 6 + debian/tvheadend.init | 5 +- debian/tvheadend.upstart | 5 +- src/avahi.c | 4 +- src/htsp_server.c | 10 +- src/http.c | 2 +- src/main.c | 473 +++++++++++++++++++++++---------------- src/tvheadend.h | 6 +- src/webui/extjs.c | 6 +- src/webui/webui.c | 2 +- 10 files changed, 310 insertions(+), 209 deletions(-) diff --git a/debian/tvheadend.default b/debian/tvheadend.default index a8d7c48a..4d919538 100644 --- a/debian/tvheadend.default +++ b/debian/tvheadend.default @@ -29,6 +29,12 @@ TVH_ADAPTERS="" # if set to "" will use binary default TVH_HTTP_PORT="" +# TVH_HTTP_ROOT +# if set to "" will use binary default +# else will change the webui root context, useful for proxied +# servers +TVH_HTTP_ROOT="" + # TVH_HTSP_PORT # if set to "" will use binary default TVH_HTSP_PORT="" diff --git a/debian/tvheadend.init b/debian/tvheadend.init index 8a24bdd8..9e3bcb97 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -33,8 +33,9 @@ ARGS="-f" [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" -[ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS -w $TVH_HTTP_PORT" -[ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS -e $TVH_HTSP_PORT" +[ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" +[ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" +[ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" # Load the VERBOSE setting and other rcS variables diff --git a/debian/tvheadend.upstart b/debian/tvheadend.upstart index 37b76b85..5b8b6512 100644 --- a/debian/tvheadend.upstart +++ b/debian/tvheadend.upstart @@ -22,8 +22,9 @@ script [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" - [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS -w $TVH_HTTP_PORT" - [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS -e $TVH_HTSP_PORT" + [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" + [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" + [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" exec tvheadend $ARGS $TVH_ARGS diff --git a/src/avahi.c b/src/avahi.c index af66db26..76ec45c8 100644 --- a/src/avahi.c +++ b/src/avahi.c @@ -133,7 +133,7 @@ create_services(AvahiClient *c) /* Add the service for HTSP */ if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, - "_htsp._tcp", NULL, NULL,htsp_port, + "_htsp._tcp", NULL, NULL,tvheadend_htsp_port, NULL)) < 0) { if (ret == AVAHI_ERR_COLLISION) @@ -149,7 +149,7 @@ create_services(AvahiClient *c) /* Add the service for HTTP */ if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, - "_http._tcp", NULL, NULL, webui_port, + "_http._tcp", NULL, NULL, tvheadend_webui_port, "path=/", NULL)) < 0) { diff --git a/src/htsp_server.c b/src/htsp_server.c index 2a4b6af7..9ce8c7ff 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -466,7 +466,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) inet_ntop(AF_INET, &addr.sin_addr, url+p, sizeof(url)-p); p = strlen(url); p += snprintf(url+p, sizeof(url)-p, ":%hd%s", - webui_port, + tvheadend_webui_port, tvheadend_webroot ?: ""); } snprintf(url+p, sizeof(url)-p, "/imagecache/%d", id); @@ -1860,10 +1860,10 @@ htsp_serve(int fd, void *opaque, struct sockaddr_in *source, void htsp_init(void) { - extern int htsp_port_extra; - htsp_server = tcp_server_create(htsp_port, htsp_serve, NULL); - if(htsp_port_extra) - htsp_server_2 = tcp_server_create(htsp_port_extra, htsp_serve, NULL); + extern int tvheadend_htsp_port_extra; + htsp_server = tcp_server_create(tvheadend_htsp_port, htsp_serve, NULL); + if(tvheadend_htsp_port_extra) + htsp_server_2 = tcp_server_create(tvheadend_htsp_port_extra, htsp_serve, NULL); } /* ************************************************************************** diff --git a/src/http.c b/src/http.c index a0135551..610e72e3 100644 --- a/src/http.c +++ b/src/http.c @@ -814,5 +814,5 @@ http_serve(int fd, void *opaque, struct sockaddr_in *peer, void http_server_init(void) { - http_server = tcp_server_create(webui_port, http_serve, NULL); + http_server = tcp_server_create(tvheadend_webui_port, http_serve, NULL); } diff --git a/src/main.c b/src/main.c index 6e6a7189..252a5ca4 100644 --- a/src/main.c +++ b/src/main.c @@ -65,24 +65,56 @@ #include "libav.h" #endif -int running; -time_t dispatch_clock; -static LIST_HEAD(, gtimer) gtimers; -pthread_mutex_t global_lock; -pthread_mutex_t ffmpeg_lock; -pthread_mutex_t fork_lock; -static int log_stderr; -static int log_decorate; +/* Command line option struct */ +typedef struct { + const char sopt; + const char *lopt; + const char *desc; + enum { + OPT_STR, + OPT_INT, + OPT_BOOL + } type; + void *param; +} cmdline_opt_t; -int log_debug_to_syslog; -int log_debug_to_console; +static cmdline_opt_t* cmdline_opt_find + ( cmdline_opt_t *opts, int num, const char *arg ) +{ + int i; + int isshort = 0; -int webui_port; -int webui_debug; -int htsp_port; -int htsp_port_extra; -const char *tvheadend_cwd; -const char *tvheadend_webroot; + if (strlen(arg) < 2 || *arg != '-') + return NULL; + arg++; + + if (strlen(arg) == 1) + isshort = 1; + else if (*arg == '-') + arg++; + else + return NULL; + + for (i = 0; i < num; i++) { + if (!opts[i].lopt) continue; + if (isshort && opts[i].sopt == *arg) + return &opts[i]; + if (!isshort && !strcmp(opts[i].lopt, arg)) + return &opts[i]; + } + + return NULL; +} + +/* + * Globals + */ +int tvheadend_webui_port; +int tvheadend_webui_debug; +int tvheadend_htsp_port; +int tvheadend_htsp_port_extra; +const char *tvheadend_cwd; +const char *tvheadend_webroot; const tvh_caps_t tvheadend_capabilities[] = { #if ENABLE_CWC { "cwc", NULL }, @@ -99,6 +131,21 @@ const tvh_caps_t tvheadend_capabilities[] = { { NULL, NULL } }; +time_t dispatch_clock; +pthread_mutex_t global_lock; +pthread_mutex_t ffmpeg_lock; +pthread_mutex_t fork_lock; + +/* + * Locals + */ +static int running; +static int log_stderr; +static int log_decorate; +static LIST_HEAD(, gtimer) gtimers; +static int log_debug_to_syslog; +static int log_debug_to_console; + static void handle_sigpipe(int x) { @@ -184,49 +231,61 @@ gtimer_disarm(gtimer_t *gti) } } +/** + * Show version info + */ +static void +show_version(const char *argv0) +{ + printf("%s: version %s\n", argv0, tvheadend_version); + exit(0); +} + /** * */ static void -usage(const char *argv0) +show_usage + (const char *argv0, cmdline_opt_t *opts, int num, const char *err, ...) { - printf("HTS Tvheadend %s\n", tvheadend_version); - printf("usage: %s [options]\n", argv0); - printf("\n"); - printf(" -a Use only DVB adapters specified (csv)\n"); - printf(" -c Alternate configuration path.\n" - " Defaults to [$HOME/.hts/tvheadend]\n"); - printf(" -m Alternate mux configuration directory\n"); - printf(" -f Fork and daemonize\n"); - printf(" -p Write pid to instead of /var/run/tvheadend.pid,\n" - " only works with -f\n"); - printf(" -u Run as user , only works with -f\n"); - printf(" -g Run as group , only works with -f\n"); - printf(" -C If no useraccount exist then create one with\n" - " no username and no password. Use with care as\n" - " it will allow world-wide administrative access\n" - " to your Tvheadend installation until you edit\n" - " the access-control from within the Tvheadend UI\n"); - printf(" -w Web interface access port [default 9981]\n"); - printf(" -e HTSP access port [default 9982]\n"); - printf(" -W Web interface context path [default /]\n"); - printf("\n"); - printf("Development options\n"); - printf("\n"); - printf(" -d Log debug to console\n"); - printf(" -s Log debug to syslog\n"); - printf(" -x Run web interface in debug mode\n"); - printf(" -j Statically join the given transport id\n"); - printf(" -r Read the given transport stream file and present\n" - " found services as channels\n"); - printf(" -A Immediately call abort()\n"); - + int i; + char buf[256]; + printf("Usage :- %s [options]\n\n", argv0); + printf("Options\n"); + for (i = 0; i < num; i++) { + + /* Section */ + if (!opts[i].lopt) { + printf("\n%s\n\n", + opts[i].desc); + + /* Option */ + } else { + char sopt[4]; + char *desc, *tok; + if (opts[i].sopt) + snprintf(sopt, sizeof(sopt), "-%c/", opts[i].sopt); + else + sopt[0] = 0; + snprintf(buf, sizeof(buf), " %s--%s", sopt, opts[i].lopt); + desc = strdup(opts[i].desc); + tok = strtok(desc, "\n"); + while (tok) { + printf("%s\t\t%s\n", buf, tok); + tok = buf; + while (*tok) { + *tok = ' '; + tok++; + } + tok = strtok(NULL, "\n"); + } + } + } printf("\n"); printf("For more information read the man page or visit\n"); printf(" http://www.lonelycoder.com/hts/\n"); printf("\n"); exit(0); - } @@ -272,32 +331,87 @@ mainloop(void) int main(int argc, char **argv) { - int c; - int forkaway = 0; - FILE *pidfile; - const char *pidpath = "/var/run/tvheadend.pid"; - struct group *grp; - struct passwd *pw; - char *webroot; - const char *usernam = NULL; - const char *groupnam = NULL; - int logfacility = LOG_DAEMON; - int createdefault = 0; + int i; sigset_t set; - const char *homedir; - const char *rawts_input = NULL; #if ENABLE_LINUXDVB - const char *dvb_rawts_input = NULL; + uint32_t adapter_mask; #endif - const char *join_transport = NULL; - const char *confpath = NULL; - char *p, *endp; - uint32_t adapter_mask = 0xffffffff; - int crash = 0; - webui_port = 9981; - htsp_port = 9982; - gid_t gid; - uid_t uid; + + /* Defaults */ + log_stderr = 1; + log_decorate = isatty(2); + log_debug_to_syslog = 0; + log_debug_to_console = 0; + tvheadend_webui_port = 9981; + tvheadend_webroot = NULL; + tvheadend_htsp_port = 9992; + tvheadend_htsp_port_extra = 0; + + /* Command line options */ + int opt_help = 0, + opt_version = 0, + opt_fork = 0, + opt_firstrun = 0, + opt_debug = 0, + opt_syslog = 0, + opt_uidebug = 0, + opt_abort = 0; + const char *opt_config = NULL, + *opt_user = NULL, + *opt_group = NULL, + *opt_pidpath = "/var/run/tvheadend.pid", +#if ENABLE_LINUXDVB + *opt_dvb_adapters = NULL, + *opt_dvb_raw = NULL, +#endif + *opt_rawts = NULL, + *opt_subscribe = NULL; + cmdline_opt_t cmdline_opts[] = { + { 0, NULL, "Generic Options", OPT_BOOL, NULL }, + { 'h', "help", "Show this page", OPT_BOOL, &opt_help }, + { 'v', "version", "Show version infomation", OPT_BOOL, &opt_version }, + + { 0, NULL, "Service Configuration", OPT_BOOL, NULL }, + { 'c', "config", "Alternate config path", OPT_STR, &opt_config }, + { 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork }, + { 'u', "user", "Run as user", OPT_STR, &opt_user }, + { 'g', "group", "Run as group", OPT_STR, &opt_group }, + { 'p', "pid", "Alternate pid path", OPT_STR, &opt_pidpath }, + { 'C', "firstrun", "If no useraccount exist then create one with\n" + "no username and no password. Use with care as\n" + "it will allow world-wide administrative access\n" + "to your Tvheadend installation until you edit\n" + "the access-control from within the Tvheadend UI", + OPT_BOOL, &opt_firstrun }, +#ifdef ENABLE_LINUXDVB + { 'a', "adapters", "Use only specified DVB adapters", + OPT_STR, &opt_dvb_adapters }, +#endif + + { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, + { 0, "http_port", "Specify alternative http port", + OPT_INT, &tvheadend_webui_port }, + { 0, "http_root", "Specify alternative http webroot", + OPT_STR, &tvheadend_webroot }, + { 0, "htsp_port", "Specify alternative htsp port", + OPT_INT, &tvheadend_htsp_port }, + { 0, "htsp_port2", "Specify extra htsp port", + OPT_INT, &tvheadend_htsp_port_extra }, + + { 0, NULL, "Debug Options", OPT_BOOL, NULL }, + { 'd', "debug", "Enable all debug", OPT_BOOL, &opt_debug }, + { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, + { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, + { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, +#if ENABLE_LINUXDVB + { 'R', "dvbraw", "Use rawts file to create virtual adapter", + OPT_STR, &opt_dvb_raw }, +#endif + { 'r', "rawts", "Use rawts file to generate virtual services", + OPT_STR, &opt_rawts }, + { 'j', "join", "Subscribe to a service permanently", + OPT_STR, &opt_subscribe } + }; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); @@ -305,108 +419,85 @@ main(int argc, char **argv) /* Set locale */ setlocale(LC_ALL, ""); - // make sure the timezone is set + /* make sure the timezone is set */ tzset(); - while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdxr:j:sw:e:E:R:W:")) != -1) { - switch(c) { - case 'a': - adapter_mask = 0x0; - p = strtok(optarg, ","); - if (p != NULL) { - do { - int adapter = strtol(p, &endp, 10); - if (*endp != 0 || adapter < 0 || adapter > 31) { - fprintf(stderr, "Invalid adapter number '%s'\n", p); - return 1; - } - adapter_mask |= (1 << adapter); - } while ((p = strtok(NULL, ",")) != NULL); - if (adapter_mask == 0x0) { - fprintf(stderr, "No adapters specified!\n"); - return 1; - } - } else { - usage(argv[0]); - } - break; - case 'A': - crash = 1; - break; - case 'f': - forkaway = 1; - break; - case 'p': - pidpath = optarg; - break; - case 'w': - webui_port = atoi(optarg); - break; - case 'e': - htsp_port = atoi(optarg); - break; - case 'E': - htsp_port_extra = atoi(optarg); - break; - case 'u': - usernam = optarg; - break; - case 'g': - groupnam = optarg; - break; - case 'c': - confpath = optarg; - break; - case 'd': - log_debug_to_console = 1; - break; - case 's': - log_debug_to_syslog = 1; - break; - case 'x': - webui_debug = 1; - break; - case 'C': - createdefault = 1; - break; - case 'r': - rawts_input = optarg; - break; -#if ENABLE_LINUXDVB - case 'R': - dvb_rawts_input = optarg; - break; -#endif - case 'j': - join_transport = optarg; - break; - case 'W': - webroot = malloc(strlen(optarg) + (*optarg == '/' ? 0 : 1)); - if (*optarg != '/') { - *webroot = '/'; - strcpy(webroot+1, optarg); - } else { - strcpy(webroot, optarg); - } - if (webroot[strlen(webroot)-1] == '/') - webroot[strlen(webroot)-1] = '\0'; - tvheadend_webroot = webroot; - break; - default: - usage(argv[0]); - } + /* Process command line */ + for (i = 1; i < argc; i++) { + + /* Find option */ + cmdline_opt_t *opt + = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]); + if (!opt) + show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), + "invalid option specified [%s]", argv[i]); + + /* Process */ + if (opt->type == OPT_BOOL) + *((int*)opt->param) = 1; + else if (++i == argc) + show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), + "option %s requires a value", opt->lopt); + else if (opt->type == OPT_INT) + *((int*)opt->param) = atoi(argv[i]); + else + *((char**)opt->param) = argv[i]; + + /* Stop processing */ + if (opt_help) + show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL); + if (opt_version) + show_version(argv[0]); } - signal(SIGPIPE, handle_sigpipe); + /* Additional cmdline processing */ + log_debug_to_console = opt_debug; + log_debug_to_syslog = opt_syslog; + tvheadend_webui_debug = opt_debug || opt_uidebug; +#if ENABLE_LINUXDVB + if (!opt_dvb_adapters) { + adapter_mask = ~0; + } else { + char *p, *r, *e; + adapter_mask = 0x0; + p = strtok_r((char*)opt_dvb_adapters, ",", &r); + while (p) { + int a = strtol(p, &e, 10); + if (*e != 0 || a < 0 || a > 31) { + tvhlog(LOG_ERR, "START", "Invalid adapter number '%s'", p); + return 1; + } + adapter_mask |= (1 << a); + p = strtok_r(NULL, ",", &r); + } + if (!adapter_mask) { + tvhlog(LOG_ERR, "START", "No adapters specified!"); + return 1; + } + } +#endif + if (tvheadend_webroot) { + char *tmp; + if (*tvheadend_webroot == '/') + tmp = strdup(tvheadend_webroot); + else { + tmp = malloc(strlen(tvheadend_webroot)+1); + *tmp = '/'; + strcpy(tmp+1, tvheadend_webroot); + } + if (tmp[strlen(tmp)-1] == '/') + tmp[strlen(tmp)-1] = '\0'; + tvheadend_webroot = tmp; + } - log_stderr = 1; - log_decorate = isatty(2); - - if(forkaway) { - grp = getgrnam(groupnam ?: "video"); - pw = usernam ? getpwnam(usernam) : NULL; - - pidfile = fopen(pidpath, "w+"); + /* Daemonise */ + if(opt_fork) { + const char *homedir; + gid_t gid; + uid_t uid; + struct group *grp = getgrnam(opt_group ?: "video"); + struct passwd *pw = getpwnam(opt_user) ?: NULL; + FILE *pidfile = fopen(opt_pidpath, "w+"); if(grp != NULL) { gid = grp->gr_gid; @@ -420,7 +511,8 @@ main(int argc, char **argv) int gnum; gnum = get_user_groups(pw, glist, 10); if (setgroups(gnum, glist)) { - tvhlog(LOG_ALERT, "START", "setgroups() failed, do you have permission?"); + tvhlog(LOG_ALERT, "START", + "setgroups() failed, do you have permission?"); return 1; } } @@ -431,11 +523,13 @@ main(int argc, char **argv) uid = 1; } if ((getgid() != gid) && setgid(gid)) { - tvhlog(LOG_ALERT, "START", "setgid() failed, do you have permission?"); + tvhlog(LOG_ALERT, "START", + "setgid() failed, do you have permission?"); return 1; } if ((getuid() != uid) && setuid(uid)) { - tvhlog(LOG_ALERT, "START", "setuid() failed, do you have permission?"); + tvhlog(LOG_ALERT, "START", + "setuid() failed, do you have permission?"); return 1; } @@ -450,24 +544,24 @@ main(int argc, char **argv) umask(0); } - log_stderr = !forkaway; + /* Setup logging */ + log_stderr = !opt_fork; log_decorate = isatty(2); + openlog("tvheadend", LOG_PID, LOG_DAEMON); - sigfillset(&set); - sigprocmask(SIG_BLOCK, &set, NULL); - - openlog("tvheadend", LOG_PID, logfacility); - - hts_settings_init(confpath); + /* Initialise configuration */ + hts_settings_init(opt_config); + /* Setup global mutexes */ pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); - pthread_mutex_lock(&global_lock); time(&dispatch_clock); + /* Signal handling */ + signal(SIGPIPE, handle_sigpipe); trap_init(argv[0]); /** @@ -488,11 +582,11 @@ main(int argc, char **argv) subscription_init(); - access_init(createdefault); + access_init(opt_firstrun); #if ENABLE_LINUXDVB muxes_init(); - dvb_init(adapter_mask, dvb_rawts_input); + dvb_init(adapter_mask, opt_dvb_raw); #endif iptv_input_init(); @@ -522,11 +616,11 @@ main(int argc, char **argv) htsp_init(); - if(rawts_input != NULL) - rawts_init(rawts_input); + if(opt_rawts != NULL) + rawts_init(opt_rawts); - if(join_transport != NULL) - subscription_dummy_join(join_transport, 1); + if(opt_subscribe != NULL) + subscription_dummy_join(opt_subscribe, 1); #ifdef CONFIG_AVAHI avahi_init(); @@ -536,7 +630,6 @@ main(int argc, char **argv) pthread_mutex_unlock(&global_lock); - /** * Wait for SIGTERM / SIGINT, but only in this thread */ @@ -556,7 +649,7 @@ main(int argc, char **argv) tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root()); - if(crash) + if(opt_abort) abort(); mainloop(); @@ -565,8 +658,8 @@ main(int argc, char **argv) tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); - if(forkaway) - unlink("/var/run/tvheadend.pid"); + if(opt_fork) + unlink(opt_pidpath); return 0; diff --git a/src/tvheadend.h b/src/tvheadend.h index 8c566440..c578a6ed 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -64,9 +64,9 @@ extern pthread_mutex_t global_lock; extern pthread_mutex_t ffmpeg_lock; extern pthread_mutex_t fork_lock; -extern int webui_port; -extern int webui_debug; -extern int htsp_port; +extern int tvheadend_webui_port; +extern int tvheadend_webui_debug; +extern int tvheadend_htsp_port; typedef struct source_info { char *si_device; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index f1bd1e47..f68dff87 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -94,9 +94,9 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) "\n" "\n" "\n", - webui_debug ? "-debug" : "", - webui_debug ? "-debug" : "", - webui_debug ? "" : "-min"); + tvheadend_webui_debug ? "-debug" : "", + tvheadend_webui_debug ? "-debug" : "", + tvheadend_webui_debug ? "" : "-min"); extjs_exec(hq, "Ext.BLANK_IMAGE_URL = " "'"EXTJSPATH"/resources/images/default/s.gif';"); diff --git a/src/webui/webui.c b/src/webui/webui.c index 90ffc6f4..c4963223 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -939,7 +939,7 @@ int page_statedump(http_connection_t *hc, const char *remain, void *opaque); void webui_init(void) { - if (webui_debug) + if (tvheadend_webui_debug) tvhlog(LOG_INFO, "webui", "Running web interface in debug mode"); http_path_add("", NULL, page_root2, ACCESS_WEB_INTERFACE); From 62acb529b603ce17934fad272ff85ceb6e776a42 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 8 Jan 2013 16:35:41 +0000 Subject: [PATCH 217/503] debian: add curl as build req --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 060e7757..cc924927 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: tvheadend Section: video Priority: extra Maintainer: Andreas Öman -Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2 +Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev Standards-Version: 3.7.3 Package: tvheadend From 92c89bc644b7ae151dba7b43017dcf5c0677a3d5 Mon Sep 17 00:00:00 2001 From: Jacek Tomasiak Date: Tue, 8 Jan 2013 18:27:50 +0000 Subject: [PATCH 218/503] restored old default port --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 252a5ca4..e86f3842 100644 --- a/src/main.c +++ b/src/main.c @@ -344,7 +344,7 @@ main(int argc, char **argv) log_debug_to_console = 0; tvheadend_webui_port = 9981; tvheadend_webroot = NULL; - tvheadend_htsp_port = 9992; + tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; /* Command line options */ From 6b25dd99a86bb9117651226b3800d0840f32361b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20R=C3=B6=C3=B6s?= Date: Tue, 8 Jan 2013 20:38:22 +0100 Subject: [PATCH 219/503] dont use variables that goes out of scope Change-Id: Ica5f05e63bb811141314cc24f4f68be245f364f6 --- src/htsbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htsbuf.c b/src/htsbuf.c index 03f49a3a..045fa071 100644 --- a/src/htsbuf.c +++ b/src/htsbuf.c @@ -384,6 +384,7 @@ htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s) while(1) { const char *esc; + char buf[4]; C = *c++; if((C >= '0' && C <= '9') || @@ -396,7 +397,6 @@ htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s) esc = NULL; } else { static const char hexchars[16] = "0123456789ABCDEF"; - char buf[4]; buf[0] = '%'; buf[1] = hexchars[(C >> 4) & 0xf]; buf[2] = hexchars[C & 0xf];; From 153c2f09d8d53570677a5e22129ba9153651ea3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 15:29:21 +0100 Subject: [PATCH 220/503] mkv: added support for chapter insertion --- src/muxer/tvh/mkmux.c | 127 +++++++++++++++++++++++++++++++++++++++++- src/muxer/tvh/mkmux.h | 2 + 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index da6a34ba..9293b03d 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -34,6 +34,7 @@ extern int dvr_iov_max; TAILQ_HEAD(mk_cue_queue, mk_cue); +TAILQ_HEAD(mk_chapter_queue, mk_chapter); #define MATROSKA_TIMESCALE 1000000 // in nS @@ -61,6 +62,15 @@ struct mk_cue { off_t cluster_pos; }; +/** + * + */ +struct mk_chapter { + TAILQ_ENTRY(mk_chapter) link; + int uuid; + int64_t ts; +}; + /** * */ @@ -90,10 +100,12 @@ struct mk_mux { off_t trackinfo_pos; off_t metadata_pos; off_t cue_pos; + off_t chapters_pos; int addcue; struct mk_cue_queue cues; + struct mk_chapter_queue chapters; char uuid[16]; char *title; @@ -412,6 +424,70 @@ mk_write_segment_header(mk_mux_t *mkm, int64_t size) } +/** + * + */ +static htsbuf_queue_t * +mk_build_one_chapter(struct mk_chapter *ch) +{ + htsbuf_queue_t *q = htsbuf_queue_alloc(0); + + ebml_append_uint(q, 0x73C4, ch->uuid); + ebml_append_uint(q, 0x91, ch->ts * MATROSKA_TIMESCALE); + ebml_append_uint(q, 0x98, 0); //ChapterFlagHidden + ebml_append_uint(q, 0x4598, 1); //ChapterFlagEnabled + + return q; +} + + +/** + * + */ +static htsbuf_queue_t * +mk_build_edition_entry(mk_mux_t *mkm) +{ + struct mk_chapter *ch; + htsbuf_queue_t *q = htsbuf_queue_alloc(0); + + ebml_append_uint(q, 0x45bd, 0); //EditionFlagHidden + ebml_append_uint(q, 0x45db, 1); //EditionFlagDefault + + TAILQ_FOREACH(ch, &mkm->chapters, link) { + ebml_append_master(q, 0xB6, mk_build_one_chapter(ch)); + } + + return q; +} + + +/** + * + */ +static htsbuf_queue_t * +mk_build_chapters(mk_mux_t *mkm) +{ + htsbuf_queue_t *q = htsbuf_queue_alloc(0); + + ebml_append_master(q, 0x45b9, mk_build_edition_entry(mkm)); + + return q; +} + +/** + * + */ +static void +mk_write_chapters(mk_mux_t *mkm) +{ + if(TAILQ_FIRST(&mkm->chapters) == NULL) + return; + + mkm->chapters_pos = mkm->fdpos; + mk_write_master(mkm, 0x1043a770, mk_build_chapters(mkm)); +} + + /** * */ @@ -581,6 +657,11 @@ mk_build_metaseek(mk_mux_t *mkm) ebml_append_master(q, 0x4dbb, mk_build_one_metaseek(mkm, 0x1c53bb6b, mkm->cue_pos)); + + if(mkm->chapters_pos) + ebml_append_master(q, 0x4dbb, + mk_build_one_metaseek(mkm, 0x1043a770, + mkm->chapters_pos)); return q; } @@ -657,6 +738,29 @@ addcue(mk_mux_t *mkm, int64_t pts, int tracknum) } +/** + * + */ +static void +mk_add_chapter(mk_mux_t *mkm, int64_t ts) +{ + struct mk_chapter *ch; + int uuid; + + ch = TAILQ_LAST(&mkm->chapters, mk_chapter_queue); + if(ch) + uuid = ch->uuid + 1; + else + uuid = 1; + + ch = malloc(sizeof(struct mk_chapter)); + + ch->uuid = uuid; + ch->ts = ts; + + TAILQ_INSERT_TAIL(&mkm->chapters, ch, link); +} + /** * */ @@ -863,6 +967,7 @@ mk_mux_init(mk_mux_t *mkm, const char *title, const struct streaming_start *ss) mkm->title = strdup(mkm->filename); TAILQ_INIT(&mkm->cues); + TAILQ_INIT(&mkm->chapters); htsbuf_queue_init(&q, 0); @@ -901,7 +1006,7 @@ mk_mux_write_pkt(mk_mux_t *mkm, struct th_pkt *pkt) pkt = pkt_merge_header(pkt); mk_write_frame_i(mkm, t, pkt); } - + pkt_ref_dec(pkt); return mkm->error; @@ -928,6 +1033,19 @@ mk_mux_write_meta(mk_mux_t *mkm, const dvr_entry_t *de, } +/** + * Insert a new chapter at the current location + */ +int +mk_mux_insert_chapter(mk_mux_t *mkm) +{ + if(mkm->totduration != PTS_UNSET) + mk_add_chapter(mkm, mkm->totduration); + + return mkm->error; +} + + /** * Close the muxer */ @@ -937,6 +1055,7 @@ mk_mux_close(mk_mux_t *mkm) int64_t totsize; mk_close_cluster(mkm); mk_write_cues(mkm); + mk_write_chapters(mkm); mk_write_metaseek(mkm, 0); totsize = mkm->fdpos; @@ -977,6 +1096,12 @@ mk_mux_close(mk_mux_t *mkm) void mk_mux_destroy(mk_mux_t *mkm) { + struct mk_chapter *ch; + + while((ch = TAILQ_FIRST(&mkm->chapters)) != NULL) { + free(ch); + } + free(mkm->filename); free(mkm->tracks); free(mkm->title); diff --git a/src/muxer/tvh/mkmux.h b/src/muxer/tvh/mkmux.h index ddb4a866..8b44145d 100644 --- a/src/muxer/tvh/mkmux.h +++ b/src/muxer/tvh/mkmux.h @@ -40,6 +40,8 @@ int mk_mux_write_pkt (mk_mux_t *mkm, struct th_pkt *pkt); int mk_mux_write_meta(mk_mux_t *mkm, const struct dvr_entry *de, const struct epg_broadcast *eb); +int mk_mux_insert_chapter(mk_mux_t *mkm); + int mk_mux_close (mk_mux_t *mkm); void mk_mux_destroy(mk_mux_t *mkm); From cff10d66639d0c8be55c747220e65652e597a5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 15:37:20 +0100 Subject: [PATCH 221/503] added support for chapters insertion in tha muxer api --- src/muxer.c | 12 ++++++++++++ src/muxer.h | 2 ++ src/muxer/muxer_tvh.c | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/muxer.c b/src/muxer.c index 2de07edf..ecf5618c 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -308,6 +308,18 @@ muxer_reconfigure(muxer_t *m, const struct streaming_start *ss) } +/** + * sanity wrapper arround m_add_marker() + */ +int +muxer_add_marker(muxer_t *m) +{ + if(!m) + return -1; + + return m->m_add_marker(m); +} + /** * sanity wrapper arround m_open_file() */ diff --git a/src/muxer.h b/src/muxer.h index 6d57cd7f..7894c3fc 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -52,6 +52,7 @@ typedef struct muxer { int (*m_write_pkt) (struct muxer *, // Append a media packet streaming_message_type_t, void *); + int (*m_add_marker) (struct muxer *); // Add a marker (or chapter) int m_errors; // Number of errors muxer_container_type_t m_container; // The type of the container @@ -77,6 +78,7 @@ int muxer_open_file (muxer_t *m, const char *filename); int muxer_open_stream (muxer_t *m, int fd); int muxer_init (muxer_t *m, const struct streaming_start *ss, const char *name); int muxer_reconfigure (muxer_t *m, const struct streaming_start *ss); +int muxer_add_marker (muxer_t *m); int muxer_close (muxer_t *m); int muxer_destroy (muxer_t *m); int muxer_write_meta (muxer_t *m, struct epg_broadcast *eb); diff --git a/src/muxer/muxer_tvh.c b/src/muxer/muxer_tvh.c index 02846ae5..6d46b912 100644 --- a/src/muxer/muxer_tvh.c +++ b/src/muxer/muxer_tvh.c @@ -81,6 +81,23 @@ tvh_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) } +/** + * Insert a new chapter at the current location + */ +static int +tvh_muxer_add_marker(muxer_t* m) +{ + tvh_muxer_t *tm = (tvh_muxer_t*)m; + + if(mk_mux_insert_chapter(tm->tm_ref)) { + tm->m_errors++; + return -1; + } + + return 0; +} + + /** * Multisegment matroska files do exist but I am not sure if they are supported * by many media players. For now, we'll treat it as an error. @@ -216,6 +233,7 @@ tvh_muxer_create(muxer_container_type_t mc) tm->m_mime = tvh_muxer_mime; tm->m_init = tvh_muxer_init; tm->m_reconfigure = tvh_muxer_reconfigure; + tm->m_add_marker = tvh_muxer_add_marker; tm->m_write_meta = tvh_muxer_write_meta; tm->m_write_pkt = tvh_muxer_write_pkt; tm->m_close = tvh_muxer_close; From fe9eb0c05e42125277f347dd1fe7c7bc06d96e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 15:41:12 +0100 Subject: [PATCH 222/503] tsfix: sometimes, commersial skipping caused the audio/video to get out off sync. Lets just skip the packets for now. --- src/dvr/dvr_rec.c | 9 ++++----- src/plumbing/tsfix.c | 23 ----------------------- src/plumbing/tsfix.h | 2 -- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index d933c195..5129e7e8 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -423,14 +423,13 @@ dvr_thread(void *aux) case SMT_PACKET: pkt = sm->sm_data; - if(pkt->pkt_commercial == COMMERCIAL_YES) { + if(pkt->pkt_commercial == COMMERCIAL_YES) dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0); - tsfix_set_comm_skip(de->de_tsfix, comm_skip); - } else { + else dvr_rec_set_state(de, DVR_RS_RUNNING, 0); - tsfix_set_comm_skip(de->de_tsfix, 0); - } + if(pkt->pkt_commercial == COMMERCIAL_YES && comm_skip) + break; if(started) { muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); sm->sm_data = NULL; diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index 4fea8751..a811e67c 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -40,7 +40,6 @@ typedef struct tfstream { int64_t tfs_dts_epoch; int64_t tfs_last_dts_in; - int64_t tfs_drops; } tfstream_t; @@ -57,7 +56,6 @@ typedef struct tsfix { int tf_hasvideo; int64_t tf_tsref; time_t tf_start_time; - int tf_comm_skip; struct th_pktref_queue tf_ptsq; @@ -162,9 +160,6 @@ normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) /* Subtract the transport wide start offset */ dts = pkt->pkt_dts - tf->tf_tsref; - /* Subtract dropped packets due to commercial breaks */ - dts -= tfs->tfs_drops; - if(tfs->tfs_last_dts_norm == PTS_UNSET) { if(dts < 0) { /* Early packet with negative time stamp, drop those */ @@ -331,7 +326,6 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) if(pkt->pkt_dts == PTS_UNSET) { if(tfs->tfs_last_dts_in == PTS_UNSET) { - tfs->tfs_drops += pdur; pkt_ref_dec(pkt); return; } @@ -343,12 +337,6 @@ tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) tfs->tfs_last_dts_in, pdur, pkt->pkt_dts); } - if(tf->tf_comm_skip && pkt->pkt_commercial == COMMERCIAL_YES) { - tfs->tfs_drops += pdur; - pkt_ref_dec(pkt); - return; - } - tfs->tfs_last_dts_in = pkt->pkt_dts; compute_pts(tf, tfs, pkt); @@ -416,17 +404,6 @@ void tsfix_set_start_time(streaming_target_t *pad, time_t start) } -/** - * - */ -void -tsfix_set_comm_skip(streaming_target_t *pad, int bool) { - tsfix_t *tf = (tsfix_t *)pad; - - tf->tf_comm_skip = !!bool; -} - - /** * */ diff --git a/src/plumbing/tsfix.h b/src/plumbing/tsfix.h index 210bc88c..156284fe 100644 --- a/src/plumbing/tsfix.h +++ b/src/plumbing/tsfix.h @@ -25,8 +25,6 @@ streaming_target_t *tsfix_create(streaming_target_t *output); void tsfix_set_start_time(streaming_target_t *pad, time_t start); -void tsfix_set_comm_skip(streaming_target_t *pad, int bool); - void tsfix_destroy(streaming_target_t *gh); From a846f70e4f2394d686d5d947a67b5edfb8c69ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 15:43:20 +0100 Subject: [PATCH 223/503] dvr: add a marker when there is a commercial break --- src/dvr/dvr_rec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 5129e7e8..59daec43 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -405,6 +405,7 @@ dvr_thread(void *aux) int run = 1; int started = 0; int comm_skip = (cfg->dvr_flags & DVR_SKIP_COMMERCIALS); + int commercial = COMMERCIAL_UNKNOWN; pthread_mutex_lock(&sq->sq_mutex); @@ -430,6 +431,12 @@ dvr_thread(void *aux) if(pkt->pkt_commercial == COMMERCIAL_YES && comm_skip) break; + + if(commercial != pkt->pkt_commercial) + muxer_add_marker(de->de_mux); + + commercial = pkt->pkt_commercial; + if(started) { muxer_write_pkt(de->de_mux, sm->sm_type, sm->sm_data); sm->sm_data = NULL; From 578c9fea796f6cc99dc217419ce84f3abf598786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 18:02:04 +0100 Subject: [PATCH 224/503] mkv: don't add a new chapter if the previous one was added less than 10s ago --- src/muxer/tvh/mkmux.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 9293b03d..22c23bdf 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -748,10 +748,17 @@ mk_add_chapter(mk_mux_t *mkm, int64_t ts) int uuid; ch = TAILQ_LAST(&mkm->chapters, mk_chapter_queue); - if(ch) + if(ch) { + // don't add a new chapter if the previous one was + // added less than 10s ago + if(ts - ch->ts < 10000) + return; + uuid = ch->uuid + 1; - else + } + else { uuid = 1; + } ch = malloc(sizeof(struct mk_chapter)); From a9cef28b9f12947225eaf4d002f0fb4daa82a9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 18:18:23 +0100 Subject: [PATCH 225/503] mkv: cosmetics --- src/muxer/tvh/mkmux.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 22c23bdf..5b723be2 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -50,26 +50,26 @@ typedef struct mk_track { int tracknum; int disabled; int64_t nextpts; -} mk_track; +} mk_track_t; /** * */ -struct mk_cue { +typedef struct mk_cue { TAILQ_ENTRY(mk_cue) link; int64_t ts; int tracknum; off_t cluster_pos; -}; +} mk_cue_t; /** * */ -struct mk_chapter { +typedef struct mk_chapter { TAILQ_ENTRY(mk_chapter) link; int uuid; int64_t ts; -}; +} mk_chapter_t; /** * @@ -81,7 +81,7 @@ struct mk_mux { off_t fdpos; // Current position in file int seekable; - mk_track *tracks; + mk_track_t *tracks; int ntracks; int has_video; @@ -177,7 +177,7 @@ mk_build_segment_info(mk_mux_t *mkm) * */ static htsbuf_queue_t * -mk_build_tracks(mk_mux_t *mkm, const struct streaming_start *ss) +mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) { const streaming_start_component_t *ssc; const char *codec_id; @@ -186,7 +186,7 @@ mk_build_tracks(mk_mux_t *mkm, const struct streaming_start *ss) int tracknum = 0; uint8_t buf4[4]; - mkm->tracks = calloc(1, sizeof(mk_track) * ss->ss_num_components); + mkm->tracks = calloc(1, sizeof(mk_track_t) * ss->ss_num_components); mkm->ntracks = ss->ss_num_components; for(i = 0; i < ss->ss_num_components; i++) { @@ -428,7 +428,7 @@ mk_write_segment_header(mk_mux_t *mkm, int64_t size) * */ static htsbuf_queue_t * -mk_build_one_chapter(struct mk_chapter *ch) +mk_build_one_chapter(mk_chapter_t *ch) { htsbuf_queue_t *q = htsbuf_queue_alloc(0); @@ -447,7 +447,7 @@ mk_build_one_chapter(struct mk_chapter *ch) static htsbuf_queue_t * mk_build_edition_entry(mk_mux_t *mkm) { - struct mk_chapter *ch; + mk_chapter_t *ch; htsbuf_queue_t *q = htsbuf_queue_alloc(0); ebml_append_uint(q, 0x45bd, 0); //EditionFlagHidden @@ -704,7 +704,7 @@ mk_write_metaseek(mk_mux_t *mkm, int first) */ static htsbuf_queue_t * mk_build_segment(mk_mux_t *mkm, - const struct streaming_start *ss) + const streaming_start_t *ss) { htsbuf_queue_t *p = htsbuf_queue_alloc(0); @@ -730,7 +730,7 @@ mk_build_segment(mk_mux_t *mkm, static void addcue(mk_mux_t *mkm, int64_t pts, int tracknum) { - struct mk_cue *mc = malloc(sizeof(struct mk_cue)); + mk_cue_t *mc = malloc(sizeof(mk_cue_t)); mc->ts = pts; mc->tracknum = tracknum; mc->cluster_pos = mkm->cluster_pos; @@ -744,7 +744,7 @@ addcue(mk_mux_t *mkm, int64_t pts, int tracknum) static void mk_add_chapter(mk_mux_t *mkm, int64_t ts) { - struct mk_chapter *ch; + mk_chapter_t *ch; int uuid; ch = TAILQ_LAST(&mkm->chapters, mk_chapter_queue); @@ -760,7 +760,7 @@ mk_add_chapter(mk_mux_t *mkm, int64_t ts) uuid = 1; } - ch = malloc(sizeof(struct mk_chapter)); + ch = malloc(sizeof(mk_chapter_t)); ch->uuid = uuid; ch->ts = ts; @@ -784,7 +784,7 @@ mk_close_cluster(mk_mux_t *mkm) * */ static void -mk_write_frame_i(mk_mux_t *mkm, mk_track *t, th_pkt_t *pkt) +mk_write_frame_i(mk_mux_t *mkm, mk_track_t *t, th_pkt_t *pkt) { int64_t pts = pkt->pkt_pts, delta, nxt; unsigned char c_delta_flags[3]; @@ -877,7 +877,7 @@ mk_write_frame_i(mk_mux_t *mkm, mk_track *t, th_pkt_t *pkt) static void mk_write_cues(mk_mux_t *mkm) { - struct mk_cue *mc; + mk_cue_t *mc; htsbuf_queue_t *q, *c, *p; if(TAILQ_FIRST(&mkm->cues) == NULL) @@ -911,7 +911,7 @@ mk_write_cues(mk_mux_t *mkm) */ mk_mux_t *mk_mux_create(void) { - mk_mux_t *mkm = calloc(1, sizeof(struct mk_mux)); + mk_mux_t *mkm = calloc(1, sizeof(mk_mux_t)); mkm->fd = -1; @@ -962,7 +962,7 @@ mk_mux_open_file(mk_mux_t *mkm, const char *filename) * Init the muxer with a title and some tracks */ int -mk_mux_init(mk_mux_t *mkm, const char *title, const struct streaming_start *ss) +mk_mux_init(mk_mux_t *mkm, const char *title, const streaming_start_t *ss) { htsbuf_queue_t q; @@ -996,10 +996,10 @@ mk_mux_init(mk_mux_t *mkm, const char *title, const struct streaming_start *ss) * Append a packet to the muxer */ int -mk_mux_write_pkt(mk_mux_t *mkm, struct th_pkt *pkt) +mk_mux_write_pkt(mk_mux_t *mkm, th_pkt_t *pkt) { int i; - mk_track *t = NULL; + mk_track_t *t = NULL; for(i = 0; i < mkm->ntracks;i++) { if(mkm->tracks[i].index == pkt->pkt_componentindex && mkm->tracks[i].enabled) { @@ -1103,7 +1103,7 @@ mk_mux_close(mk_mux_t *mkm) void mk_mux_destroy(mk_mux_t *mkm) { - struct mk_chapter *ch; + mk_chapter_t *ch; while((ch = TAILQ_FIRST(&mkm->chapters)) != NULL) { free(ch); From aa1eaff9277c9ee5fa4ea3649b57e45a8da82d0f Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Tue, 8 Jan 2013 22:49:17 +0000 Subject: [PATCH 226/503] TRANSMISSION_MODE_4K did not exist prior to DVB API v5, use AUTO instead. Fixes issue 1510. --- src/dvb/dvb_tables.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 725d01ca..5c1398c4 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -630,7 +630,14 @@ static const fe_guard_interval_t guard_interval_tab [4] = { }; static const fe_transmit_mode_t transmission_mode_tab [4] = { - TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_AUTO + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_8K, +#if DVB_API_VERSION >= 5 + TRANSMISSION_MODE_4K, +#else + TRANSMISSION_MODE_AUTO, /* For older DVB API versions - hope the device can detect */ +#endif + TRANSMISSION_MODE_AUTO }; static const fe_hierarchy_t hierarchy_info_tab [8] = { From ec76be3f1bba9db69cde30299ede6d0666465c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 23:15:27 +0100 Subject: [PATCH 227/503] mkv: insert chapters upon various codec changes --- src/muxer/tvh/mkmux.c | 51 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 5b723be2..2837edb3 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -50,6 +50,12 @@ typedef struct mk_track { int tracknum; int disabled; int64_t nextpts; + + uint8_t channels; + uint8_t sri; + + uint16_t aspect_num; + uint16_t aspect_den; } mk_track_t; /** @@ -198,7 +204,11 @@ mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) continue; mkm->tracks[i].index = ssc->ssc_index; - mkm->tracks[i].type = ssc->ssc_type; + mkm->tracks[i].type = ssc->ssc_type; + mkm->tracks[i].channels = ssc->ssc_channels; + mkm->tracks[i].aspect_num = ssc->ssc_aspect_num; + mkm->tracks[i].aspect_den = ssc->ssc_aspect_den; + mkm->tracks[i].sri = ssc->ssc_sri; mkm->tracks[i].nextpts = PTS_UNSET; switch(ssc->ssc_type) { @@ -998,7 +1008,7 @@ mk_mux_init(mk_mux_t *mkm, const char *title, const streaming_start_t *ss) int mk_mux_write_pkt(mk_mux_t *mkm, th_pkt_t *pkt) { - int i; + int i, mark; mk_track_t *t = NULL; for(i = 0; i < mkm->ntracks;i++) { if(mkm->tracks[i].index == pkt->pkt_componentindex && @@ -1008,12 +1018,41 @@ mk_mux_write_pkt(mk_mux_t *mkm, th_pkt_t *pkt) } } - if(t != NULL && !t->disabled) { - if(t->merge) - pkt = pkt_merge_header(pkt); - mk_write_frame_i(mkm, t, pkt); + if(t == NULL || t->disabled) { + pkt_ref_dec(pkt); + return mkm->error; } + mark = 0; + if(pkt->pkt_channels != t->channels && + pkt->pkt_channels) { + mark = 1; + t->channels = pkt->pkt_channels; + } + if(pkt->pkt_aspect_num != t->aspect_num && + pkt->pkt_aspect_num) { + mark = 1; + pkt->pkt_aspect_num = t->aspect_num; + } + if(pkt->pkt_aspect_den != t->aspect_den && + pkt->pkt_aspect_den) { + mark = 1; + pkt->pkt_aspect_den = t->aspect_den; + } + if(pkt->pkt_sri != t->sri && + pkt->pkt_sri) { + mark = 1; + t->sri = pkt->pkt_sri; + } + + if(mark) + mk_mux_insert_chapter(mkm); + + if(t->merge) + pkt = pkt_merge_header(pkt); + + mk_write_frame_i(mkm, t, pkt); + pkt_ref_dec(pkt); return mkm->error; From de818c09f25199f32eba8c75bd7d55d1fa4d3c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Tue, 8 Jan 2013 23:50:09 +0100 Subject: [PATCH 228/503] mkv: fixed typo causing endless loop when chapters are created --- src/muxer/tvh/mkmux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 2837edb3..54113d75 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -1145,6 +1145,7 @@ mk_mux_destroy(mk_mux_t *mkm) mk_chapter_t *ch; while((ch = TAILQ_FIRST(&mkm->chapters)) != NULL) { + TAILQ_REMOVE(&mkm->chapters, ch, link); free(ch); } From 09e2ebd54de311b801653384ff6008b78e91af7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 00:06:42 +0100 Subject: [PATCH 229/503] mkv: fixed copy & pase typo --- src/muxer/tvh/mkmux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 54113d75..587dc755 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -1032,12 +1032,12 @@ mk_mux_write_pkt(mk_mux_t *mkm, th_pkt_t *pkt) if(pkt->pkt_aspect_num != t->aspect_num && pkt->pkt_aspect_num) { mark = 1; - pkt->pkt_aspect_num = t->aspect_num; + t->aspect_num = pkt->pkt_aspect_num; } if(pkt->pkt_aspect_den != t->aspect_den && pkt->pkt_aspect_den) { mark = 1; - pkt->pkt_aspect_den = t->aspect_den; + t->aspect_den = pkt->pkt_aspect_den; } if(pkt->pkt_sri != t->sri && pkt->pkt_sri) { From 3c5f789d6c3a7929c094e6b603af0774a251f397 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 8 Jan 2013 23:10:29 +0000 Subject: [PATCH 230/503] streaming: make sure raw streaming bypasses muxer. --- src/webui/webui.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index c4963223..c7a79c50 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -155,7 +155,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) */ static void http_stream_run(http_connection_t *hc, streaming_queue_t *sq, - const char *name, muxer_container_type_t mc) + const char *name, muxer_container_type_t mc, int raw) { streaming_message_t *sm; int run = 1; @@ -167,9 +167,11 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, int err = 0; socklen_t errlen = sizeof(err); - mux = muxer_create(mc); - if(muxer_open_stream(mux, hc->hc_fd)) - run = 0; + if (!raw) { + mux = muxer_create(mc); + if(muxer_open_stream(mux, hc->hc_fd)) + run = 0; + } /* reduce timeout on write() for streaming */ tp.tv_sec = 5; @@ -205,6 +207,16 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); pthread_mutex_unlock(&sq->sq_mutex); + if (raw) { + if (sm->sm_type == SMT_MPEGTS) { + pktbuf_t *pb = sm->sm_data; + if (tvh_write(hc->hc_fd, pb->pb_data, pb->pb_size)) + run = 0; + } + streaming_msg_free(sm); + continue; + } + switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: @@ -268,10 +280,12 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } } - if(started) - muxer_close(mux); - muxer_destroy(mux); + if(mux != NULL){ + if(started) + muxer_close(mux); + muxer_destroy(mux); + } } @@ -592,7 +606,7 @@ http_stream_service(http_connection_t *hc, service_t *service) name = strdupa(service->s_ch ? service->s_ch->ch_name : service->s_nicename); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, mc); + http_stream_run(hc, &sq, name, mc, 0); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); } @@ -624,7 +638,7 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st); name = strdupa(tdmi->tdmi_identifier); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, MC_PASS); + http_stream_run(hc, &sq, name, MC_PASS, 1); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); @@ -687,7 +701,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) if(s) { name = strdupa(ch->ch_name); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, mc); + http_stream_run(hc, &sq, name, mc, 0); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); } From 82ac35c078aeb0a33bebb7c9c7f60e2926df6a6f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 11:01:31 +0000 Subject: [PATCH 231/503] main: restore original signal handling removed in error. --- src/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index e86f3842..710e736c 100644 --- a/src/main.c +++ b/src/main.c @@ -490,6 +490,8 @@ main(int argc, char **argv) tvheadend_webroot = tmp; } + signal(SIGPIPE, handle_sigpipe); // will be redundant later + /* Daemonise */ if(opt_fork) { const char *homedir; @@ -561,7 +563,8 @@ main(int argc, char **argv) time(&dispatch_clock); /* Signal handling */ - signal(SIGPIPE, handle_sigpipe); + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /** From ef43010617d584c7222324501ad0202d2d068255 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 13:44:48 +0000 Subject: [PATCH 232/503] imagecache: restrict access to webui allowed users only. --- src/webui/webui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui/webui.c b/src/webui/webui.c index c7a79c50..8d7d90d8 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -967,7 +967,7 @@ webui_init(void) http_path_add("/stream", NULL, http_stream, ACCESS_STREAMING); - http_path_add("/imagecache", NULL, page_imagecache, ACCESS_ANONYMOUS); + http_path_add("/imagecache", NULL, page_imagecache, ACCESS_WEB_INTERFACE); webui_static_content("/static", "src/webui/static"); webui_static_content("/docs", "docs/html"); From f2d6ef4aa0cbb3b3813d79adcadc79e2342c2c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 15:59:32 +0100 Subject: [PATCH 233/503] webui: show username, hostname and client name when subscriving to a service --- src/serviceprobe.c | 3 ++- src/subscriptions.c | 8 +++++--- src/subscriptions.h | 5 ++++- src/webui/webui.c | 5 ++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/serviceprobe.c b/src/serviceprobe.c index 62dcc211..2d82a713 100644 --- a/src/serviceprobe.c +++ b/src/serviceprobe.c @@ -114,7 +114,8 @@ serviceprobe_thread(void *aux) tvhlog(LOG_INFO, "serviceprobe", "%20s: checking...", t->s_svcname); - s = subscription_create_from_service(t, "serviceprobe", &sq.sq_st, 0); + s = subscription_create_from_service(t, "serviceprobe", &sq.sq_st, 0, + NULL, NULL, "serviceprobe"); if(s == NULL) { t->s_sp_onqueue = 0; TAILQ_REMOVE(&serviceprobe_queue, t, s_sp_link); diff --git a/src/subscriptions.c b/src/subscriptions.c index 2f56825b..1dc1c844 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -406,7 +406,9 @@ subscription_create_from_channel(channel_t *ch, unsigned int weight, */ th_subscription_t * subscription_create_from_service(service_t *t, const char *name, - streaming_target_t *st, int flags) + streaming_target_t *st, int flags, + const char *hostname, const char *username, + const char *client) { th_subscription_t *s; source_info_t si; @@ -414,7 +416,7 @@ subscription_create_from_service(service_t *t, const char *name, s = subscription_create(INT32_MAX, name, st, flags, subscription_input_direct, - NULL, NULL, NULL); + hostname, username, client); if(t->s_status != SERVICE_RUNNING) { if((r = service_start(t, INT32_MAX, 1)) != 0) { @@ -526,7 +528,7 @@ subscription_dummy_join(const char *id, int first) st = calloc(1, sizeof(streaming_target_t)); streaming_target_init(st, dummy_callback, NULL, 0); - subscription_create_from_service(t, "dummy", st, 0); + subscription_create_from_service(t, "dummy", st, 0, NULL, NULL, "dummy"); tvhlog(LOG_NOTICE, "subscription", "Dummy join %s ok", id); diff --git a/src/subscriptions.h b/src/subscriptions.h index 0a88e5f5..a73605f3 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -97,7 +97,10 @@ th_subscription_t *subscription_create_from_channel(struct channel *ch, th_subscription_t *subscription_create_from_service(struct service *t, const char *name, streaming_target_t *st, - int flags); + int flags, + const char *hostname, + const char *username, + const char *client); th_subscription_t *subscription_create(int weight, const char *name, streaming_target_t *st, diff --git a/src/webui/webui.c b/src/webui/webui.c index 8d7d90d8..37c6ee91 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -601,7 +601,10 @@ http_stream_service(http_connection_t *hc, service_t *service) flags = 0; } - s = subscription_create_from_service(service, "HTTP", st, flags); + s = subscription_create_from_service(service, "HTTP", st, flags, + inet_ntoa(hc->hc_peer->sin_addr), + hc->hc_username, + http_arg_get(&hc->hc_args, "User-Agent")); if(s) { name = strdupa(service->s_ch ? service->s_ch->ch_name : service->s_nicename); From ea0d6918ff3c12ae5e55a79ef87cad4b0c324f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 16:00:57 +0100 Subject: [PATCH 234/503] webui: show username, hostname and client name when subscribing to a complete mux --- src/dvb/dvb.h | 5 ++++- src/dvb/dvb_multiplex.c | 7 +++++-- src/webui/webui.c | 5 ++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 1b61f576..5ff64f63 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -548,7 +548,10 @@ void dvb_lnb_get_frequencies(const char *id, struct th_subscription; struct th_subscription *dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, const char *name, - streaming_target_t *st); + streaming_target_t *st, + const char *hostname, + const char *username, + const char *client); #endif /* DVB_H_ */ diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index e129ae63..abce0ec8 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1293,13 +1293,16 @@ th_dvb_mux_instance_t *dvb_mux_find th_subscription_t * dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, const char *name, - streaming_target_t *st) + streaming_target_t *st, + const char *hostname, + const char *username, + const char *client) { th_subscription_t *s; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; s = subscription_create(INT32_MAX, name, st, SUBSCRIPTION_RAW_MPEGTS, - NULL, NULL, NULL, NULL); + NULL, hostname, username, client); s->ths_tdmi = tdmi; diff --git a/src/webui/webui.c b/src/webui/webui.c index 37c6ee91..d5c8357b 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -638,7 +638,10 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) const char *name; streaming_queue_init(&sq, SMT_PACKET); - s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st); + s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st, + inet_ntoa(hc->hc_peer->sin_addr), + hc->hc_username, + http_arg_get(&hc->hc_args, "User-Agent")); name = strdupa(tdmi->tdmi_identifier); pthread_mutex_unlock(&global_lock); http_stream_run(hc, &sq, name, MC_PASS, 1); From 048856342f289cf1ee80329deda3448d7a5f5630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 16:14:04 +0100 Subject: [PATCH 235/503] collect bandwidth statistics on direct subscriptions --- src/subscriptions.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/subscriptions.c b/src/subscriptions.c index 1dc1c844..5dd58379 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -308,6 +308,17 @@ static void subscription_input_direct(void *opauqe, streaming_message_t *sm) { th_subscription_t *s = opauqe; + + if(sm->sm_type == SMT_PACKET) { + th_pkt_t *pkt = sm->sm_data; + if(pkt->pkt_err) + s->ths_total_err++; + s->ths_bytes += pkt->pkt_payload->pb_size; + } else if(sm->sm_type == SMT_MPEGTS) { + pktbuf_t *pb = sm->sm_data; + s->ths_bytes += pb->pb_size; + } + streaming_target_deliver(s->ths_output, sm); } From 54dc40ba881390f5358da22097a31d9515e847e9 Mon Sep 17 00:00:00 2001 From: Piotras Date: Wed, 9 Jan 2013 15:19:50 +0000 Subject: [PATCH 236/503] add missing caids --- src/psi.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/psi.c b/src/psi.c index 1329377e..7ed7a8dd 100644 --- a/src/psi.c +++ b/src/psi.c @@ -855,16 +855,27 @@ static struct strtab caidnametab[] = { { "Viaccess", 0x0500 }, { "Irdeto", 0x0600 }, { "Irdeto", 0x0602 }, - { "Irdeto", 0x0604 }, + { "Irdeto", 0x0603 }, + { "Irdeto", 0x0604 }, + { "Irdeto", 0x0622 }, { "Irdeto", 0x0624 }, + { "Irdeto", 0x0648 }, { "Irdeto", 0x0666 }, { "Jerroldgi", 0x0700 }, { "Matra", 0x0800 }, { "NDS", 0x0900 }, + { "NDS", 0x0919 }, + { "NDS", 0x091F }, + { "NDS", 0x092B }, + { "NDS", 0x09AF }, + { "NDS", 0x09C4 }, { "NDS", 0x0960 }, { "NDS", 0x0963 }, { "Nokia", 0x0A00 }, - { "Conax", 0x0B00 }, + { "Conax", 0x0B00 }, + { "Conax", 0x0B01 }, + { "Conax", 0x0B02 }, + { "Conax", 0x0BAA }, { "NTL", 0x0C00 }, { "CryptoWorks", 0x0D00 }, { "CryptoWorks", 0x0D01 }, @@ -873,6 +884,7 @@ static struct strtab caidnametab[] = { { "CryptoWorks", 0x0D05 }, { "CryptoWorks", 0x0D0F }, { "CryptoWorks", 0x0D70 }, + { "CryptoWorks ICE", 0x0D95 }, { "CryptoWorks ICE", 0x0D96 }, { "CryptoWorks ICE", 0x0D97 }, { "PowerVu", 0x0E00 }, @@ -889,7 +901,16 @@ static struct strtab caidnametab[] = { { "BetaCrypt", 0x1702 }, { "BetaCrypt", 0x1722 }, { "BetaCrypt", 0x1762 }, - { "NagraVision", 0x1800 }, + { "NagraVision", 0x1800 }, + { "NagraVision", 0x1803 }, + { "Nagra Media Access", 0x1813 }, + { "NagraVision", 0x1810 }, + { "NagraVision", 0x1815 }, + { "NagraVision", 0x1830 }, + { "NagraVision", 0x1833 }, + { "NagraVision", 0x1834 }, + { "NagraVision", 0x183D }, + { "NagraVision", 0x1861 }, { "Titan", 0x1900 }, { "Telefonica", 0x2000 }, { "Stentor", 0x2100 }, From cdd6c6c7d328542ee353d11605783de12a8a6722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 16:37:31 +0100 Subject: [PATCH 237/503] http streaming: added a 'raw' container that works just like the passthrough muxer, but doesn't inject pmt/pat this is used when subscribing to a complete dvb mux --- src/dvb/dvb_multiplex.c | 32 +++++++++++++++++++++++++++++++- src/muxer.c | 5 +++++ src/muxer.h | 1 + src/muxer/muxer_pass.c | 10 +++++++--- src/webui/webui.c | 38 ++++++++++++-------------------------- 5 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index abce0ec8..37d3ca17 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1299,7 +1299,11 @@ dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, const char *client) { th_subscription_t *s; + streaming_message_t *sm; + streaming_start_t *ss; + int r; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + char buf[100]; s = subscription_create(INT32_MAX, name, st, SUBSCRIPTION_RAW_MPEGTS, NULL, hostname, username, client); @@ -1308,10 +1312,36 @@ dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, s->ths_tdmi = tdmi; LIST_INSERT_HEAD(&tdmi->tdmi_subscriptions, s, ths_tdmi_link); - dvb_fe_tune(tdmi, "Full mux subscription"); + r = dvb_fe_tune(tdmi, "Full mux subscription"); pthread_mutex_lock(&tda->tda_delivery_mutex); + streaming_target_connect(&tda->tda_streaming_pad, &s->ths_input); + + if(r) { + sm = streaming_msg_create_code(SMT_NOSTART, SM_CODE_NO_INPUT); + streaming_target_deliver(s->ths_output, sm); + } else { + ss = calloc(1, sizeof(streaming_start_t)); + ss->ss_num_components = 0; + ss->ss_refcount = 1; + + ss->ss_si.si_type = S_MPEG_TS; + ss->ss_si.si_device = strdup(tdmi->tdmi_adapter->tda_rootpath); + ss->ss_si.si_adapter = strdup(tdmi->tdmi_adapter->tda_displayname); + ss->ss_si.si_service = strdup("Full mux subscription"); + + if(tdmi->tdmi_network != NULL) + ss->ss_si.si_network = strdup(tdmi->tdmi_network); + + dvb_mux_nicename(buf, sizeof(buf), tdmi); + ss->ss_si.si_mux = strdup(buf); + + + sm = streaming_msg_create_data(SMT_START, ss); + streaming_target_deliver(s->ths_output, sm); + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); notify_reload("subscriptions"); diff --git a/src/muxer.c b/src/muxer.c index ecf5618c..e252a53b 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -36,6 +36,7 @@ static struct strtab container_audio_mime[] = { { "audio/x-mpegts", MC_MPEGTS }, { "audio/mpeg", MC_MPEGPS }, { "application/octet-stream", MC_PASS }, + { "application/octet-stream", MC_RAW }, }; @@ -48,6 +49,7 @@ static struct strtab container_video_mime[] = { { "video/x-mpegts", MC_MPEGTS }, { "video/mpeg", MC_MPEGPS }, { "application/octet-stream", MC_PASS }, + { "application/octet-stream", MC_RAW }, }; @@ -60,6 +62,7 @@ static struct strtab container_name[] = { { "mpegts", MC_MPEGTS }, { "mpegps", MC_MPEGPS }, { "pass", MC_PASS }, + { "raw", MC_RAW }, }; @@ -72,6 +75,7 @@ static struct strtab container_audio_file_suffix[] = { { "ts", MC_MPEGTS }, { "mpeg", MC_MPEGPS }, { "bin", MC_PASS }, + { "bin", MC_RAW }, }; @@ -84,6 +88,7 @@ static struct strtab container_video_file_suffix[] = { { "ts", MC_MPEGTS }, { "mpeg", MC_MPEGPS }, { "bin", MC_PASS }, + { "bin", MC_RAW }, }; diff --git a/src/muxer.h b/src/muxer.h index 7894c3fc..1cfc947a 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -27,6 +27,7 @@ typedef enum { MC_MPEGTS = 2, MC_MPEGPS = 3, MC_PASS = 4, + MC_RAW = 5, } muxer_container_type_t; diff --git a/src/muxer/muxer_pass.c b/src/muxer/muxer_pass.c index ca21c8dd..50dd5e23 100644 --- a/src/muxer/muxer_pass.c +++ b/src/muxer/muxer_pass.c @@ -49,6 +49,7 @@ typedef struct pass_muxer { char *pm_filename; /* TS muxing */ + uint8_t pm_injection; uint8_t *pm_pat; uint8_t *pm_pmt; uint16_t pm_pmt_version; @@ -221,7 +222,9 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) pass_muxer_t *pm = (pass_muxer_t*)m; int rem; - if(pm->pm_pat != NULL) { + if(pm->pm_pat != NULL && + pm->pm_pmt != NULL && + pm->pm_injection) { // Inject pmt and pat into the stream rem = pm->pm_pc % TS_INJECTION_RATE; if(!rem) { @@ -324,7 +327,7 @@ pass_muxer_create(muxer_container_type_t mc) { pass_muxer_t *pm; - if(mc != MC_PASS) + if(mc != MC_PASS && mc != MC_RAW) return NULL; pm = calloc(1, sizeof(pass_muxer_t)); @@ -338,7 +341,8 @@ pass_muxer_create(muxer_container_type_t mc) pm->m_close = pass_muxer_close; pm->m_destroy = pass_muxer_destroy; pm->pm_fd = -1; - + pm->pm_injection = (mc == MC_PASS); + return (muxer_t *)pm; } diff --git a/src/webui/webui.c b/src/webui/webui.c index d5c8357b..0257010e 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -155,7 +155,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque) */ static void http_stream_run(http_connection_t *hc, streaming_queue_t *sq, - const char *name, muxer_container_type_t mc, int raw) + const char *name, muxer_container_type_t mc) { streaming_message_t *sm; int run = 1; @@ -167,11 +167,9 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, int err = 0; socklen_t errlen = sizeof(err); - if (!raw) { - mux = muxer_create(mc); - if(muxer_open_stream(mux, hc->hc_fd)) - run = 0; - } + mux = muxer_create(mc); + if(muxer_open_stream(mux, hc->hc_fd)) + run = 0; /* reduce timeout on write() for streaming */ tp.tv_sec = 5; @@ -207,16 +205,6 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); pthread_mutex_unlock(&sq->sq_mutex); - if (raw) { - if (sm->sm_type == SMT_MPEGTS) { - pktbuf_t *pb = sm->sm_data; - if (tvh_write(hc->hc_fd, pb->pb_data, pb->pb_size)) - run = 0; - } - streaming_msg_free(sm); - continue; - } - switch(sm->sm_type) { case SMT_MPEGTS: case SMT_PACKET: @@ -280,12 +268,10 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } } + if(started) + muxer_close(mux); - if(mux != NULL){ - if(started) - muxer_close(mux); - muxer_destroy(mux); - } + muxer_destroy(mux); } @@ -587,7 +573,7 @@ http_stream_service(http_connection_t *hc, service_t *service) else qsize = 1500000; - if(mc == MC_PASS) { + if(mc == MC_PASS || mc == MC_RAW) { streaming_queue_init2(&sq, SMT_PACKET, qsize); gh = NULL; tsfix = NULL; @@ -609,7 +595,7 @@ http_stream_service(http_connection_t *hc, service_t *service) name = strdupa(service->s_ch ? service->s_ch->ch_name : service->s_nicename); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, mc, 0); + http_stream_run(hc, &sq, name, mc); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); } @@ -644,7 +630,7 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) http_arg_get(&hc->hc_args, "User-Agent")); name = strdupa(tdmi->tdmi_identifier); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, MC_PASS, 1); + http_stream_run(hc, &sq, name, MC_RAW); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); @@ -685,7 +671,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) else qsize = 1500000; - if(mc == MC_PASS) { + if(mc == MC_PASS || mc == MC_RAW) { streaming_queue_init2(&sq, SMT_PACKET, qsize); gh = NULL; tsfix = NULL; @@ -707,7 +693,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) if(s) { name = strdupa(ch->ch_name); pthread_mutex_unlock(&global_lock); - http_stream_run(hc, &sq, name, mc, 0); + http_stream_run(hc, &sq, name, mc); pthread_mutex_lock(&global_lock); subscription_unsubscribe(s); } From f69022390fa2932ca85b4304621daec2aa3acf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 16:37:44 +0100 Subject: [PATCH 238/503] mkv: plug minor memory leak --- src/muxer/tvh/mkmux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 587dc755..258a5438 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -998,6 +998,8 @@ mk_mux_init(mk_mux_t *mkm, const char *title, const streaming_start_t *ss) mk_write_queue(mkm, &q); + htsbuf_queue_flush(&q); + return mkm->error; } From a1749a27c96e7a7b2b894c3d9b891363233d0c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 16:45:14 +0100 Subject: [PATCH 239/503] webui: expose a "play" url to the entire mux. --- src/webui/static/app/dvb.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index a3808527..d351c98c 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -24,6 +24,14 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { var cmlist = Array(); cmlist.push(enabledColumn, { + header : "Play", + dataIndex : 'id', + width : 50, + renderer : function(value, metadata, record, row, col, store) { + url = 'stream/mux/' + value + return 'Play' + } + }, { header : "Network", dataIndex : 'network', width : 200 From 78df091e41ee3f80968bd14726b908b9a4cbe440 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 21:26:20 +0000 Subject: [PATCH 240/503] make: check if configure output is still likely to be valid. --- Makefile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d8d6197e..445586bc 100644 --- a/Makefile +++ b/Makefile @@ -221,10 +221,16 @@ DEPS = ${OBJS:%.o=%.d} all: ${PROG} # Special -.PHONY: clean distclean +.PHONY: clean distclean check_config + +# Check configure output is valid +check_config: + @test $(CURDIR)/.config.mk -nt $(CURDIR)/configure\ + || echo "./configure output is old, please re-run" + @test $(CURDIR)/.config.mk -nt $(CURDIR)/configure # Binary -${PROG}: $(OBJS) $(ALLDEPS) +${PROG}: check_config $(OBJS) $(ALLDEPS) $(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS) # Object From 9180546fc6c6a96015f9581aaa5c408fabf02073 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 12 Oct 2012 17:24:27 +0100 Subject: [PATCH 241/503] timeshift: early prototype of the new timeshift feature. Currently this supports pause/resume, and speed control. FF up to 4x uses full frame output, faster than that or reverse uses i-frame only output. This causes problems with some players and needs work. Also buffers are done at the subscription level which means the disk space is not shared even if it holds the same content. And more importantly this means you cannot yet record the timeshift buffer like on a standard PVR. --- Makefile | 9 +- configure | 1 + src/config2.c | 82 ++++- src/config2.h | 12 + src/dvr/dvr_rec.c | 2 + src/htsp_server.c | 139 ++++++++- src/main.c | 9 + src/packet.h | 8 + src/plumbing/globalheaders.c | 4 + src/plumbing/tsfix.c | 2 + src/streaming.c | 24 +- src/subscriptions.c | 40 ++- src/subscriptions.h | 8 + src/timeshift.c | 184 +++++++++++ src/timeshift.h | 30 ++ src/timeshift/private.h | 139 +++++++++ src/timeshift/timeshift_filemgr.c | 306 +++++++++++++++++++ src/timeshift/timeshift_reader.c | 490 ++++++++++++++++++++++++++++++ src/timeshift/timeshift_writer.c | 327 ++++++++++++++++++++ src/tvheadend.h | 34 +++ src/webui/static/app/config.js | 53 +++- src/webui/webui.c | 2 + 22 files changed, 1873 insertions(+), 32 deletions(-) create mode 100644 src/timeshift.c create mode 100644 src/timeshift.h create mode 100644 src/timeshift/private.h create mode 100644 src/timeshift/timeshift_filemgr.c create mode 100644 src/timeshift/timeshift_reader.c create mode 100644 src/timeshift/timeshift_writer.c diff --git a/Makefile b/Makefile index 445586bc..c42e63d5 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ SRCS-$(CONFIG_LINUXDVB) += src/epggrab/otamux.c\ src/epggrab/support/freesat_huffman.c \ SRCS += src/plumbing/tsfix.c \ - src/plumbing/globalheaders.c \ + src/plumbing/globalheaders.c SRCS += src/dvr/dvr_db.c \ src/dvr/dvr_rec.c \ @@ -145,6 +145,13 @@ SRCS += src/muxer.c \ # Optional code # +# Timeshift +SRCS-${CONFIG_TIMESHIFT} += \ + src/timeshift.c \ + src/timeshift/timeshift_filemgr.c \ + src/timeshift/timeshift_writer.c \ + src/timeshift/timeshift_reader.c \ + # DVB SRCS-${CONFIG_LINUXDVB} += \ src/dvb/dvb.c \ diff --git a/configure b/configure index 5a5ce0ee..6aa50abf 100755 --- a/configure +++ b/configure @@ -24,6 +24,7 @@ OPTIONS=( "avahi:auto" "zlib:auto" "libav:auto" + "timeshift:no" "bundle:no" "dvbcsa:no" ) diff --git a/src/config2.c b/src/config2.c index 5bcbd0d1..05bb7a34 100644 --- a/src/config2.c +++ b/src/config2.c @@ -26,11 +26,24 @@ static htsmsg_t *config; void config_init ( void ) { + int save = 0; + uint32_t u32; + config = hts_settings_load("config"); if (!config) { tvhlog(LOG_DEBUG, "config", "no configuration, loading defaults"); config = htsmsg_create_map(); } + + /* Defaults */ + if (htsmsg_get_u32(config, "timeshiftperiod", &u32)) + save |= config_set_timeshift_period(0); + if (htsmsg_get_u32(config, "timeshiftsize", &u32)) + save |= config_set_timeshift_size(0); + + /* Save defaults */ + if (save) + config_save(); } void config_save ( void ) @@ -43,6 +56,29 @@ htsmsg_t *config_get_all ( void ) return htsmsg_copy(config); } +static int _config_set_str ( const char *fld, const char *val ) +{ + const char *c = htsmsg_get_str(config, fld); + if (!c || strcmp(c, val)) { + if (c) htsmsg_delete_field(config, fld); + htsmsg_add_str(config, fld, val); + return 1; + } + return 0; +} + +static int _config_set_u32 ( const char *fld, uint32_t val ) +{ + uint32_t u32; + int ret = htsmsg_get_u32(config, fld, &u32); + if (ret || (u32 != val)) { + if (!ret) htsmsg_delete_field(config, fld); + htsmsg_add_u32(config, fld, val); + return 1; + } + return 0; +} + const char *config_get_language ( void ) { return htsmsg_get_str(config, "language"); @@ -50,13 +86,7 @@ const char *config_get_language ( void ) int config_set_language ( const char *lang ) { - const char *c = config_get_language(); - if (!c || strcmp(c, lang)) { - if (c) htsmsg_delete_field(config, "language"); - htsmsg_add_str(config, "language", lang); - return 1; - } - return 0; + return _config_set_str("language", lang); } const char *config_get_muxconfpath ( void ) @@ -66,11 +96,35 @@ const char *config_get_muxconfpath ( void ) int config_set_muxconfpath ( const char *path ) { - const char *c = config_get_muxconfpath(); - if (!c || strcmp(c, path)) { - if (c) htsmsg_delete_field(config, "muxconfpath"); - htsmsg_add_str(config, "muxconfpath", path); - return 1; - } - return 0; + return _config_set_str("muxconfpath", path); +} + +const char *config_get_timeshift_path ( void ) +{ + return htsmsg_get_str(config, "timeshiftpath"); +} + +int config_set_timeshift_path ( const char *path ) +{ + return _config_set_str("timeshiftpath", path); +} + +uint32_t config_get_timeshift_period ( void ) +{ + return htsmsg_get_u32_or_default(config, "timeshiftperiod", 0); +} + +int config_set_timeshift_period ( uint32_t period ) +{ + return _config_set_u32("timeshiftperiod", period); +} + +uint32_t config_get_timeshift_size ( void ) +{ + return htsmsg_get_u32_or_default(config, "timeshiftsize", 0); +} + +int config_set_timeshift_size ( uint32_t size ) +{ + return _config_set_u32("timeshiftsize", size); } diff --git a/src/config2.h b/src/config2.h index cd68e306..7ba28e0a 100644 --- a/src/config2.h +++ b/src/config2.h @@ -36,4 +36,16 @@ const char *config_get_language ( void ); int config_set_language ( const char *str ) __attribute__((warn_unused_result)); +const char *config_get_timeshift_path ( void ); +int config_set_timeshift_path ( const char *str ) + __attribute__((warn_unused_result)); + +uint32_t config_get_timeshift_period ( void ); +int config_set_timeshift_period ( uint32_t val ) + __attribute__((warn_unused_result)); + +uint32_t config_get_timeshift_size ( void ); +int config_set_timeshift_size ( uint32_t val ) + __attribute__((warn_unused_result)); + #endif /* __TVH_CONFIG__H__ */ diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 59daec43..0c98f533 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -538,6 +538,8 @@ dvr_thread(void *aux) } break; + case SMT_SPEED: + case SMT_SKIP: case SMT_SIGNAL_STATUS: break; diff --git a/src/htsp_server.c b/src/htsp_server.c index 9ce8c7ff..0c1db9b1 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -43,6 +43,9 @@ #include "epg.h" #include "plumbing/tsfix.h" #include "imagecache.h" +#if ENABLE_TIMESHIFT +#include "timeshift.h" +#endif #include #include "settings.h" @@ -170,6 +173,10 @@ typedef struct htsp_subscription { streaming_target_t hs_input; streaming_target_t *hs_tsfix; +#if ENABLE_TIMESHIFT + streaming_target_t *hs_tshift; +#endif + htsp_msg_q_t hs_q; time_t hs_last_report; /* Last queue status report sent */ @@ -274,6 +281,9 @@ htsp_subscription_destroy(htsp_connection_t *htsp, htsp_subscription_t *hs) if(hs->hs_tsfix != NULL) tsfix_destroy(hs->hs_tsfix); htsp_flush_queue(htsp, &hs->hs_q); +#if ENABLE_TIMESHIFT + if(hs->hs_tshift) timeshift_destroy(hs->hs_tshift); +#endif free(hs); } @@ -1228,6 +1238,9 @@ static htsmsg_t * htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) { uint32_t chid, sid, weight, req90khz, normts; +#if ENABLE_TIMESHIFT + uint32_t timeshiftPeriod; +#endif channel_t *ch; htsp_subscription_t *hs; const char *str; @@ -1249,6 +1262,11 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) req90khz = htsmsg_get_u32_or_default(in, "90khz", 0); normts = htsmsg_get_u32_or_default(in, "normts", 0); +#if ENABLE_TIMESHIFT + timeshiftPeriod = htsmsg_get_u32_or_default(in, "timeshiftPeriod", 0); + timeshiftPeriod = MIN(timeshiftPeriod, config_get_timeshift_period()); +#endif + /* * We send the reply now to avoid the user getting the 'subscriptionStart' * async message before the reply to 'subscribe'. @@ -1279,14 +1297,19 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) LIST_INSERT_HEAD(&htsp->htsp_subscriptions, hs, hs_link); streaming_target_init(&hs->hs_input, htsp_streaming_input, hs, 0); - streaming_target_t *st; + streaming_target_t *st = &hs->hs_input; - if(normts) { - hs->hs_tsfix = tsfix_create(&hs->hs_input); - st = hs->hs_tsfix; - } else { - st = &hs->hs_input; + if(normts) + st = hs->hs_tsfix = tsfix_create(st); +#if ENABLE_TIMESHIFT + if (timeshiftPeriod != 0) { + if (timeshiftPeriod == ~0) + tvhlog(LOG_DEBUG, "htsp", "using timeshift buffer (unlimited)"); + else + tvhlog(LOG_DEBUG, "htsp", "using timeshift buffer (%u mins)", timeshiftPeriod / 60); + st = hs->hs_tshift = timeshift_create(st, timeshiftPeriod); } +#endif hs->hs_s = subscription_create_from_channel(ch, weight, htsp->htsp_logname, @@ -1353,6 +1376,72 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in) return NULL; } +/** + * Skip stream + */ +static htsmsg_t * +htsp_method_skip(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_subscription_t *hs; + uint32_t sid, abs; + int64_t s64; + streaming_skip_t skip; + + if(htsmsg_get_u32(in, "subscriptionId", &sid)) + return htsp_error("Missing argument 'subscriptionId'"); + + abs = htsmsg_get_u32_or_default(in, "absolute", 0); + + if(!htsmsg_get_s64(in, "time", &s64)) { + skip.type = abs ? SMT_SKIP_ABS_TIME : SMT_SKIP_REL_TIME; + skip.time = s64; + } else if (!htsmsg_get_s64(in, "size", &s64)) { + skip.type = abs ? SMT_SKIP_ABS_SIZE : SMT_SKIP_REL_SIZE; + skip.size = s64; + } else { + return htsp_error("Missing argument 'time' or 'size'"); + } + + LIST_FOREACH(hs, &htsp->htsp_subscriptions, hs_link) + if(hs->hs_sid == sid) + break; + + if(hs == NULL) + return htsp_error("Requested subscription does not exist"); + + subscription_set_skip(hs->hs_s, &skip); + + htsp_reply(htsp, in, htsmsg_create_map()); + return NULL; +} + +/* + * Set stream speed + */ +static htsmsg_t * +htsp_method_speed(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_subscription_t *hs; + uint32_t sid; + int32_t speed; + + if(htsmsg_get_u32(in, "subscriptionId", &sid)) + return htsp_error("Missing argument 'subscriptionId'"); + if(htsmsg_get_s32(in, "speed", &speed)) + return htsp_error("Missing argument 'speed'"); + + LIST_FOREACH(hs, &htsp->htsp_subscriptions, hs_link) + if(hs->hs_sid == sid) + break; + + if(hs == NULL) + return htsp_error("Requested subscription does not exist"); + + subscription_set_speed(hs->hs_s, speed); + + htsp_reply(htsp, in, htsmsg_create_map()); + return NULL; +} /** * Open file @@ -1529,6 +1618,8 @@ struct { { "subscribe", htsp_method_subscribe, ACCESS_STREAMING}, { "unsubscribe", htsp_method_unsubscribe, ACCESS_STREAMING}, { "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING}, + { "subscriptionSkip", htsp_method_skip, ACCESS_STREAMING}, + { "subscriptionSpeed", htsp_method_speed, ACCESS_STREAMING}, { "fileOpen", htsp_method_file_open, ACCESS_RECORDER}, { "fileRead", htsp_method_file_read, ACCESS_RECORDER}, { "fileClose", htsp_method_file_close, ACCESS_RECORDER}, @@ -2065,7 +2156,11 @@ const static char frametypearray[PKT_NTYPES] = { * Build a htsmsg from a th_pkt and enqueue it on our HTSP service */ static void +#if ENABLE_TIMESHIFT +htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt, uint64_t timeshift) +#else htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) +#endif { htsmsg_t *m; htsp_msg_t *hm; @@ -2093,6 +2188,11 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) htsmsg_add_u32(m, "stream", pkt->pkt_componentindex); htsmsg_add_u32(m, "com", pkt->pkt_commercial); +#if ENABLE_TIMESHIFT + if (timeshift) + htsmsg_add_s64(m, "timeshift", timeshift); +#endif + if(pkt->pkt_pts != PTS_UNSET) { int64_t pts = hs->hs_90khz ? pkt->pkt_pts : ts_rescale(pkt->pkt_pts, 1000000); @@ -2305,6 +2405,19 @@ htsp_subscription_signal_status(htsp_subscription_t *hs, signal_status_t *sig) htsp_send_message(hs->hs_htsp, m, &hs->hs_htsp->htsp_hmq_qstatus); } +/** + * + */ +static void +htsp_subscription_speed(htsp_subscription_t *hs, int speed) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "method", "subscriptionSpeed"); + htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); + htsmsg_add_u32(m, "speed", speed); + htsp_send(hs->hs_htsp, m, NULL, &hs->hs_q, 0); +} + /** * */ @@ -2315,7 +2428,12 @@ htsp_streaming_input(void *opaque, streaming_message_t *sm) switch(sm->sm_type) { case SMT_PACKET: - htsp_stream_deliver(hs, sm->sm_data); // reference is transfered +#if ENABLE_TIMESHIFT + htsp_stream_deliver(hs, sm->sm_data, sm->sm_timeshift); +#else + htsp_stream_deliver(hs, sm->sm_data); +#endif + // reference is transfered sm->sm_data = NULL; break; @@ -2344,6 +2462,13 @@ htsp_streaming_input(void *opaque, streaming_message_t *sm) case SMT_EXIT: abort(); + + case SMT_SKIP: + break; + + case SMT_SPEED: + htsp_subscription_speed(hs, sm->sm_code); + break; } streaming_msg_free(sm); } diff --git a/src/main.c b/src/main.c index 710e736c..6cff04cb 100644 --- a/src/main.c +++ b/src/main.c @@ -61,6 +61,7 @@ #include "muxes.h" #include "config2.h" #include "imagecache.h" +#include "timeshift.h" #if ENABLE_LIBAV #include "libav.h" #endif @@ -598,6 +599,10 @@ main(int argc, char **argv) v4l_init(); #endif +#if ENABLE_TIMESHIFT + timeshift_init(); +#endif + tcp_server_init(); http_server_init(); webui_init(); @@ -659,6 +664,10 @@ main(int argc, char **argv) epg_save(); +#if ENABLE_TIMESHIFT + timeshift_term(); +#endif + tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); if(opt_fork) diff --git a/src/packet.h b/src/packet.h index fedc1402..2aecce7b 100644 --- a/src/packet.h +++ b/src/packet.h @@ -36,6 +36,14 @@ typedef struct pktbuf { #define PKT_B_FRAME 3 #define PKT_NTYPES 4 +static inline char pkt_frametype_to_char ( int frametype ) +{ + if (frametype == PKT_I_FRAME) return 'I'; + if (frametype == PKT_P_FRAME) return 'P'; + if (frametype == PKT_B_FRAME) return 'B'; + return ' '; +} + typedef struct th_pkt { int64_t pkt_dts; int64_t pkt_pts; diff --git a/src/plumbing/globalheaders.c b/src/plumbing/globalheaders.c index d776f34c..509da87b 100644 --- a/src/plumbing/globalheaders.c +++ b/src/plumbing/globalheaders.c @@ -255,6 +255,8 @@ gh_hold(globalheaders_t *gh, streaming_message_t *sm) case SMT_SIGNAL_STATUS: case SMT_NOSTART: case SMT_MPEGTS: + case SMT_SPEED: + case SMT_SKIP: streaming_target_deliver2(gh->gh_output, sm); break; } @@ -283,6 +285,8 @@ gh_pass(globalheaders_t *gh, streaming_message_t *sm) case SMT_SIGNAL_STATUS: case SMT_NOSTART: case SMT_MPEGTS: + case SMT_SKIP: + case SMT_SPEED: streaming_target_deliver2(gh->gh_output, sm); break; diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index a811e67c..c54b76fc 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -369,6 +369,8 @@ tsfix_input(void *opaque, streaming_message_t *sm) case SMT_SIGNAL_STATUS: case SMT_NOSTART: case SMT_MPEGTS: + case SMT_SPEED: + case SMT_SKIP: break; } diff --git a/src/streaming.c b/src/streaming.c index bf1614f8..3173d3b0 100755 --- a/src/streaming.c +++ b/src/streaming.c @@ -137,6 +137,10 @@ streaming_msg_create(streaming_message_type_t type) { streaming_message_t *sm = malloc(sizeof(streaming_message_t)); sm->sm_type = type; +#if ENABLE_TIMESHIFT + sm->sm_time = 0; + sm->sm_timeshift = 0; +#endif return sm; } @@ -188,7 +192,11 @@ streaming_msg_clone(streaming_message_t *src) streaming_message_t *dst = malloc(sizeof(streaming_message_t)); streaming_start_t *ss; - dst->sm_type = src->sm_type; + dst->sm_type = src->sm_type; +#if ENABLE_TIMESHIFT + dst->sm_time = src->sm_time; + dst->sm_timeshift = src->sm_timeshift; +#endif switch(src->sm_type) { @@ -202,11 +210,17 @@ streaming_msg_clone(streaming_message_t *src) atomic_add(&ss->ss_refcount, 1); break; + case SMT_SKIP: + dst->sm_data = malloc(sizeof(streaming_skip_t)); + memcpy(dst->sm_data, src->sm_data, sizeof(streaming_skip_t)); + break; + case SMT_SIGNAL_STATUS: dst->sm_data = malloc(sizeof(signal_status_t)); memcpy(dst->sm_data, src->sm_data, sizeof(signal_status_t)); break; + case SMT_SPEED: case SMT_STOP: case SMT_SERVICE_STATUS: case SMT_NOSTART: @@ -264,17 +278,13 @@ streaming_msg_free(streaming_message_t *sm) break; case SMT_STOP: - break; - case SMT_EXIT: - break; - case SMT_SERVICE_STATUS: - break; - case SMT_NOSTART: + case SMT_SPEED: break; + case SMT_SKIP: case SMT_SIGNAL_STATUS: free(sm->sm_data); break; diff --git a/src/subscriptions.c b/src/subscriptions.c index 5dd58379..8dff4acf 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -545,8 +545,6 @@ subscription_dummy_join(const char *id, int first) "Dummy join %s ok", id); } - - /** * */ @@ -635,3 +633,41 @@ subscription_init(void) { gtimer_arm(&every_sec, every_sec_cb, NULL, 1); } + +/** + * Set speed + */ +void +subscription_set_speed ( th_subscription_t *s, int speed ) +{ + streaming_message_t *sm; + service_t *t = s->ths_service; + + pthread_mutex_lock(&t->s_stream_mutex); + + sm = streaming_msg_create_code(SMT_SPEED, speed); + + streaming_target_deliver(s->ths_output, sm); + + pthread_mutex_unlock(&t->s_stream_mutex); +} + +/** + * Set skip + */ +void +subscription_set_skip ( th_subscription_t *s, const streaming_skip_t *skip ) +{ + streaming_message_t *sm; + service_t *t = s->ths_service; + + pthread_mutex_lock(&t->s_stream_mutex); + + sm = streaming_msg_create(SMT_SKIP); + sm->sm_data = malloc(sizeof(streaming_skip_t)); + memcpy(sm->sm_data, skip, sizeof(streaming_skip_t)); + + streaming_target_deliver(s->ths_output, sm); + + pthread_mutex_unlock(&t->s_stream_mutex); +} diff --git a/src/subscriptions.h b/src/subscriptions.h index a73605f3..28b84af8 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -111,6 +111,14 @@ th_subscription_t *subscription_create(int weight, const char *name, void subscription_change_weight(th_subscription_t *s, int weight); +void subscription_set_speed + (th_subscription_t *s, int32_t speed ); + +void subscription_set_skip + (th_subscription_t *s, const streaming_skip_t *skip); + +void subscription_stop(th_subscription_t *s); + void subscription_unlink_service(th_subscription_t *s, int reason); void subscription_dummy_join(const char *id, int first); diff --git a/src/timeshift.c b/src/timeshift.c new file mode 100644 index 00000000..ebbb0c64 --- /dev/null +++ b/src/timeshift.c @@ -0,0 +1,184 @@ +/** + * TV headend - Timeshift + * Copyright (C) 2012 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 . + */ + +#include "tvheadend.h" +#include "streaming.h" +#include "timeshift.h" +#include "timeshift/private.h" +#include "config2.h" +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static int timeshift_index = 0; + +/* + * Intialise global file manager + */ +void timeshift_init ( void ) +{ + timeshift_filemgr_init(); +} + +/* + * Terminate global file manager + */ +void timeshift_term ( void ) +{ + timeshift_filemgr_term(); +} + +/* + * Receive data + */ +static void timeshift_input + ( void *opaque, streaming_message_t *sm ) +{ + timeshift_t *ts = opaque; + + pthread_mutex_lock(&ts->state_mutex); + + /* Control */ + if (sm->sm_type == SMT_SKIP) { + if (ts->state >= TS_LIVE) + timeshift_write_skip(ts->rd_pipe.wr, sm->sm_data); + } else if (sm->sm_type == SMT_SPEED) { + if (ts->state >= TS_LIVE) + timeshift_write_speed(ts->rd_pipe.wr, sm->sm_code); + } + + else { + + /* Start */ + if (sm->sm_type == SMT_START && ts->state == TS_INIT) { + ts->state = TS_LIVE; + } + + /* Pass-thru */ + if (ts->state <= TS_LIVE) { + streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); + } + + /* Buffer to disk */ + if (ts->state >= TS_LIVE) { + sm->sm_time = getmonoclock(); + streaming_target_deliver2(&ts->wr_queue.sq_st, sm); + } else + streaming_msg_free(sm); + + /* Exit/Stop */ + if (sm->sm_type == SMT_EXIT || + (sm->sm_type == SMT_STOP && sm->sm_code == 0)) { + timeshift_write_exit(ts->rd_pipe.wr); + ts->state = TS_EXIT; + } + } + + pthread_mutex_unlock(&ts->state_mutex); +} + +/** + * + */ +void +timeshift_destroy(streaming_target_t *pad) +{ + timeshift_t *ts = (timeshift_t*)pad; + timeshift_file_t *tsf; + streaming_message_t *sm; + + /* Must hold global lock */ + lock_assert(&global_lock); + + /* Ensure the thread exits */ + // Note: this is a workaround for the fact the Q might have been flushed + // in reader thread (VERY unlikely) + sm = streaming_msg_create(SMT_EXIT); + streaming_target_deliver2(&ts->wr_queue.sq_st, sm); + + /* Wait for all threads */ + pthread_join(ts->rd_thread, NULL); + pthread_join(ts->wr_thread, NULL); + pthread_join(ts->rm_thread, NULL); + + /* Shut stuff down */ + streaming_queue_deinit(&ts->wr_queue); + + close(ts->rd_pipe.rd); + close(ts->rd_pipe.wr); + + /* Flush files */ + while ((tsf = TAILQ_FIRST(&ts->files))) + timeshift_filemgr_remove(ts, tsf, 1); + + free(ts->path); + free(ts); +} + +/** + * Create timeshift buffer + * + * max_period of buffer in seconds (0 = unlimited) + * max_size of buffer in bytes (0 = unlimited) + */ +streaming_target_t *timeshift_create + (streaming_target_t *out, time_t max_time) +{ + char buf[512]; + timeshift_t *ts = calloc(1, sizeof(timeshift_t)); + + /* Must hold global lock */ + lock_assert(&global_lock); + + /* Create directories */ + if (timeshift_filemgr_makedirs(timeshift_index, buf, sizeof(buf))) + return NULL; + + /* Setup structure */ + TAILQ_INIT(&ts->files); + ts->output = out; + ts->path = strdup(buf); + ts->max_time = max_time; + ts->state = TS_INIT; + ts->full = 0; + ts->vididx = -1; + ts->id = timeshift_index; + pthread_mutex_init(&ts->rdwr_mutex, NULL); + pthread_mutex_init(&ts->state_mutex, NULL); + + /* Initialise output */ + tvh_pipe(O_NONBLOCK, &ts->rd_pipe); + + /* Initialise input */ + streaming_queue_init(&ts->wr_queue, 0); + streaming_target_init(&ts->input, timeshift_input, ts, 0); + pthread_create(&ts->wr_thread, NULL, timeshift_writer, ts); + pthread_create(&ts->rd_thread, NULL, timeshift_reader, ts); + + /* Update index */ + timeshift_index++; + + return &ts->input; +} diff --git a/src/timeshift.h b/src/timeshift.h new file mode 100644 index 00000000..69574bc5 --- /dev/null +++ b/src/timeshift.h @@ -0,0 +1,30 @@ +/* + * TV headend - Timeshift + * Copyright (C) 2012 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_TIMESHIFT_H__ +#define __TVH_TIMESHIFT_H__ + +void timeshift_init ( void ); +void timeshift_term ( void ); + +streaming_target_t *timeshift_create + (streaming_target_t *out, time_t max_period); + +void timeshift_destroy(streaming_target_t *pad); + +#endif /* __TVH_TIMESHIFT_H__ */ diff --git a/src/timeshift/private.h b/src/timeshift/private.h new file mode 100644 index 00000000..455fd93b --- /dev/null +++ b/src/timeshift/private.h @@ -0,0 +1,139 @@ +/* + * TV headend - Timeshift + * Copyright (C) 2012 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_TIMESHIFT_PRIVATE_H__ +#define __TVH_TIMESHIFT_PRIVATE_H__ + +#define TS_PLAY_BUF 100000 // us to buffer in TX + +/** + * Indexes of import data in the stream + */ +typedef struct timeshift_index +{ + off_t pos; ///< Position in the file + union { + int64_t time; ///< Packet time + void *data; ///< Associated data + }; + TAILQ_ENTRY(timeshift_index) link; ///< List entry +} timeshift_index_t; + +typedef TAILQ_HEAD(timeshift_index_list,timeshift_index) timeshift_index_list_t; + +/** + * Timeshift file + */ +typedef struct timeshift_file +{ + int fd; ///< Write descriptor + char *path; ///< Full path to file + + time_t time; ///< Files coarse timestamp + size_t size; ///< Current file size; + int64_t last; ///< Latest timestamp + + uint8_t bad; ///< File is broken + + int refcount; ///< Reader ref count + + timeshift_index_list_t iframes; ///< I-frame indexing + timeshift_index_list_t sstart; ///< Stream start messages + + TAILQ_ENTRY(timeshift_file) link; ///< List entry +} timeshift_file_t; + +typedef TAILQ_HEAD(timeshift_file_list,timeshift_file) timeshift_file_list_t; + +/** + * + */ +typedef struct timeshift { + // Note: input MUST BE FIRST in struct + streaming_target_t input; ///< Input source + streaming_target_t *output; ///< Output dest + + int id; ///< Reference number + char *path; ///< Directory containing buffer + time_t max_time; ///< Maximum period to shift + + enum { + TS_INIT, + TS_EXIT, + TS_LIVE, + TS_PAUSE, + TS_PLAY, + } state; ///< Play state + pthread_mutex_t state_mutex; ///< Protect state changes + uint8_t full; ///< Buffer is full + + streaming_queue_t wr_queue; ///< Writer queue + pthread_t wr_thread; ///< Writer thread + + pthread_t rd_thread; ///< Reader thread + th_pipe_t rd_pipe; ///< Message passing to reader + + pthread_t rm_thread; ///< Reaper thread + timeshift_file_list_t rm_list; ///< Remove files + + pthread_mutex_t rdwr_mutex; ///< Buffer protection + timeshift_file_list_t files; ///< List of files + + int vididx; ///< Index of (current) video stream + +} timeshift_t; + +/* + * Write functions + */ +ssize_t timeshift_write_start ( int fd, int64_t time, streaming_start_t *ss ); +ssize_t timeshift_write_sigstat ( int fd, int64_t time, signal_status_t *ss ); +ssize_t timeshift_write_packet ( int fd, int64_t time, th_pkt_t *pkt ); +ssize_t timeshift_write_mpegts ( int fd, int64_t time, void *data ); +ssize_t timeshift_write_skip ( int fd, streaming_skip_t *skip ); +ssize_t timeshift_write_speed ( int fd, int speed ); +ssize_t timeshift_write_stop ( int fd, int code ); +ssize_t timeshift_write_exit ( int fd ); +ssize_t timeshift_write_eof ( int fd ); + +void timeshift_writer_flush ( timeshift_t *ts ); + +/* + * Threads + */ +void *timeshift_reader ( void *p ); +void *timeshift_writer ( void *p ); + +/* + * File management + */ +void timeshift_filemgr_init ( void ); +void timeshift_filemgr_term ( void ); +int timeshift_filemgr_makedirs ( int ts_index, char *buf, size_t len ); + +timeshift_file_t *timeshift_filemgr_get + ( timeshift_t *ts, int create ); +timeshift_file_t *timeshift_filemgr_prev + ( timeshift_file_t *ts, int *end, int keep ); +timeshift_file_t *timeshift_filemgr_next + ( timeshift_file_t *ts, int *end, int keep ); +void timeshift_filemgr_remove + ( timeshift_t *ts, timeshift_file_t *tsf, int force ); +void timeshift_filemgr_close ( timeshift_file_t *tsf ); + +#endif /* __TVH_TIMESHIFT_PRIVATE_H__ */ diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c new file mode 100644 index 00000000..ff00ff8b --- /dev/null +++ b/src/timeshift/timeshift_filemgr.c @@ -0,0 +1,306 @@ +/** + * TV headend - Timeshift File Manager + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "streaming.h" +#include "timeshift.h" +#include "timeshift/private.h" +#include "config2.h" +#include "settings.h" + +static int timeshift_reaper_run; +static timeshift_file_list_t timeshift_reaper_list; +static pthread_t timeshift_reaper_thread; +static pthread_mutex_t timeshift_reaper_lock; +static pthread_cond_t timeshift_reaper_cond; + +/* ************************************************************************** + * File reaper thread + * *************************************************************************/ + +static void* timeshift_reaper_callback ( void *p ) +{ + char *dpath; + timeshift_file_t *tsf; + timeshift_index_t *ti; + streaming_message_t *sm; + pthread_mutex_lock(×hift_reaper_lock); + while (timeshift_reaper_run) { + + /* Get next */ + tsf = TAILQ_FIRST(×hift_reaper_list); + if (!tsf) { + pthread_cond_wait(×hift_reaper_cond, ×hift_reaper_lock); + continue; + } + TAILQ_REMOVE(×hift_reaper_list, tsf, link); + pthread_mutex_unlock(×hift_reaper_lock); + +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "remove file %s", tsf->path); +#endif + + /* Remove */ + unlink(tsf->path); + dpath = dirname(tsf->path); + if (rmdir(dpath) == -1) + if (errno != ENOTEMPTY) + tvhlog(LOG_ERR, "timeshift", "failed to remove %s [e=%s]", + dpath, strerror(errno)); + + /* Free memory */ + while ((ti = TAILQ_FIRST(&tsf->iframes))) { + TAILQ_REMOVE(&tsf->iframes, ti, link); + free(ti); + } + while ((ti = TAILQ_FIRST(&tsf->sstart))) { + TAILQ_REMOVE(&tsf->sstart, ti, link); + sm = ti->data; + streaming_msg_free(sm); + free(ti); + } + free(tsf->path); + free(tsf); + } + pthread_mutex_unlock(×hift_reaper_lock); +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "reaper thread exit"); +#endif + return NULL; +} + +static void timeshift_reaper_remove ( timeshift_file_t *tsf ) +{ +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "queue file for removal %s", tsf->path); +#endif + pthread_mutex_lock(×hift_reaper_lock); + TAILQ_INSERT_TAIL(×hift_reaper_list, tsf, link); + pthread_cond_signal(×hift_reaper_cond); + pthread_mutex_unlock(×hift_reaper_lock); +} + +/* ************************************************************************** + * File Handling + * *************************************************************************/ + +/* + * Get root directory + */ +static void timeshift_filemgr_get_root ( char *buf, size_t len ) +{ + const char *path = config_get_timeshift_path(); + if (!path || !*path) + path = hts_settings_get_root(); + snprintf(buf, len, "%s/timeshift", path); +} + +/* + * Create timeshift directories (for a given instance) + */ +int timeshift_filemgr_makedirs ( int index, char *buf, size_t len ) +{ + timeshift_filemgr_get_root(buf, len); + snprintf(buf+strlen(buf), len-strlen(buf), "/%d", index); + return makedirs(buf, 0700); +} + +/* + * Close file + */ +void timeshift_filemgr_close ( timeshift_file_t *tsf ) +{ + ssize_t r = timeshift_write_eof(tsf->fd); + if (r > 0) + tsf->size += r; + close(tsf->fd); + tsf->fd = -1; +} + +/* + * Remove file + */ +void timeshift_filemgr_remove + ( timeshift_t *ts, timeshift_file_t *tsf, int force ) +{ + if (tsf->fd != -1) + close(tsf->fd); + TAILQ_REMOVE(&ts->files, tsf, link); + timeshift_reaper_remove(tsf); +} + +/* + * Get current / new file + */ +timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) +{ + int fd; + struct timespec tp; + timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; + timeshift_index_t *ti; + char path[512]; + + /* Return last file */ + if (!create) + return TAILQ_LAST(&ts->files, timeshift_file_list); + + /* No space */ + if (ts->full) + return NULL; + + /* Store to file */ + clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); + tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); + if (!tsf_tl || tsf_tl->time != tp.tv_sec) { + tsf_hd = TAILQ_FIRST(&ts->files); + + /* Close existing */ + if (tsf_tl && tsf_tl->fd != -1) + timeshift_filemgr_close(tsf_tl); + + /* Check period */ + if (ts->max_time && tsf_hd && tsf_tl) { + time_t d = tsf_tl->time - tsf_hd->time; + if (d > (ts->max_time+5)) { + if (!tsf_hd->refcount) { + timeshift_filemgr_remove(ts, tsf_hd, 0); + } else { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); +#endif + ts->full = 1; + } + } + } + + /* Check size */ + // TODO: need to implement this + + /* Create new file */ + tsf_tmp = NULL; + if (!ts->full) { + snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, tp.tv_sec); +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d create file %s", ts->id, path); +#endif + if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { + tsf_tmp = calloc(1, sizeof(timeshift_file_t)); + tsf_tmp->time = tp.tv_sec; + tsf_tmp->fd = fd; + tsf_tmp->path = strdup(path); + tsf_tmp->refcount = 0; + TAILQ_INIT(&tsf_tmp->iframes); + TAILQ_INIT(&tsf_tmp->sstart); + TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); + + /* Copy across last start message */ + if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_list))) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d copy smt_start to new file", + ts->id); +#endif + timeshift_index_t *ti2 = calloc(1, sizeof(timeshift_index_t)); + ti2->pos = ti->pos; + ti2->data = streaming_msg_clone(ti->data); + TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); + } + } + } + tsf_tl = tsf_tmp; + } + + return tsf_tl; +} + +timeshift_file_t *timeshift_filemgr_next + ( timeshift_file_t *tsf, int *end, int keep ) +{ + timeshift_file_t *nxt = TAILQ_NEXT(tsf, link); + if (!nxt && end) *end = 1; + if (!nxt && keep) return tsf; + tsf->refcount--; + if (nxt) + nxt->refcount++; + return nxt; +} + +timeshift_file_t *timeshift_filemgr_prev + ( timeshift_file_t *tsf, int *end, int keep ) +{ + timeshift_file_t *nxt = TAILQ_PREV(tsf, timeshift_file_list, link); + if (!nxt && end) *end = 1; + if (!nxt && keep) return tsf; + tsf->refcount--; + if (nxt) + nxt->refcount++; + return nxt; +} + +/* ************************************************************************** + * Setup / Teardown + * *************************************************************************/ + +/* + * Initialise global structures + */ +void timeshift_filemgr_init ( void ) +{ + char path[512]; + + /* Try to remove any rubbish left from last run */ + timeshift_filemgr_get_root(path, sizeof(path)); + rmtree(path); + + /* Start the reaper thread */ + timeshift_reaper_run = 1; + pthread_mutex_init(×hift_reaper_lock, NULL); + pthread_cond_init(×hift_reaper_cond, NULL); + TAILQ_INIT(×hift_reaper_list); + pthread_create(×hift_reaper_thread, NULL, + timeshift_reaper_callback, NULL); +} + +/* + * Terminate + */ +void timeshift_filemgr_term ( void ) +{ + char path[512]; + + /* Wait for thread */ + pthread_mutex_lock(×hift_reaper_lock); + timeshift_reaper_run = 0; + pthread_cond_signal(×hift_reaper_cond); + pthread_mutex_unlock(×hift_reaper_lock); + pthread_join(timeshift_reaper_thread, NULL); + + /* Remove the lot */ + timeshift_filemgr_get_root(path, sizeof(path)); + rmtree(path); +} + + diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c new file mode 100644 index 00000000..c3e57d70 --- /dev/null +++ b/src/timeshift/timeshift_reader.c @@ -0,0 +1,490 @@ +/** + * TV headend - Timeshift Reader + * Copyright (C) 2012 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 . + */ + +#include "tvheadend.h" +#include "streaming.h" +#include "timeshift.h" +#include "timeshift/private.h" + +#include +#include +#include +#include +#include +#include +#include + +/* ************************************************************************** + * File Reading + * *************************************************************************/ + +static ssize_t _read_pktbuf ( int fd, pktbuf_t **pktbuf ) +{ + ssize_t r, cnt = 0; + size_t sz; + + /* Size */ + r = read(fd, &sz, sizeof(sz)); + if (r < 0) return -1; + if (r != sizeof(sz)) return 0; + cnt += r; + + /* Empty */ + if (!sz) { + *pktbuf = NULL; + return cnt; + } + + /* Data */ + *pktbuf = pktbuf_alloc(NULL, sz); + r = read(fd, (*pktbuf)->pb_data, sz); + if (r != sz) { + free((*pktbuf)->pb_data); + free(*pktbuf); + return r < 0 ? -1 : 0; + } + cnt += r; + + return cnt; +} + + +static ssize_t _read_msg ( int fd, streaming_message_t **sm ) +{ + ssize_t r, cnt = 0; + size_t sz; + streaming_message_type_t type; + int64_t time; + void *data; + int code; + + /* Clear */ + *sm = NULL; + + /* Size */ + r = read(fd, &sz, sizeof(sz)); + if (r < 0) return -1; + if (r != sizeof(sz)) return 0; + cnt += r; + + /* EOF */ + if (sz == 0) return cnt; + + /* Type */ + r = read(fd, &type, sizeof(type)); + if (r < 0) return -1; + if (r != sizeof(type)) return 0; + cnt += r; + + /* Time */ + r = read(fd, &time, sizeof(time)); + if (r < 0) return -1; + if (r != sizeof(time)) return 0; + cnt += r; + + /* Adjust size */ + sz -= sizeof(type) + sizeof(time); + cnt += sz; + + /* Standard messages */ + switch (type) { + + /* Unhandled */ + case SMT_START: + case SMT_NOSTART: + case SMT_SERVICE_STATUS: + break; + + /* Code */ + case SMT_STOP: + case SMT_EXIT: + case SMT_SPEED: + if (sz != sizeof(code)) return -1; + r = read(fd, &code, sz); + if (r != sz) { + if (r < 0) return -1; + return 0; + } + *sm = streaming_msg_create_code(type, code); + break; + + /* Data */ + case SMT_SKIP: + case SMT_SIGNAL_STATUS: + case SMT_MPEGTS: + case SMT_PACKET: + data = malloc(sz); + r = read(fd, data, sz); + if (r != sz) { + free(data); + if (r < 0) return -1; + return 0; + } + if (type == SMT_PACKET) { + th_pkt_t *pkt = data; + pkt->pkt_payload = pkt->pkt_header = NULL; + *sm = streaming_msg_create_pkt(pkt); + r = _read_pktbuf(fd, &pkt->pkt_header); + if (r < 0) { + streaming_msg_free(*sm); + return r; + } + cnt += r; + r = _read_pktbuf(fd, &pkt->pkt_payload); + if (r < 0) { + streaming_msg_free(*sm); + return r; + } + cnt += r; + } else { + *sm = streaming_msg_create_data(type, data); + } + (*sm)->sm_time = time; + break; + } + + /* OK */ + return cnt; +} + +/* ************************************************************************** + * Thread + * *************************************************************************/ + +/* + * Timeshift thread + */ +void *timeshift_reader ( void *p ) +{ + timeshift_t *ts = p; + int efd, nfds, end, fd = -1, run = 1, wait = -1; + off_t cur_off = 0; + int cur_speed = 100, keyframe_mode = 0; + int64_t pause_time = 0, play_time = 0, last_time = 0, tx_time = 0; + int64_t now, deliver; + streaming_message_t *sm = NULL, *ctrl; + timeshift_file_t *cur_file = NULL, *tsi_file = NULL; + timeshift_index_t *tsi = NULL; + + /* Poll */ + struct epoll_event ev; + efd = epoll_create(1); + ev.events = EPOLLIN; + ev.data.fd = ts->rd_pipe.rd; + epoll_ctl(efd, EPOLL_CTL_ADD, ev.data.fd, &ev); + + /* Output */ + while (run) { + + /* Wait for data */ + if(wait) + nfds = epoll_wait(efd, &ev, 1, wait); + else + nfds = 0; + wait = -1; + end = 0; + + /* Control */ + pthread_mutex_lock(&ts->state_mutex); + if (nfds == 1) { + if (_read_msg(ev.data.fd, &ctrl) > 0) { + + /* Exit */ + if (ctrl->sm_type == SMT_EXIT) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d read exit request", ts->id); +#endif + run = 0; + streaming_msg_free(ctrl); + + /* Speed */ + // TODO: currently just pause + } else if (ctrl->sm_type == SMT_SPEED) { + int speed = ctrl->sm_code; + int keyframe; + + /* Bound it */ + if (speed > 3200) speed = 3200; + if (speed < -3200) speed = -3200; + + /* Process */ + if (cur_speed != speed) { + + /* Live playback */ + if (ts->state == TS_LIVE) { + + /* Reject */ + if (speed >= 100) { + tvhlog(LOG_DEBUG, "timeshift", "ts %d reject 1x+ in live mode", + ts->id); + speed = 100; + + /* Set position */ + } else { + tvhlog(LOG_DEBUG, "timeshift", "ts %d enter timeshift mode", + ts->id); + timeshift_writer_flush(ts); + pthread_mutex_lock(&ts->rdwr_mutex); + if ((cur_file = timeshift_filemgr_get(ts, 0))) { + cur_off = cur_file->size; + pause_time = cur_file->last; + last_time = pause_time; + } + pthread_mutex_unlock(&ts->rdwr_mutex); + } + + /* Buffer playback */ + } else if (ts->state == TS_PLAY) { + pause_time = last_time; + + /* Paused */ + } else { + } + + /* Check keyframe mode */ + keyframe = (speed < 0) || (speed > 400); + if (keyframe != keyframe_mode) { + tvhlog(LOG_DEBUG, "timeshift", "using keyframe mode? %s", + keyframe ? "yes" : "no"); + keyframe_mode = keyframe; + if (keyframe) { + tsi = NULL; + tsi_file = cur_file; + } + } + + /* Update */ + play_time = getmonoclock(); + cur_speed = speed; + if (speed != 100 || ts->state != TS_LIVE) + ts->state = speed == 0 ? TS_PAUSE : TS_PLAY; + tvhlog(LOG_DEBUG, "timeshift", "ts %d change speed %d", + ts->id, speed); + } + + /* Send on the message */ + ctrl->sm_code = speed; + streaming_target_deliver2(ts->output, ctrl); + + /* Skip */ + } else { + streaming_msg_free(ctrl); + } + + ctrl = NULL; + } + } + + /* Done */ + if (!run || ts->state != TS_PLAY || !cur_file) { + pthread_mutex_unlock(&ts->state_mutex); + continue; + } + + /* Calculate delivery time */ + now = getmonoclock(); + deliver = (now - play_time) + TS_PLAY_BUF; + deliver = (deliver * cur_speed) / 100; + deliver = (deliver + pause_time); + + /* Rewind or Fast forward (i-frame only) */ + if (keyframe_mode) { + wait = 0; + + /* Find next index */ + if (cur_speed < 0) { + if (!tsi) { + TAILQ_FOREACH_REVERSE(tsi, &tsi_file->iframes, + timeshift_index_list, link) { + if (tsi->time < last_time) break; + } + } + } else { + if (!tsi) { + TAILQ_FOREACH(tsi, &tsi_file->iframes, link) { + if (tsi->time > last_time) break; + } + } + } + + /* Next file */ + if (!tsi) { + if (fd != -1) + close(fd); + wait = 0; // immediately cycle around + fd = -1; + if (cur_speed < 0) + tsi_file = timeshift_filemgr_prev(tsi_file, &end, 1); + else + tsi_file = timeshift_filemgr_next(tsi_file, &end, 0); + } + + /* Deliver */ + if (tsi && (((cur_speed < 0) && (tsi->time >= deliver)) || + ((cur_speed > 0) && (tsi->time <= deliver)))) { + + /* Keep delivery to 5fps max */ + if ((now - tx_time) >= 200000) { + + /* Open */ + if (fd == -1) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", + ts->id, tsi_file->path); +#endif + fd = open(tsi_file->path, O_RDONLY); + } + + /* Read */ + off_t ret = lseek(fd, tsi->pos, SEEK_SET); + assert(ret == tsi->pos); + ssize_t r = _read_msg(fd, &sm); + + /* Send */ + if (r > 0) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, + ts->id, sm->sm_time); +#endif + sm->sm_timeshift = now - sm->sm_time; + streaming_target_deliver2(ts->output, sm); + cur_file = tsi_file; + cur_off = tsi->pos + r; + last_time = sm->sm_time; + tx_time = now; + sm = NULL; + } else { + wait = -1; + close(fd); + fd = -1; + } + } + + /* Next index */ + if (cur_speed < 0) + tsi = TAILQ_PREV(tsi, timeshift_index_list, link); + else + tsi = TAILQ_NEXT(tsi, link); + + /* Not yet! */ + } else if (tsi) { + if (cur_speed > 0) + wait = (tsi->time - deliver) / 1000; + else + wait = (deliver - tsi->time) / 1000; + if (wait == 0) wait = 1; + } + + /* Full frame delivery */ + } else { + + /* Open file */ + if (fd == -1) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", + ts->id, cur_file->path); +#endif + fd = open(cur_file->path, O_RDONLY); + if (cur_off) lseek(fd, cur_off, SEEK_SET); + } + + /* Process */ + pthread_mutex_lock(&ts->rdwr_mutex); + end = 1; + while (cur_file && cur_off < cur_file->size) { + + /* Read msg */ + if (!sm) { + ssize_t r = _read_msg(fd, &sm); + assert(r != -1); + + /* Incomplete */ + if (r == 0) { + lseek(fd, cur_off, SEEK_SET); + break; + } + + cur_off += r; + + /* Special case - EOF */ + if (r == sizeof(size_t) || cur_off > cur_file->size) { + close(fd); + wait = 0; // immediately cycle around + cur_off = 0; // reset + fd = -1; + cur_file = timeshift_filemgr_next(cur_file, NULL, 0); + break; + } + } + + assert(sm); + end = 0; + + /* Deliver */ + if (sm->sm_time <= deliver) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, + ts->id, sm->sm_time); +#endif + sm->sm_timeshift = now - sm->sm_time; + streaming_target_deliver2(ts->output, sm); + tx_time = now; + last_time = sm->sm_time; + sm = NULL; + wait = 0; + } else { + wait = (sm->sm_time - deliver) / 1000; + if (wait == 0) wait = 1; + break; + } + } + } + + /* Terminate */ + if (!cur_file || end) { + + /* Back to live */ + if (cur_speed > 0) { + tvhlog(LOG_DEBUG, "timeshift", "ts %d eob revert to live mode", ts->id); + ts->state = TS_LIVE; + cur_speed = 100; + ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); + streaming_target_deliver2(ts->output, ctrl); + + /* Pause */ + } else if (cur_speed < 0) { + tvhlog(LOG_DEBUG, "timeshift", "ts %d sob pause stream", ts->id); + cur_speed = 0; + ts->state = TS_PAUSE; + ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); + streaming_target_deliver2(ts->output, ctrl); + } + } + + pthread_mutex_unlock(&ts->rdwr_mutex); + pthread_mutex_unlock(&ts->state_mutex); + } + + /* Cleanup */ + if (sm) streaming_msg_free(sm); +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d exit reader thread", ts->id); +#endif + + return NULL; +} diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c new file mode 100644 index 00000000..a66e6e1b --- /dev/null +++ b/src/timeshift/timeshift_writer.c @@ -0,0 +1,327 @@ +/** + * TV headend - Timeshift Write Handler + * Copyright (C) 2012 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 . + */ + +#include "tvheadend.h" +#include "streaming.h" +#include "timeshift.h" +#include "timeshift/private.h" + +#include +#include +#include +#include +#include +#include +#include + +/* ************************************************************************** + * File Writing + * *************************************************************************/ + +/* + * Write data (retry on EAGAIN) + */ +static ssize_t _write + ( int fd, const void *buf, size_t count ) +{ + ssize_t r; + size_t n = 0; + while ( n < count ) { + r = write(fd, buf+n, count-n); + if (r == -1) { + if (errno == EAGAIN) + continue; + else + return -1; + } + n += r; + } + return count == n ? n : -1; +} + +/* + * Write message + */ +static ssize_t _write_msg + ( int fd, streaming_message_type_t type, int64_t time, + const void *buf, size_t len ) +{ + size_t len2 = len + sizeof(type) + sizeof(time); + ssize_t err, ret; + ret = err = _write(fd, &len2, sizeof(len2)); + if (err < 0) return err; + err = _write(fd, &type, sizeof(type)); + if (err < 0) return err; + ret += err; + err = _write(fd, &time, sizeof(time)); + if (err < 0) return err; + ret += err; + if (len) { + err = _write(fd, buf, len); + if (err < 0) return err; + ret += err; + } + return ret; +} + +/* + * Write packet buffer + */ +static int _write_pktbuf ( int fd, pktbuf_t *pktbuf ) +{ + ssize_t ret, err; + if (pktbuf) { + ret = err = _write(fd, &pktbuf->pb_size, sizeof(pktbuf->pb_size)); + if (err < 0) return err; + err = _write(fd, pktbuf->pb_data, pktbuf->pb_size); + if (err < 0) return err; + ret += err; + } else { + size_t sz = 0; + ret = _write(fd, &sz, sizeof(sz)); + } + return ret; +} + +/* + * Write signal status + */ +ssize_t timeshift_write_sigstat + ( int fd, int64_t time, signal_status_t *sigstat ) +{ + return _write_msg(fd, SMT_SIGNAL_STATUS, time, sigstat, + sizeof(signal_status_t)); +} + +/* + * Write packet + */ +ssize_t timeshift_write_packet ( int fd, int64_t time, th_pkt_t *pkt ) +{ + ssize_t ret = 0, err; + ret = err = _write_msg(fd, SMT_PACKET, time, pkt, sizeof(th_pkt_t)); + if (err <= 0) return err; + err = _write_pktbuf(fd, pkt->pkt_header); + if (err <= 0) return err; + ret += err; + err = _write_pktbuf(fd, pkt->pkt_payload); + if (err <= 0) return err; + ret += err; + return ret; +} + +/* + * Write MPEGTS data + */ +ssize_t timeshift_write_mpegts ( int fd, int64_t time, void *data ) +{ + return _write_msg(fd, SMT_MPEGTS, time, data, 188); +} + +/* + * Write skip message + */ +ssize_t timeshift_write_skip ( int fd, streaming_skip_t *skip ) +{ + return _write_msg(fd, SMT_SKIP, 0, skip, sizeof(streaming_skip_t)); +} + +/* + * Write speed message + */ +ssize_t timeshift_write_speed ( int fd, int speed ) +{ + return _write_msg(fd, SMT_SPEED, 0, &speed, sizeof(speed)); +} + +/* + * Stop + */ +ssize_t timeshift_write_stop ( int fd, int code ) +{ + return _write_msg(fd, SMT_STOP, 0, &code, sizeof(code)); +} + +/* + * Exit + */ +ssize_t timeshift_write_exit ( int fd ) +{ + int code = 0; + return _write_msg(fd, SMT_EXIT, 0, &code, sizeof(code)); +} + +/* + * Write end of file (special internal message) + */ +ssize_t timeshift_write_eof ( int fd ) +{ + size_t sz = 0; + return _write(fd, &sz, sizeof(sz)); +} + +/* ************************************************************************** + * Thread + * *************************************************************************/ + +static inline ssize_t _process_msg0 + ( timeshift_t *ts, timeshift_file_t *tsf, streaming_message_t **smp ) +{ + int i; + ssize_t err; + streaming_start_t *ss; + streaming_message_t *sm = *smp; + if (sm->sm_type == SMT_START) { + err = 0; + timeshift_index_t *ti = calloc(1, sizeof(timeshift_index_t)); + ti->pos = tsf->size; + ti->data = sm; + *smp = NULL; + TAILQ_INSERT_TAIL(&tsf->sstart, ti, link); + + /* Update video index */ + ss = sm->sm_data; + for (i = 0; i < ss->ss_num_components; i++) + if (SCT_ISVIDEO(ss->ss_components[i].ssc_type)) + ts->vididx = ss->ss_components[i].ssc_index; + } else if (sm->sm_type == SMT_SIGNAL_STATUS) + err = timeshift_write_sigstat(tsf->fd, sm->sm_time, sm->sm_data); + else if (sm->sm_type == SMT_PACKET) { + err = timeshift_write_packet(tsf->fd, sm->sm_time, sm->sm_data); + if (err > 0) { + th_pkt_t *pkt = sm->sm_data; + + /* Index video iframes */ + if (pkt->pkt_componentindex == ts->vididx && + pkt->pkt_frametype == PKT_I_FRAME) { + timeshift_index_t *ti = calloc(1, sizeof(timeshift_index_t)); + ti->pos = tsf->size; + ti->time = sm->sm_time; + TAILQ_INSERT_TAIL(&tsf->iframes, ti, link); + } + } + } else if (sm->sm_type == SMT_MPEGTS) + err = timeshift_write_mpegts(tsf->fd, sm->sm_time, sm->sm_data); + else + err = 0; + + /* OK */ + if (err > 0) { + tsf->last = sm->sm_time; + tsf->size += err; + } + return err; +} + +static void _process_msg + ( timeshift_t *ts, streaming_message_t *sm, int *run ) +{ + int err; + timeshift_file_t *tsf; + + /* Process */ + switch (sm->sm_type) { + + /* Terminate */ + case SMT_EXIT: + if (run) *run = 0; + break; + case SMT_STOP: + if (sm->sm_code == 0 && run) + *run = 0; + break; + + /* Timeshifting */ + case SMT_SKIP: + case SMT_SPEED: + break; + + /* Status */ + case SMT_NOSTART: + case SMT_SERVICE_STATUS: + break; + + /* Store */ + case SMT_SIGNAL_STATUS: + case SMT_START: + case SMT_MPEGTS: + case SMT_PACKET: + pthread_mutex_lock(&ts->rdwr_mutex); + if ((tsf = timeshift_filemgr_get(ts, 1)) && (tsf->fd != -1)) { + if ((err = _process_msg0(ts, tsf, &sm)) < 0) { + timeshift_filemgr_close(tsf); + tsf->bad = 1; + ts->full = 1; ///< Stop any more writing + } + } + pthread_mutex_unlock(&ts->rdwr_mutex); + break; + } + + /* Next */ + if (sm) + streaming_msg_free(sm); +} + +void *timeshift_writer ( void *aux ) +{ + int run = 1; + timeshift_t *ts = aux; + streaming_queue_t *sq = &ts->wr_queue; + streaming_message_t *sm; + + pthread_mutex_lock(&sq->sq_mutex); + + while (run) { + + /* Get message */ + sm = TAILQ_FIRST(&sq->sq_queue); + if (sm == NULL) { + pthread_cond_wait(&sq->sq_cond, &sq->sq_mutex); + continue; + } + TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); + pthread_mutex_unlock(&sq->sq_mutex); + + _process_msg(ts, sm, &run); + + pthread_mutex_lock(&sq->sq_mutex); + } + + pthread_mutex_unlock(&sq->sq_mutex); + return NULL; +} + +/* ************************************************************************** + * Utilities + * *************************************************************************/ + +void timeshift_writer_flush ( timeshift_t *ts ) + +{ + streaming_message_t *sm; + streaming_queue_t *sq = &ts->wr_queue; + + pthread_mutex_lock(&sq->sq_mutex); + while ((sm = TAILQ_FIRST(&sq->sq_queue))) { + TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); + _process_msg(ts, sm, NULL); + } + pthread_mutex_unlock(&sq->sq_mutex); +} + diff --git a/src/tvheadend.h b/src/tvheadend.h index c578a6ed..2f6adcf9 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -210,6 +210,24 @@ typedef struct signal_status { int unc; /* uncorrected blocks */ } signal_status_t; +/** + * Streaming skip + */ +typedef struct streaming_skip +{ + enum { + SMT_SKIP_REL_TIME, + SMT_SKIP_ABS_TIME, + SMT_SKIP_REL_SIZE, + SMT_SKIP_ABS_SIZE + } type; + union { + off_t size; + time_t time; + }; +} streaming_skip_t; + + /** * A streaming pad generates data. * It has one or more streaming targets attached to it. @@ -234,6 +252,7 @@ TAILQ_HEAD(streaming_message_queue, streaming_message); * Streaming messages types */ typedef enum { + /** * Packet with data. * @@ -291,6 +310,17 @@ typedef enum { * Internal message to exit receiver */ SMT_EXIT, + + /** + * Set stream speed + */ + SMT_SPEED, + + /** + * Skip the stream + */ + SMT_SKIP, + } streaming_message_type_t; #define SMT_TO_MASK(x) (1 << ((unsigned int)x)) @@ -326,6 +356,10 @@ typedef enum { typedef struct streaming_message { TAILQ_ENTRY(streaming_message) sm_link; streaming_message_type_t sm_type; +#if ENABLE_TIMESHIFT + int64_t sm_time; + uint64_t sm_timeshift; +#endif union { void *sm_data; int sm_code; diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index 42eb6082..a7e4d968 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -103,6 +103,49 @@ tvheadend.miscconf = function() { if (tvheadend.capabilities.indexOf('imagecache') == -1) imagecachePanel.hide(); + /* **************************************************************** + * Timeshift + * ***************************************************************/ + + var timeshiftPath = new Ext.form.TextField({ + fieldLabel : 'Temp. storage path', + name : 'timeshiftpath', + allowBlank : true, + width : 400 + }); + + var timeshiftPeriod = new Ext.form.NumberField({ + fieldLabel : 'Max period (minutes, per stream)', + name : 'timeshiftperiod', + allowBlank : false, + width : 400 + }); + + var timeshiftPeriodU = new Ext.form.Checkbox({ + fieldLabel : '(unlimited)', + name : 'timeshiftperiod_unlimited', + allowBlank : false, + width : 400 + }); + timeshiftPeriodU.on('check', function(e, c) { + timeshiftPeriod.setDisabled(c); + }); + + var timeshiftSize = new Ext.form.NumberField({ + fieldLabel : 'Max size (MB, global)', + name : 'timeshiftsize', + allowBlank : false, + width : 400 + }); + + var timeshiftFields = new Ext.form.FieldSet({ + title : 'Timeshift', + width : 700, + autoHeight : true, + collapsible : true, + items : [ timeshiftPath, timeshiftPeriod, timeshiftPeriodU ]//, timeshiftSize ] + }); + /* **************************************************************** * Form * ***************************************************************/ @@ -127,7 +170,7 @@ tvheadend.miscconf = function() { border : false, bodyStyle : 'padding:15px', labelAlign : 'left', - labelWidth : 150, + labelWidth : 200, waitMsgTarget : true, reader : confreader, layout : 'form', @@ -149,6 +192,14 @@ tvheadend.miscconf = function() { op : 'loadSettings' }, success : function(form, action) { + v = parseInt(timeshiftPeriod.getValue()); + if (v == 4294967295) { + timeshiftPeriodU.setValue(true); + timeshiftPeriod.setValue(""); + timeshiftPeriod.setDisabled(true); // TODO: this isn't working + } else { + timeshiftPeriod.setValue(v / 60); + } confpanel.enable(); } }); diff --git a/src/webui/webui.c b/src/webui/webui.c index 0257010e..3e1e525c 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -244,6 +244,8 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, } break; + case SMT_SKIP: + case SMT_SPEED: case SMT_SIGNAL_STATUS: break; From 4e67a1345ed8c3baa3ae1d6338f0c3cca3b3eaaf Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 12:14:46 +0000 Subject: [PATCH 242/503] timeshift: fix memory leak in reader I had failed to zero the ref count after reading from disk, this will garauntee that the refcount never actually reaches zero and is therefore the pkt is leaked. --- src/timeshift/timeshift_reader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index c3e57d70..a8f30065 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -137,7 +137,8 @@ static ssize_t _read_msg ( int fd, streaming_message_t **sm ) } if (type == SMT_PACKET) { th_pkt_t *pkt = data; - pkt->pkt_payload = pkt->pkt_header = NULL; + pkt->pkt_payload = pkt->pkt_header = NULL; + pkt->pkt_refcount = 0; *sm = streaming_msg_create_pkt(pkt); r = _read_pktbuf(fd, &pkt->pkt_header); if (r < 0) { From 551e5ff8f940cbb196c1921a7a97442f24186a5e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 21 Dec 2012 21:50:05 +0000 Subject: [PATCH 243/503] timeshift: fix use after free() bug. --- src/timeshift.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/timeshift.c b/src/timeshift.c index ebbb0c64..11657513 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -56,6 +56,7 @@ void timeshift_term ( void ) static void timeshift_input ( void *opaque, streaming_message_t *sm ) { + int exit = 0; timeshift_t *ts = opaque; pthread_mutex_lock(&ts->state_mutex); @@ -81,6 +82,11 @@ static void timeshift_input streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); } + /* Check for exit */ + if (sm->sm_type == SMT_EXIT || + (sm->sm_type == SMT_STOP && sm->sm_code == 0)) + exit = 1; + /* Buffer to disk */ if (ts->state >= TS_LIVE) { sm->sm_time = getmonoclock(); @@ -89,8 +95,7 @@ static void timeshift_input streaming_msg_free(sm); /* Exit/Stop */ - if (sm->sm_type == SMT_EXIT || - (sm->sm_type == SMT_STOP && sm->sm_code == 0)) { + if (exit) { timeshift_write_exit(ts->rd_pipe.wr); ts->state = TS_EXIT; } From 869f95ee0f93c90d2382550678b82cddecb0126f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 3 Dec 2012 14:59:47 +0000 Subject: [PATCH 244/503] timeshift: Remove redundant (and faulty) code. This appears to have been left in place from an older implementation. Ultimately the call to pthread_join will operate on uninit'd memory and could in certain circumstances cause a crash. Thanks goes to Seri Al-Najjar for spotting this. --- src/timeshift.c | 1 - src/timeshift/private.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/timeshift.c b/src/timeshift.c index 11657513..2114442e 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -126,7 +126,6 @@ timeshift_destroy(streaming_target_t *pad) /* Wait for all threads */ pthread_join(ts->rd_thread, NULL); pthread_join(ts->wr_thread, NULL); - pthread_join(ts->rm_thread, NULL); /* Shut stuff down */ streaming_queue_deinit(&ts->wr_queue); diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 455fd93b..22022ac1 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -88,9 +88,6 @@ typedef struct timeshift { pthread_t rd_thread; ///< Reader thread th_pipe_t rd_pipe; ///< Message passing to reader - pthread_t rm_thread; ///< Reaper thread - timeshift_file_list_t rm_list; ///< Remove files - pthread_mutex_t rdwr_mutex; ///< Buffer protection timeshift_file_list_t files; ///< List of files From a300377036b50c220c32a15f9bf1734cc9ee14ad Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 29 Nov 2012 10:55:48 +0100 Subject: [PATCH 245/503] timeshift: Split timeshift_index_t in two separate datatypes. --- src/timeshift/private.h | 45 ++++++++++++++++++------------- src/timeshift/timeshift_filemgr.c | 18 ++++++------- src/timeshift/timeshift_reader.c | 6 ++--- src/timeshift/timeshift_writer.c | 4 +-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 22022ac1..1f79593e 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -24,36 +24,45 @@ /** * Indexes of import data in the stream */ -typedef struct timeshift_index +typedef struct timeshift_index_iframe { - off_t pos; ///< Position in the file - union { - int64_t time; ///< Packet time - void *data; ///< Associated data - }; - TAILQ_ENTRY(timeshift_index) link; ///< List entry -} timeshift_index_t; + off_t pos; ///< Position in the file + int64_t time; ///< Packet time + TAILQ_ENTRY(timeshift_index_iframe) link; ///< List entry +} timeshift_index_iframe_t; -typedef TAILQ_HEAD(timeshift_index_list,timeshift_index) timeshift_index_list_t; +typedef TAILQ_HEAD(timeshift_index_iframe_list,timeshift_index_iframe) timeshift_index_iframe_list_t; + +/** + * Indexes of import data in the stream + */ +typedef struct timeshift_index_data +{ + off_t pos; ///< Position in the file + void *data; ///< Associated data + TAILQ_ENTRY(timeshift_index_data) link; ///< List entry +} timeshift_index_data_t; + +typedef TAILQ_HEAD(timeshift_index_data_list,timeshift_index_data) timeshift_index_data_list_t; /** * Timeshift file */ typedef struct timeshift_file { - int fd; ///< Write descriptor - char *path; ///< Full path to file + int fd; ///< Write descriptor + char *path; ///< Full path to file - time_t time; ///< Files coarse timestamp - size_t size; ///< Current file size; - int64_t last; ///< Latest timestamp + time_t time; ///< Files coarse timestamp + size_t size; ///< Current file size; + int64_t last; ///< Latest timestamp - uint8_t bad; ///< File is broken + uint8_t bad; ///< File is broken - int refcount; ///< Reader ref count + int refcount; ///< Reader ref count - timeshift_index_list_t iframes; ///< I-frame indexing - timeshift_index_list_t sstart; ///< Stream start messages + timeshift_index_iframe_list_t iframes; ///< I-frame indexing + timeshift_index_data_list_t sstart; ///< Stream start messages TAILQ_ENTRY(timeshift_file) link; ///< List entry } timeshift_file_t; diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index ff00ff8b..01c2dd15 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -46,7 +46,8 @@ static void* timeshift_reaper_callback ( void *p ) { char *dpath; timeshift_file_t *tsf; - timeshift_index_t *ti; + timeshift_index_iframe_t *ti; + timeshift_index_data_t *tid; streaming_message_t *sm; pthread_mutex_lock(×hift_reaper_lock); while (timeshift_reaper_run) { @@ -77,11 +78,11 @@ static void* timeshift_reaper_callback ( void *p ) TAILQ_REMOVE(&tsf->iframes, ti, link); free(ti); } - while ((ti = TAILQ_FIRST(&tsf->sstart))) { - TAILQ_REMOVE(&tsf->sstart, ti, link); - sm = ti->data; + while ((tid = TAILQ_FIRST(&tsf->sstart))) { + TAILQ_REMOVE(&tsf->sstart, tid, link); + sm = tid->data; streaming_msg_free(sm); - free(ti); + free(tid); } free(tsf->path); free(tsf); @@ -161,7 +162,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; - timeshift_index_t *ti; + timeshift_index_data_t *ti; char path[512]; /* Return last file */ @@ -218,13 +219,12 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ - if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_list))) { + if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d copy smt_start to new file", ts->id); #endif - timeshift_index_t *ti2 = calloc(1, sizeof(timeshift_index_t)); - ti2->pos = ti->pos; + timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index a8f30065..25968f24 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -180,7 +180,7 @@ void *timeshift_reader ( void *p ) int64_t now, deliver; streaming_message_t *sm = NULL, *ctrl; timeshift_file_t *cur_file = NULL, *tsi_file = NULL; - timeshift_index_t *tsi = NULL; + timeshift_index_iframe_t *tsi = NULL; /* Poll */ struct epoll_event ev; @@ -311,7 +311,7 @@ void *timeshift_reader ( void *p ) if (cur_speed < 0) { if (!tsi) { TAILQ_FOREACH_REVERSE(tsi, &tsi_file->iframes, - timeshift_index_list, link) { + timeshift_index_iframe_list, link) { if (tsi->time < last_time) break; } } @@ -378,7 +378,7 @@ void *timeshift_reader ( void *p ) /* Next index */ if (cur_speed < 0) - tsi = TAILQ_PREV(tsi, timeshift_index_list, link); + tsi = TAILQ_PREV(tsi, timeshift_index_iframe_list, link); else tsi = TAILQ_NEXT(tsi, link); diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index a66e6e1b..0fb3129f 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -188,7 +188,7 @@ static inline ssize_t _process_msg0 streaming_message_t *sm = *smp; if (sm->sm_type == SMT_START) { err = 0; - timeshift_index_t *ti = calloc(1, sizeof(timeshift_index_t)); + timeshift_index_data_t *ti = calloc(1, sizeof(timeshift_index_data_t)); ti->pos = tsf->size; ti->data = sm; *smp = NULL; @@ -209,7 +209,7 @@ static inline ssize_t _process_msg0 /* Index video iframes */ if (pkt->pkt_componentindex == ts->vididx && pkt->pkt_frametype == PKT_I_FRAME) { - timeshift_index_t *ti = calloc(1, sizeof(timeshift_index_t)); + timeshift_index_iframe_t *ti = calloc(1, sizeof(timeshift_index_iframe_t)); ti->pos = tsf->size; ti->time = sm->sm_time; TAILQ_INSERT_TAIL(&tsf->iframes, ti, link); From 4b86d4210c65d6da052d6e35016050b1e8cbdd05 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 29 Nov 2012 11:02:00 +0100 Subject: [PATCH 246/503] timeshift: Use more specific type for timeshift_index_data_t.data. --- src/timeshift/private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 1f79593e..11b53bac 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -39,7 +39,7 @@ typedef TAILQ_HEAD(timeshift_index_iframe_list,timeshift_index_iframe) timeshift typedef struct timeshift_index_data { off_t pos; ///< Position in the file - void *data; ///< Associated data + streaming_message_t *data; ///< Associated data TAILQ_ENTRY(timeshift_index_data) link; ///< List entry } timeshift_index_data_t; From fa4841b725e4d984d559490d42c81bcda34fed12 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 21 Dec 2012 21:43:29 +0000 Subject: [PATCH 247/503] timeshift: reworked the configuration general config stuff has now been removed and a new timeshift tab has been created --- src/config2.c | 45 +-------- src/config2.h | 12 --- src/htsp_server.c | 3 +- src/timeshift.c | 60 ++++++++++++ src/timeshift.h | 9 ++ src/timeshift/private.h | 1 + src/timeshift/timeshift_filemgr.c | 6 +- src/webui/extjs.c | 80 ++++++++++++++++ src/webui/static/app/config.js | 51 ---------- src/webui/static/app/timeshift.js | 152 ++++++++++++++++++++++++++++++ src/webui/static/app/tvheadend.js | 3 + 11 files changed, 313 insertions(+), 109 deletions(-) create mode 100644 src/webui/static/app/timeshift.js diff --git a/src/config2.c b/src/config2.c index 05bb7a34..cfeeb127 100644 --- a/src/config2.c +++ b/src/config2.c @@ -26,24 +26,11 @@ static htsmsg_t *config; void config_init ( void ) { - int save = 0; - uint32_t u32; - config = hts_settings_load("config"); if (!config) { tvhlog(LOG_DEBUG, "config", "no configuration, loading defaults"); config = htsmsg_create_map(); } - - /* Defaults */ - if (htsmsg_get_u32(config, "timeshiftperiod", &u32)) - save |= config_set_timeshift_period(0); - if (htsmsg_get_u32(config, "timeshiftsize", &u32)) - save |= config_set_timeshift_size(0); - - /* Save defaults */ - if (save) - config_save(); } void config_save ( void ) @@ -67,6 +54,7 @@ static int _config_set_str ( const char *fld, const char *val ) return 0; } +#if 0 static int _config_set_u32 ( const char *fld, uint32_t val ) { uint32_t u32; @@ -78,6 +66,7 @@ static int _config_set_u32 ( const char *fld, uint32_t val ) } return 0; } +#endif const char *config_get_language ( void ) { @@ -98,33 +87,3 @@ int config_set_muxconfpath ( const char *path ) { return _config_set_str("muxconfpath", path); } - -const char *config_get_timeshift_path ( void ) -{ - return htsmsg_get_str(config, "timeshiftpath"); -} - -int config_set_timeshift_path ( const char *path ) -{ - return _config_set_str("timeshiftpath", path); -} - -uint32_t config_get_timeshift_period ( void ) -{ - return htsmsg_get_u32_or_default(config, "timeshiftperiod", 0); -} - -int config_set_timeshift_period ( uint32_t period ) -{ - return _config_set_u32("timeshiftperiod", period); -} - -uint32_t config_get_timeshift_size ( void ) -{ - return htsmsg_get_u32_or_default(config, "timeshiftsize", 0); -} - -int config_set_timeshift_size ( uint32_t size ) -{ - return _config_set_u32("timeshiftsize", size); -} diff --git a/src/config2.h b/src/config2.h index 7ba28e0a..cd68e306 100644 --- a/src/config2.h +++ b/src/config2.h @@ -36,16 +36,4 @@ const char *config_get_language ( void ); int config_set_language ( const char *str ) __attribute__((warn_unused_result)); -const char *config_get_timeshift_path ( void ); -int config_set_timeshift_path ( const char *str ) - __attribute__((warn_unused_result)); - -uint32_t config_get_timeshift_period ( void ); -int config_set_timeshift_period ( uint32_t val ) - __attribute__((warn_unused_result)); - -uint32_t config_get_timeshift_size ( void ); -int config_set_timeshift_size ( uint32_t val ) - __attribute__((warn_unused_result)); - #endif /* __TVH_CONFIG__H__ */ diff --git a/src/htsp_server.c b/src/htsp_server.c index 0c1db9b1..77556d6e 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1264,7 +1264,8 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) #if ENABLE_TIMESHIFT timeshiftPeriod = htsmsg_get_u32_or_default(in, "timeshiftPeriod", 0); - timeshiftPeriod = MIN(timeshiftPeriod, config_get_timeshift_period()); + if (!timeshift_unlimited_period) + timeshiftPeriod = MIN(timeshiftPeriod, timeshift_max_period); #endif /* diff --git a/src/timeshift.c b/src/timeshift.c index 2114442e..14cee0d1 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -34,12 +34,51 @@ static int timeshift_index = 0; +int timeshift_enabled; +int timeshift_ondemand; +char *timeshift_path; +int timeshift_unlimited_period; +uint32_t timeshift_max_period; +int timeshift_unlimited_size; +size_t timeshift_max_size; + /* * Intialise global file manager */ void timeshift_init ( void ) { + htsmsg_t *m; + const char *str; + uint32_t u32; + timeshift_filemgr_init(); + + /* Defaults */ + timeshift_enabled = 0; // Disabled + timeshift_ondemand = 0; // Permanent + timeshift_path = NULL; // setting dir + timeshift_unlimited_period = 0; + timeshift_max_period = 3600; // 1Hr + timeshift_unlimited_size = 0; + timeshift_max_size = 10000 * (size_t)1048576; // 10G + + /* Load settings */ + if ((m = hts_settings_load("timeshift/config"))) { + if (!htsmsg_get_u32(m, "enabled", &u32)) + timeshift_enabled = u32 ? 1 : 0; + if (!htsmsg_get_u32(m, "ondemand", &u32)) + timeshift_ondemand = u32 ? 1 : 0; + if ((str = htsmsg_get_str(m, "path"))) + timeshift_path = strdup(str); + if (!htsmsg_get_u32(m, "unlimited_period", &u32)) + timeshift_unlimited_period = u32 ? 1 : 0; + htsmsg_get_u32(m, "max_period", ×hift_max_period); + if (!htsmsg_get_u32(m, "unlimited_size", &u32)) + timeshift_unlimited_size = u32 ? 1 : 0; + if (!htsmsg_get_u32(m, "max_size", &u32)) + timeshift_max_size = 1048576LL * u32; + htsmsg_destroy(m); + } } /* @@ -50,6 +89,26 @@ void timeshift_term ( void ) timeshift_filemgr_term(); } +/* + * Save settings + */ +void timeshift_save ( void ) +{ + htsmsg_t *m; + + m = htsmsg_create_map(); + htsmsg_add_u32(m, "enabled", timeshift_enabled); + htsmsg_add_u32(m, "ondemand", timeshift_ondemand); + if (timeshift_path) + htsmsg_add_str(m, "path", timeshift_path); + htsmsg_add_u32(m, "unlimited_period", timeshift_unlimited_period); + htsmsg_add_u32(m, "max_period", timeshift_max_period); + htsmsg_add_u32(m, "unlimited_size", timeshift_unlimited_size); + htsmsg_add_u32(m, "max_size", timeshift_max_size / 1048576); + + hts_settings_save(m, "timeshift/config"); +} + /* * Receive data */ @@ -169,6 +228,7 @@ streaming_target_t *timeshift_create ts->full = 0; ts->vididx = -1; ts->id = timeshift_index; + ts->ondemand = timeshift_ondemand; pthread_mutex_init(&ts->rdwr_mutex, NULL); pthread_mutex_init(&ts->state_mutex, NULL); diff --git a/src/timeshift.h b/src/timeshift.h index 69574bc5..eeb6baad 100644 --- a/src/timeshift.h +++ b/src/timeshift.h @@ -19,8 +19,17 @@ #ifndef __TVH_TIMESHIFT_H__ #define __TVH_TIMESHIFT_H__ +extern int timeshift_enabled; +extern int timeshift_ondemand; +extern char *timeshift_path; +extern int timeshift_unlimited_period; +extern uint32_t timeshift_max_period; +extern int timeshift_unlimited_size; +extern size_t timeshift_max_size; + void timeshift_init ( void ); void timeshift_term ( void ); +void timeshift_save ( void ); streaming_target_t *timeshift_create (streaming_target_t *out, time_t max_period); diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 11b53bac..133eb695 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -80,6 +80,7 @@ typedef struct timeshift { int id; ///< Reference number char *path; ///< Directory containing buffer time_t max_time; ///< Maximum period to shift + int ondemand; ///< Whether this is an on-demand timeshift enum { TS_INIT, diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 01c2dd15..3562393f 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -111,13 +111,15 @@ static void timeshift_reaper_remove ( timeshift_file_t *tsf ) /* * Get root directory + * + * TODO: should this be fixed on startup? */ static void timeshift_filemgr_get_root ( char *buf, size_t len ) { - const char *path = config_get_timeshift_path(); + const char *path = timeshift_path; if (!path || !*path) path = hts_settings_get_root(); - snprintf(buf, len, "%s/timeshift", path); + snprintf(buf, len, "%s/timeshift/temp", path); } /* diff --git a/src/webui/extjs.c b/src/webui/extjs.c index f68dff87..2b6fff45 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -48,6 +48,7 @@ #include "lang_codes.h" #include "subscriptions.h" #include "imagecache.h" +#include "timeshift.h" /** * @@ -130,6 +131,9 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) extjs_load(hq, "static/app/iptv.js"); #if ENABLE_V4L extjs_load(hq, "static/app/v4l.js"); +#endif +#if ENABLE_TIMESHIFT + extjs_load(hq, "static/app/timeshift.js"); #endif extjs_load(hq, "static/app/chconf.js"); extjs_load(hq, "static/app/epg.js"); @@ -2034,6 +2038,79 @@ extjs_capabilities(http_connection_t *hc, const char *remain, void *opaque) return 0; } +/** + * + */ +#if ENABLE_TIMESHIFT +static int +extjs_timeshift(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + const char *op = http_arg_get(&hc->hc_req_args, "op"); + htsmsg_t *out, *m; + const char *str; + + if(op == NULL) + return 400; + + pthread_mutex_lock(&global_lock); + + if(http_access_verify(hc, ACCESS_ADMIN)) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_UNAUTHORIZED; + } + + pthread_mutex_unlock(&global_lock); + + /* Basic settings (not the advanced schedule) */ + if(!strcmp(op, "loadSettings")) { + pthread_mutex_lock(&global_lock); + m = htsmsg_create_map(); + htsmsg_add_u32(m, "timeshift_enabled", timeshift_enabled); + htsmsg_add_u32(m, "timeshift_ondemand", timeshift_ondemand); + if (timeshift_path) + htsmsg_add_str(m, "timeshift_path", timeshift_path); + htsmsg_add_u32(m, "timeshift_unlimited_period", timeshift_unlimited_period); + htsmsg_add_u32(m, "timeshift_max_period", timeshift_max_period); + htsmsg_add_u32(m, "timeshift_unlimited_size", timeshift_unlimited_size); + htsmsg_add_u32(m, "timeshift_max_size", timeshift_max_size / 1048576); + pthread_mutex_unlock(&global_lock); + out = json_single_record(m, "config"); + + /* Save settings */ + } else if (!strcmp(op, "saveSettings") ) { + pthread_mutex_lock(&global_lock); + timeshift_enabled = http_arg_get(&hc->hc_req_args, "timeshift_enabled") ? 1 : 0; + timeshift_ondemand = http_arg_get(&hc->hc_req_args, "timeshift_ondemand") ? 1 : 0; + if ((str = http_arg_get(&hc->hc_req_args, "timeshift_path"))) { + if (timeshift_path) + free(timeshift_path); + timeshift_path = strdup(str); + } + timeshift_unlimited_period = http_arg_get(&hc->hc_req_args, "timeshift_unlimited_period") ? 1 : 0; + if ((str = http_arg_get(&hc->hc_req_args, "timeshift_max_period"))) + timeshift_max_period = (uint32_t)atol(str); + timeshift_unlimited_size = http_arg_get(&hc->hc_req_args, "timeshift_unlimited_size") ? 1 : 0; + if ((str = http_arg_get(&hc->hc_req_args, "timeshift_max_size"))) + timeshift_max_size = atol(str) * 1048576LL; + timeshift_save(); + pthread_mutex_unlock(&global_lock); + + out = htsmsg_create_map(); + htsmsg_add_u32(out, "success", 1); + + } else { + return HTTP_STATUS_BAD_REQUEST; + } + + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); + http_output_content(hc, "text/x-json; charset=UTF-8"); + + return 0; +} +#endif + /** * WEB user interface */ @@ -2064,6 +2141,9 @@ extjs_start(void) http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN); http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN); http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN); +#if ENABLE_TIMESHIFT + http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN); +#endif #if ENABLE_LINUXDVB extjs_start_dvb(); diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index a7e4d968..b71a4a5d 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -103,49 +103,6 @@ tvheadend.miscconf = function() { if (tvheadend.capabilities.indexOf('imagecache') == -1) imagecachePanel.hide(); - /* **************************************************************** - * Timeshift - * ***************************************************************/ - - var timeshiftPath = new Ext.form.TextField({ - fieldLabel : 'Temp. storage path', - name : 'timeshiftpath', - allowBlank : true, - width : 400 - }); - - var timeshiftPeriod = new Ext.form.NumberField({ - fieldLabel : 'Max period (minutes, per stream)', - name : 'timeshiftperiod', - allowBlank : false, - width : 400 - }); - - var timeshiftPeriodU = new Ext.form.Checkbox({ - fieldLabel : '(unlimited)', - name : 'timeshiftperiod_unlimited', - allowBlank : false, - width : 400 - }); - timeshiftPeriodU.on('check', function(e, c) { - timeshiftPeriod.setDisabled(c); - }); - - var timeshiftSize = new Ext.form.NumberField({ - fieldLabel : 'Max size (MB, global)', - name : 'timeshiftsize', - allowBlank : false, - width : 400 - }); - - var timeshiftFields = new Ext.form.FieldSet({ - title : 'Timeshift', - width : 700, - autoHeight : true, - collapsible : true, - items : [ timeshiftPath, timeshiftPeriod, timeshiftPeriodU ]//, timeshiftSize ] - }); - /* **************************************************************** * Form * ***************************************************************/ @@ -192,14 +149,6 @@ tvheadend.miscconf = function() { op : 'loadSettings' }, success : function(form, action) { - v = parseInt(timeshiftPeriod.getValue()); - if (v == 4294967295) { - timeshiftPeriodU.setValue(true); - timeshiftPeriod.setValue(""); - timeshiftPeriod.setDisabled(true); // TODO: this isn't working - } else { - timeshiftPeriod.setValue(v / 60); - } confpanel.enable(); } }); diff --git a/src/webui/static/app/timeshift.js b/src/webui/static/app/timeshift.js new file mode 100644 index 00000000..7f27ba4f --- /dev/null +++ b/src/webui/static/app/timeshift.js @@ -0,0 +1,152 @@ +tvheadend.timeshift = function() { + + /* **************************************************************** + * Data + * ***************************************************************/ + + var confreader = new Ext.data.JsonReader( + { + root: 'config' + }, + [ + 'timeshift_enabled', 'timeshift_ondemand', + 'timeshift_path', + 'timeshift_unlimited_period', 'timeshift_max_period', + 'timeshift_unlimited_size', 'timeshift_max_size' + ] + ); + + /* **************************************************************** + * Fields + * ***************************************************************/ + + var timeshiftEnabled = new Ext.form.Checkbox({ + fieldLabel: 'Enabled', + name: 'timeshift_enabled', + width: 300 + }); + + var timeshiftOndemand = new Ext.form.Checkbox({ + fieldLabel: 'On-Demand', + name: 'timeshift_ondemand', + width: 300 + }); + + var timeshiftPath = new Ext.form.TextField({ + fieldLabel: 'Storage Path', + name: 'timeshift_path', + allowBlank: true, + width: 300 + }); + + var timeshiftMaxPeriod = new Ext.form.NumberField({ + fieldLabel: 'Max. Period (mins)', + name: 'timeshift_max_period', + allowBlank: false, + width: 300 + }); + + var timeshiftUnlPeriod = new Ext.form.Checkbox({ + fieldLabel: ' unlimited', + name: 'timeshift_unlimited_period', + Width: 300 + }); + + var timeshiftMaxSize = new Ext.form.NumberField({ + fieldLabel: 'Max. Size (MB)', + name: 'timeshift_max_size', + allowBlank: false, + width: 300 + }); + + var timeshiftUnlSize = new Ext.form.Checkbox({ + fieldLabel: ' unlimited', + name: 'timeshift_unlimited_size', + Width: 300 + }); + + /* **************************************************************** + * Events + * ***************************************************************/ + + timeshiftUnlPeriod.on('check', function(e, c){ + timeshiftMaxPeriod.setDisabled(c); + }); + timeshiftUnlSize.on('check', function(e, c){ + timeshiftMaxSize.setDisabled(c); + }); + + /* **************************************************************** + * Form + * ***************************************************************/ + + var saveButton = new Ext.Button({ + text : "Save configuration", + tooltip : 'Save changes made to configuration below', + iconCls : 'save', + handler : saveChanges + }); + + var helpButton = new Ext.Button({ + text : 'Help', + handler : function() { + new tvheadend.help('Timeshift Configuration', 'config_timeshift.html'); + } + }); + + var confpanel = new Ext.FormPanel({ + title : 'Timeshift', + iconCls : 'clock', + border : false, + bodyStyle : 'padding:15px', + labelAlign : 'left', + labelWidth : 150, + waitMsgTarget : true, + reader : confreader, + layout : 'form', + defaultType : 'textfield', + autoHeight : true, + items : [ + timeshiftEnabled, timeshiftOndemand, + timeshiftPath, + timeshiftMaxPeriod, timeshiftUnlPeriod, + timeshiftMaxSize, timeshiftUnlSize + ], + tbar : [ saveButton, '->', helpButton ] + }); + + /* **************************************************************** + * Load/Save + * ***************************************************************/ + + confpanel.on('render', function() { + confpanel.getForm().load({ + url: 'timeshift', + params: { + 'op': 'loadSettings' + }, + success: function() { + confpanel.enable(); + timeshiftMaxPeriod.setDisabled(timeshiftUnlPeriod.getValue()); + timeshiftMaxSize.setDisabled(timeshiftUnlSize.getValue()); + } + }); + }); + + function saveChanges() { + confpanel.getForm().submit({ + url : 'timeshift', + params : { + op : 'saveSettings', + }, + waitMsg : 'Saving Data...', + success : function(form, action) { + }, + failure : function(form, action) { + Ext.Msg.alert('Save failed', action.result.errormsg); + } + }); + } + + return confpanel; +} diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index d1754f49..96bb66a3 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -270,6 +270,9 @@ function accessUpdate(o) { tvheadend.confpanel.add(new tvheadend.cwceditor); tvheadend.confpanel.add(new tvheadend.capmteditor); } + if (tvheadend.capabilities.indexOf('timeshift') != -1) { + tvheadend.confpanel.add(new tvheadend.timeshift); + } tvheadend.confpanel.doLayout(); } From acebb13f153fb0b53b6ccb94f440aee82fecc61e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 23 Dec 2012 21:33:17 +0000 Subject: [PATCH 248/503] timeshift: Added on-demand buffer mode. In this mode data will only be written to disk when paused and anything in the buffer prior to the current play point will immediately be removed. This implies that rewinding is never possible in this mode (though FF is) and trying to do so will result in the buffer playback being paused. --- src/timeshift.c | 6 ++---- src/timeshift/private.h | 1 + src/timeshift/timeshift_filemgr.c | 20 ++++++++++++++++++-- src/timeshift/timeshift_reader.c | 16 +++++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/timeshift.c b/src/timeshift.c index 14cee0d1..afd4237a 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -147,7 +147,7 @@ static void timeshift_input exit = 1; /* Buffer to disk */ - if (ts->state >= TS_LIVE) { + if ((ts->state > TS_LIVE) || (!ts->ondemand && (ts->state == TS_LIVE))) { sm->sm_time = getmonoclock(); streaming_target_deliver2(&ts->wr_queue.sq_st, sm); } else @@ -170,7 +170,6 @@ void timeshift_destroy(streaming_target_t *pad) { timeshift_t *ts = (timeshift_t*)pad; - timeshift_file_t *tsf; streaming_message_t *sm; /* Must hold global lock */ @@ -193,8 +192,7 @@ timeshift_destroy(streaming_target_t *pad) close(ts->rd_pipe.wr); /* Flush files */ - while ((tsf = TAILQ_FIRST(&ts->files))) - timeshift_filemgr_remove(ts, tsf, 1); + timeshift_filemgr_flush(ts, NULL); free(ts->path); free(ts); diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 133eb695..ed64a12f 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -141,6 +141,7 @@ timeshift_file_t *timeshift_filemgr_next ( timeshift_file_t *ts, int *end, int keep ); void timeshift_filemgr_remove ( timeshift_t *ts, timeshift_file_t *tsf, int force ); +void timeshift_filemgr_flush ( timeshift_t *ts, timeshift_file_t *end ); void timeshift_filemgr_close ( timeshift_file_t *tsf ); #endif /* __TVH_TIMESHIFT_PRIVATE_H__ */ diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 3562393f..44571048 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -117,9 +117,12 @@ static void timeshift_reaper_remove ( timeshift_file_t *tsf ) static void timeshift_filemgr_get_root ( char *buf, size_t len ) { const char *path = timeshift_path; - if (!path || !*path) + if (!path || !*path) { path = hts_settings_get_root(); - snprintf(buf, len, "%s/timeshift/temp", path); + snprintf(buf, len, "%s/timeshift/buffer", path); + } else { + snprintf(buf, len, "%s/buffer", path); + } } /* @@ -156,6 +159,18 @@ void timeshift_filemgr_remove timeshift_reaper_remove(tsf); } +/* + * Flush all files + */ +void timeshift_filemgr_flush ( timeshift_t *ts, timeshift_file_t *end ) +{ + timeshift_file_t *tsf; + while ((tsf = TAILQ_FIRST(&ts->files))) { + if (tsf == end) break; + timeshift_filemgr_remove(ts, tsf, 1); + } +} + /* * Get current / new file */ @@ -216,6 +231,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; + tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 25968f24..ab57cdff 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -29,6 +29,8 @@ #include #include +//#define TSHFT_TRACE + /* ************************************************************************** * File Reading * *************************************************************************/ @@ -223,6 +225,10 @@ void *timeshift_reader ( void *p ) if (speed > 3200) speed = 3200; if (speed < -3200) speed = -3200; + /* Ignore negative */ + if (ts->ondemand && (speed < 0)) + speed = 0; + /* Process */ if (cur_speed != speed) { @@ -241,7 +247,7 @@ void *timeshift_reader ( void *p ) ts->id); timeshift_writer_flush(ts); pthread_mutex_lock(&ts->rdwr_mutex); - if ((cur_file = timeshift_filemgr_get(ts, 0))) { + if ((cur_file = timeshift_filemgr_get(ts, ts->ondemand))) { cur_off = cur_file->size; pause_time = cur_file->last; last_time = pause_time; @@ -467,6 +473,10 @@ void *timeshift_reader ( void *p ) ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); + /* Flush ALL files */ + if (ts->ondemand) + timeshift_filemgr_flush(ts, NULL); + /* Pause */ } else if (cur_speed < 0) { tvhlog(LOG_DEBUG, "timeshift", "ts %d sob pause stream", ts->id); @@ -475,6 +485,10 @@ void *timeshift_reader ( void *p ) ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); } + + /* Flush unwanted */ + } else if (ts->ondemand && cur_file) { + timeshift_filemgr_flush(ts, cur_file); } pthread_mutex_unlock(&ts->rdwr_mutex); From 88a9d96039abe77545247191eedc3a02e17ae0e8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 28 Dec 2012 22:14:06 +0000 Subject: [PATCH 249/503] timeshift: Reworked the reader engine to common up things and prepare for seek support. --- src/timeshift/timeshift_reader.c | 295 ++++++++++++++++--------------- src/tvheadend.h | 2 +- 2 files changed, 153 insertions(+), 144 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index ab57cdff..cf4c88c0 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -165,6 +165,68 @@ static ssize_t _read_msg ( int fd, streaming_message_t **sm ) return cnt; } +/* ************************************************************************** + * Utilities + * *************************************************************************/ + +static int _timeshift_skip + ( timeshift_t *ts, int64_t req_time, int64_t cur_time, + timeshift_file_t *cur_file, timeshift_file_t **new_file, + timeshift_index_iframe_t **iframe ) +{ + timeshift_index_iframe_t *tsi = *iframe; + timeshift_file_t *tsf = cur_file; + int64_t sec = req_time / 1000000; + int back = (req_time < cur_time) ? 1 : 0; + int end = 0; + + /* Coarse search */ + if (!tsi) { + while (tsf && !end) { + if (back) { + if ((tsf->time <= sec) && + (tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) + break; + tsf = timeshift_filemgr_prev(tsf, &end, 1); + } else { + if ((tsf->time >= sec) && + (tsi = TAILQ_FIRST(&tsf->iframes))) + break; + tsf = timeshift_filemgr_next(tsf, &end, 0); + } + tsi = NULL; + } + } + + /* Fine search */ + if (back) { + while (!end && tsf && tsi && (tsi->time > req_time)) { + tsi = TAILQ_PREV(tsi, timeshift_index_iframe_list, link); + while (!end && tsf && !tsi) { + if ((tsf = timeshift_filemgr_prev(tsf, &end, 1))) + tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list); + } + } + } else { + while (!end && tsf && tsi && (tsi->time < req_time)) { + tsi = TAILQ_NEXT(tsi, link); + while (!end && tsf && !tsi) { + if ((tsf = timeshift_filemgr_next(tsf, &end, 0))) + tsi = TAILQ_FIRST(&tsf->iframes); + } + } + } + + /* End */ + if (!tsf || !tsi) + end = 1; + + /* Done */ + *new_file = tsf; + *iframe = end ? NULL : tsi; + return end; +} + /* ************************************************************************** * Thread * *************************************************************************/ @@ -176,12 +238,12 @@ void *timeshift_reader ( void *p ) { timeshift_t *ts = p; int efd, nfds, end, fd = -1, run = 1, wait = -1; + timeshift_file_t *cur_file = NULL; off_t cur_off = 0; int cur_speed = 100, keyframe_mode = 0; - int64_t pause_time = 0, play_time = 0, last_time = 0, tx_time = 0; - int64_t now, deliver; + int64_t pause_time = 0, play_time = 0, last_time = 0; + int64_t now, deliver, skip_time; streaming_message_t *sm = NULL, *ctrl; - timeshift_file_t *cur_file = NULL, *tsi_file = NULL; timeshift_index_iframe_t *tsi = NULL; /* Poll */ @@ -199,8 +261,10 @@ void *timeshift_reader ( void *p ) nfds = epoll_wait(efd, &ev, 1, wait); else nfds = 0; - wait = -1; - end = 0; + wait = -1; + end = 0; + skip_time = 0; + now = getmonoclock(); /* Control */ pthread_mutex_lock(&ts->state_mutex); @@ -216,7 +280,6 @@ void *timeshift_reader ( void *p ) streaming_msg_free(ctrl); /* Speed */ - // TODO: currently just pause } else if (ctrl->sm_type == SMT_SPEED) { int speed = ctrl->sm_code; int keyframe; @@ -270,8 +333,7 @@ void *timeshift_reader ( void *p ) keyframe ? "yes" : "no"); keyframe_mode = keyframe; if (keyframe) { - tsi = NULL; - tsi_file = cur_file; + tsi = NULL; } } @@ -288,7 +350,11 @@ void *timeshift_reader ( void *p ) ctrl->sm_code = speed; streaming_target_deliver2(ts->output, ctrl); - /* Skip */ + /* Skip/Seek */ + } else if (ctrl->sm_type == SMT_SKIP) { + // TODO: implement this + + /* Ignore */ } else { streaming_msg_free(ctrl); } @@ -303,165 +369,108 @@ void *timeshift_reader ( void *p ) continue; } + /* File processing lock */ + pthread_mutex_lock(&ts->rdwr_mutex); + /* Calculate delivery time */ - now = getmonoclock(); deliver = (now - play_time) + TS_PLAY_BUF; deliver = (deliver * cur_speed) / 100; deliver = (deliver + pause_time); - /* Rewind or Fast forward (i-frame only) */ - if (keyframe_mode) { - wait = 0; + /* Determine next packet */ + if (!sm) { - /* Find next index */ - if (cur_speed < 0) { - if (!tsi) { - TAILQ_FOREACH_REVERSE(tsi, &tsi_file->iframes, - timeshift_index_iframe_list, link) { - if (tsi->time < last_time) break; - } - } - } else { - if (!tsi) { - TAILQ_FOREACH(tsi, &tsi_file->iframes, link) { - if (tsi->time > last_time) break; - } - } - } + /* Rewind or Fast forward (i-frame only) */ + if (skip_time || keyframe_mode) { + timeshift_file_t *tsf = NULL; + time_t req_time; - /* Next file */ - if (!tsi) { - if (fd != -1) + /* Time */ + if (!skip_time) + req_time = last_time + ((cur_speed < 0) ? -1 : 1); + else + req_time = skip_time; + + /* Find */ + end = _timeshift_skip(ts, req_time, last_time, + cur_file, &tsf, &tsi); + + /* File changed (close) */ + if ((tsf != cur_file) && (fd != -1)) { close(fd); - wait = 0; // immediately cycle around - fd = -1; - if (cur_speed < 0) - tsi_file = timeshift_filemgr_prev(tsi_file, &end, 1); - else - tsi_file = timeshift_filemgr_next(tsi_file, &end, 0); - } - - /* Deliver */ - if (tsi && (((cur_speed < 0) && (tsi->time >= deliver)) || - ((cur_speed > 0) && (tsi->time <= deliver)))) { - - /* Keep delivery to 5fps max */ - if ((now - tx_time) >= 200000) { - - /* Open */ - if (fd == -1) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", - ts->id, tsi_file->path); -#endif - fd = open(tsi_file->path, O_RDONLY); - } - - /* Read */ - off_t ret = lseek(fd, tsi->pos, SEEK_SET); - assert(ret == tsi->pos); - ssize_t r = _read_msg(fd, &sm); - - /* Send */ - if (r > 0) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, - ts->id, sm->sm_time); -#endif - sm->sm_timeshift = now - sm->sm_time; - streaming_target_deliver2(ts->output, sm); - cur_file = tsi_file; - cur_off = tsi->pos + r; - last_time = sm->sm_time; - tx_time = now; - sm = NULL; - } else { - wait = -1; - close(fd); - fd = -1; - } + fd = -1; } - /* Next index */ - if (cur_speed < 0) - tsi = TAILQ_PREV(tsi, timeshift_index_iframe_list, link); - else - tsi = TAILQ_NEXT(tsi, link); - - /* Not yet! */ - } else if (tsi) { - if (cur_speed > 0) - wait = (tsi->time - deliver) / 1000; - else - wait = (deliver - tsi->time) / 1000; - if (wait == 0) wait = 1; + /* Position */ + cur_file = tsf; + if (tsi) + cur_off = tsi->pos; } - /* Full frame delivery */ - } else { + /* Find packet */ + if (cur_file && !end) { - /* Open file */ - if (fd == -1) { + /* Open file */ + if (fd == -1) { #ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", - ts->id, cur_file->path); + tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", + ts->id, cur_file->path); #endif - fd = open(cur_file->path, O_RDONLY); + fd = open(cur_file->path, O_RDONLY); + } if (cur_off) lseek(fd, cur_off, SEEK_SET); - } - - /* Process */ - pthread_mutex_lock(&ts->rdwr_mutex); - end = 1; - while (cur_file && cur_off < cur_file->size) { /* Read msg */ - if (!sm) { - ssize_t r = _read_msg(fd, &sm); - assert(r != -1); - - /* Incomplete */ - if (r == 0) { - lseek(fd, cur_off, SEEK_SET); - break; - } + ssize_t r = _read_msg(fd, &sm); + assert(r != -1); +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d read msg %p (%ld)", + ts->id, sm, r); +#endif + /* Incomplete */ + if (r == 0) + lseek(fd, cur_off, SEEK_SET); + else cur_off += r; - /* Special case - EOF */ - if (r == sizeof(size_t) || cur_off > cur_file->size) { - close(fd); - wait = 0; // immediately cycle around - cur_off = 0; // reset - fd = -1; - cur_file = timeshift_filemgr_next(cur_file, NULL, 0); - break; - } - } - - assert(sm); - end = 0; - - /* Deliver */ - if (sm->sm_time <= deliver) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, - ts->id, sm->sm_time); -#endif - sm->sm_timeshift = now - sm->sm_time; - streaming_target_deliver2(ts->output, sm); - tx_time = now; - last_time = sm->sm_time; - sm = NULL; - wait = 0; - } else { - wait = (sm->sm_time - deliver) / 1000; - if (wait == 0) wait = 1; - break; + /* Special case - EOF */ + if (r == sizeof(size_t) || cur_off > cur_file->size) { + close(fd); + fd = -1; + cur_file = timeshift_filemgr_next(cur_file, NULL, 0); + cur_off = 0; // reset + wait = 0; } } } + /* Deliver */ + if (sm && (skip_time || + (((cur_speed < 0) && (sm->sm_time >= deliver)) || + ((cur_speed > 0) && (sm->sm_time <= deliver))))) { + +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, + ts->id, sm->sm_time); +#endif + sm->sm_timeshift = now - sm->sm_time; + streaming_target_deliver2(ts->output, sm); + last_time = sm->sm_time; + sm = NULL; + wait = 0; + } else if (sm) { + if (cur_speed > 0) + wait = (sm->sm_time - deliver) / 1000; + else + wait = (deliver - sm->sm_time) / 1000; + if (wait == 0) wait = 1; +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d wait %d", + ts->id, wait); +#endif + } + /* Terminate */ if (!cur_file || end) { diff --git a/src/tvheadend.h b/src/tvheadend.h index 2f6adcf9..a6a9a0ab 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -223,7 +223,7 @@ typedef struct streaming_skip } type; union { off_t size; - time_t time; + int64_t time; }; } streaming_skip_t; From 564340b32ddc9bf8e255e467df01d143dd2138db Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 5 Jan 2013 19:11:45 +0000 Subject: [PATCH 250/503] timeshift: add new style capability check. --- src/main.c | 3 +++ src/timeshift.c | 2 +- src/timeshift.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 6cff04cb..c546d7a1 100644 --- a/src/main.c +++ b/src/main.c @@ -128,6 +128,9 @@ const tvh_caps_t tvheadend_capabilities[] = { #endif #if ENABLE_IMAGECACHE { "imagecache", &imagecache_enabled }, +#endif +#if ENABLE_TIMESHIFT + { "timeshift", ×hift_enabled }, #endif { NULL, NULL } }; diff --git a/src/timeshift.c b/src/timeshift.c index afd4237a..2abe6678 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -34,7 +34,7 @@ static int timeshift_index = 0; -int timeshift_enabled; +uint32_t timeshift_enabled; int timeshift_ondemand; char *timeshift_path; int timeshift_unlimited_period; diff --git a/src/timeshift.h b/src/timeshift.h index eeb6baad..5281e8b5 100644 --- a/src/timeshift.h +++ b/src/timeshift.h @@ -19,7 +19,7 @@ #ifndef __TVH_TIMESHIFT_H__ #define __TVH_TIMESHIFT_H__ -extern int timeshift_enabled; +extern uint32_t timeshift_enabled; extern int timeshift_ondemand; extern char *timeshift_path; extern int timeshift_unlimited_period; From 2235b869ae2cf60e17fdddb966bea235670f445f Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Tue, 8 Jan 2013 23:13:51 +0100 Subject: [PATCH 251/503] timeshift: implemented skip --- src/timeshift/timeshift_reader.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index cf4c88c0..423d42ac 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -245,6 +245,7 @@ void *timeshift_reader ( void *p ) int64_t now, deliver, skip_time; streaming_message_t *sm = NULL, *ctrl; timeshift_index_iframe_t *tsi = NULL; + streaming_skip_t *skip; /* Poll */ struct epoll_event ev; @@ -264,6 +265,7 @@ void *timeshift_reader ( void *p ) wait = -1; end = 0; skip_time = 0; + skip = NULL; now = getmonoclock(); /* Control */ @@ -352,14 +354,27 @@ void *timeshift_reader ( void *p ) /* Skip/Seek */ } else if (ctrl->sm_type == SMT_SKIP) { - // TODO: implement this - + skip = (streaming_skip_t *) ctrl->sm_data; + switch (skip->type) { + case SMT_SKIP_REL_TIME: + skip_time = last_time + skip->time; + break; + case SMT_SKIP_ABS_TIME: + skip_time = skip->time; // Wrong - need to use starttime of video too + break; + case SMT_SKIP_REL_SIZE: + case SMT_SKIP_ABS_SIZE: + tvhlog(LOG_DEBUG, "timeshift", "unsupported skip type: %d", skip->type); + break; + default: + tvhlog(LOG_ERR, "timeshift", "invalid skip type: %d", skip->type); + } + if (!skip_time) + streaming_msg_free(ctrl); /* Ignore */ } else { streaming_msg_free(ctrl); } - - ctrl = NULL; } } @@ -395,6 +410,12 @@ void *timeshift_reader ( void *p ) end = _timeshift_skip(ts, req_time, last_time, cur_file, &tsf, &tsi); + /* Adjust skip time to actual */ + if (skip_time) { + skip->time += (tsi->time - skip_time); + streaming_target_deliver2(ts->output, ctrl); + } + /* File changed (close) */ if ((tsf != cur_file) && (fd != -1)) { close(fd); From 86c4a969f72a3dcfa79e2ef82db823936d15df91 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 12:30:11 +0000 Subject: [PATCH 252/503] timeshift: finish timeshift skip support. I now have a more complete skip support implemented. This includes properly handling buffer ends. I have tested with a custom pvr.hts in XBMC, but it does need plenty more. --- src/htsp_server.c | 27 ++++- src/timeshift/private.h | 2 + src/timeshift/timeshift_filemgr.c | 9 ++ src/timeshift/timeshift_reader.c | 182 +++++++++++++++++++++++------- src/tvheadend.h | 1 + 5 files changed, 176 insertions(+), 45 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 77556d6e..00eda674 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1300,8 +1300,6 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) streaming_target_t *st = &hs->hs_input; - if(normts) - st = hs->hs_tsfix = tsfix_create(st); #if ENABLE_TIMESHIFT if (timeshiftPeriod != 0) { if (timeshiftPeriod == ~0) @@ -1309,8 +1307,11 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) else tvhlog(LOG_DEBUG, "htsp", "using timeshift buffer (%u mins)", timeshiftPeriod / 60); st = hs->hs_tshift = timeshift_create(st, timeshiftPeriod); + normts = 1; } #endif + if(normts) + st = hs->hs_tsfix = tsfix_create(st); hs->hs_s = subscription_create_from_channel(ch, weight, htsp->htsp_logname, @@ -1619,6 +1620,7 @@ struct { { "subscribe", htsp_method_subscribe, ACCESS_STREAMING}, { "unsubscribe", htsp_method_unsubscribe, ACCESS_STREAMING}, { "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING}, + { "subscriptionSeek", htsp_method_skip, ACCESS_STREAMING}, { "subscriptionSkip", htsp_method_skip, ACCESS_STREAMING}, { "subscriptionSpeed", htsp_method_speed, ACCESS_STREAMING}, { "fileOpen", htsp_method_file_open, ACCESS_RECORDER}, @@ -2419,6 +2421,26 @@ htsp_subscription_speed(htsp_subscription_t *hs, int speed) htsp_send(hs->hs_htsp, m, NULL, &hs->hs_q, 0); } +/** + * + */ +static void +htsp_subscription_skip(htsp_subscription_t *hs, streaming_skip_t *skip) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "method", "subscriptionSkip"); + htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); + if (skip->type == SMT_SKIP_ABS_TIME || skip->type == SMT_SKIP_ABS_SIZE) + htsmsg_add_u32(m, "absolute", 1); + if (skip->type == SMT_SKIP_ERROR) + htsmsg_add_u32(m, "error", 1); + else if (skip->type == SMT_SKIP_ABS_TIME || skip->type == SMT_SKIP_REL_TIME) + htsmsg_add_s64(m, "time", skip->time); + else if (skip->type == SMT_SKIP_ABS_SIZE || skip->type == SMT_SKIP_REL_SIZE) + htsmsg_add_s64(m, "size", skip->size); + htsp_send(hs->hs_htsp, m, NULL, &hs->hs_q, 0); +} + /** * */ @@ -2465,6 +2487,7 @@ htsp_streaming_input(void *opaque, streaming_message_t *sm) abort(); case SMT_SKIP: + htsp_subscription_skip(hs, sm->sm_data); break; case SMT_SPEED: diff --git a/src/timeshift/private.h b/src/timeshift/private.h index ed64a12f..655b2539 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -133,6 +133,8 @@ void timeshift_filemgr_init ( void ); void timeshift_filemgr_term ( void ); int timeshift_filemgr_makedirs ( int ts_index, char *buf, size_t len ); +timeshift_file_t *timeshift_filemgr_last + ( timeshift_t *ts ); timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ); timeshift_file_t *timeshift_filemgr_prev diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 44571048..f8fafad5 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -278,6 +278,15 @@ timeshift_file_t *timeshift_filemgr_prev return nxt; } +/* + * Get the oldest file + */ +timeshift_file_t *timeshift_filemgr_last ( timeshift_t *ts ) +{ + return TAILQ_FIRST(&ts->files); +} + + /* ************************************************************************** * Setup / Teardown * *************************************************************************/ diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 423d42ac..4f6754f9 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -110,6 +110,7 @@ static ssize_t _read_msg ( int fd, streaming_message_t **sm ) case SMT_START: case SMT_NOSTART: case SMT_SERVICE_STATUS: + return -1; break; /* Code */ @@ -159,6 +160,9 @@ static ssize_t _read_msg ( int fd, streaming_message_t **sm ) } (*sm)->sm_time = time; break; + + default: + return -1; } /* OK */ @@ -221,9 +225,30 @@ static int _timeshift_skip if (!tsf || !tsi) end = 1; + /* Find start/end of buffer */ + if (end) { + if (back) { + tsf = timeshift_filemgr_last(ts); + tsi = NULL; + while (tsf && !tsi) { + if (!(tsi = TAILQ_FIRST(&tsf->iframes))) + tsf = timeshift_filemgr_next(tsf, &end, 0); + } + end = -1; + } else { + tsf = timeshift_filemgr_get(ts, ts->ondemand); + tsi = NULL; + while (tsf && !tsi) { + if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) + tsf = timeshift_filemgr_prev(tsf, &end, 0); + } + end = 1; + } + } + /* Done */ *new_file = tsf; - *iframe = end ? NULL : tsi; + *iframe = tsi; return end; } @@ -242,14 +267,14 @@ void *timeshift_reader ( void *p ) off_t cur_off = 0; int cur_speed = 100, keyframe_mode = 0; int64_t pause_time = 0, play_time = 0, last_time = 0; - int64_t now, deliver, skip_time; - streaming_message_t *sm = NULL, *ctrl; + int64_t now, deliver, skip_time = 0; + streaming_message_t *sm = NULL, *ctrl = NULL; timeshift_index_iframe_t *tsi = NULL; - streaming_skip_t *skip; + streaming_skip_t *skip = NULL; /* Poll */ - struct epoll_event ev; - efd = epoll_create(1); + struct epoll_event ev = { 0 }; + efd = epoll_create(1); ev.events = EPOLLIN; ev.data.fd = ts->rd_pipe.rd; epoll_ctl(efd, EPOLL_CTL_ADD, ev.data.fd, &ev); @@ -264,7 +289,6 @@ void *timeshift_reader ( void *p ) nfds = 0; wait = -1; end = 0; - skip_time = 0; skip = NULL; now = getmonoclock(); @@ -280,6 +304,7 @@ void *timeshift_reader ( void *p ) #endif run = 0; streaming_msg_free(ctrl); + ctrl = NULL; /* Speed */ } else if (ctrl->sm_type == SMT_SPEED) { @@ -351,35 +376,71 @@ void *timeshift_reader ( void *p ) /* Send on the message */ ctrl->sm_code = speed; streaming_target_deliver2(ts->output, ctrl); + ctrl = NULL; /* Skip/Seek */ } else if (ctrl->sm_type == SMT_SKIP) { - skip = (streaming_skip_t *) ctrl->sm_data; + skip = ctrl->sm_data; switch (skip->type) { case SMT_SKIP_REL_TIME: - skip_time = last_time + skip->time; - break; - case SMT_SKIP_ABS_TIME: - skip_time = skip->time; // Wrong - need to use starttime of video too - break; - case SMT_SKIP_REL_SIZE: - case SMT_SKIP_ABS_SIZE: - tvhlog(LOG_DEBUG, "timeshift", "unsupported skip type: %d", skip->type); + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip %"PRId64" requested", ts->id, skip->time); + + /* Must handle live playback case */ + if (ts->state == TS_LIVE) { + if (skip->time < 0) { + pthread_mutex_lock(&ts->rdwr_mutex); + if ((cur_file = timeshift_filemgr_get(ts, ts->ondemand))) { + ts->state = TS_PLAY; + cur_off = cur_file->size; + last_time = cur_file->last; + } else { + tvhlog(LOG_ERR, "timeshift", "ts %d failed to get current file", ts->id); + skip = NULL; + break; + } + pthread_mutex_unlock(&ts->rdwr_mutex); + } else { + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip ignored, already live", ts->id); + skip = NULL; + } + } + + /* OK */ + if (skip) { + /* Adjust time */ + play_time = now; + pause_time = skip_time = last_time + skip->time; + tsi = NULL; + + /* Clear existing packet */ + if (sm) + streaming_msg_free(sm); + sm = NULL; + } break; default: - tvhlog(LOG_ERR, "timeshift", "invalid skip type: %d", skip->type); + tvhlog(LOG_ERR, "timeshift", "ts %d invalid/unsupported skip type: %d", ts->id, skip->type); + skip = NULL; + break; } - if (!skip_time) - streaming_msg_free(ctrl); + + /* Error */ + if (!skip) { + ((streaming_skip_t*)ctrl->sm_data)->type = SMT_SKIP_ERROR; + streaming_target_deliver2(ts->output, ctrl); + ctrl = NULL; + } + /* Ignore */ } else { streaming_msg_free(ctrl); + ctrl = NULL; } } } /* Done */ - if (!run || ts->state != TS_PLAY || !cur_file) { + if (!run || !cur_file || ((ts->state != TS_PLAY && !skip))) { pthread_mutex_unlock(&ts->state_mutex); continue; } @@ -396,12 +457,12 @@ void *timeshift_reader ( void *p ) if (!sm) { /* Rewind or Fast forward (i-frame only) */ - if (skip_time || keyframe_mode) { + if (skip || keyframe_mode) { timeshift_file_t *tsf = NULL; time_t req_time; /* Time */ - if (!skip_time) + if (!skip) req_time = last_time + ((cur_speed < 0) ? -1 : 1); else req_time = skip_time; @@ -410,12 +471,6 @@ void *timeshift_reader ( void *p ) end = _timeshift_skip(ts, req_time, last_time, cur_file, &tsf, &tsi); - /* Adjust skip time to actual */ - if (skip_time) { - skip->time += (tsi->time - skip_time); - streaming_target_deliver2(ts->output, ctrl); - } - /* File changed (close) */ if ((tsf != cur_file) && (fd != -1)) { close(fd); @@ -426,10 +481,12 @@ void *timeshift_reader ( void *p ) cur_file = tsf; if (tsi) cur_off = tsi->pos; + else + cur_off = 0; } /* Find packet */ - if (cur_file && !end) { + if (cur_file) { /* Open file */ if (fd == -1) { @@ -439,11 +496,23 @@ void *timeshift_reader ( void *p ) #endif fd = open(cur_file->path, O_RDONLY); } - if (cur_off) lseek(fd, cur_off, SEEK_SET); + if (cur_off) { +#ifdef TSHFT_TRACE + tvhlog(LOG_DEBUG, "timeshift", "ts %d seek to %lu", ts->id, cur_off); +#endif + lseek(fd, cur_off, SEEK_SET); + } /* Read msg */ ssize_t r = _read_msg(fd, &sm); - assert(r != -1); + if (r < 0) { + streaming_message_t *e = streaming_msg_create_code(SMT_STOP, SM_CODE_UNDEFINED_ERROR); + streaming_target_deliver2(ts->output, e); + tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); + pthread_mutex_unlock(&ts->rdwr_mutex); + pthread_mutex_unlock(&ts->state_mutex); + break; + } #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d read msg %p (%ld)", ts->id, sm, r); @@ -466,16 +535,38 @@ void *timeshift_reader ( void *p ) } } + /* Send skip response */ + if (skip) { + if (sm && sm->sm_type == SMT_PACKET) { + th_pkt_t *pkt = sm->sm_data; + skip->time = pkt->pkt_pts; + skip->type = SMT_SKIP_ABS_TIME; + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to %"PRId64" ok", ts->id, skip->time); + } else { + /* Report error */ + skip->type = SMT_SKIP_ERROR; + skip = NULL; + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip failed", ts->id); + } + streaming_target_deliver2(ts->output, ctrl); + ctrl = NULL; + } + /* Deliver */ - if (sm && (skip_time || + if (sm && (skip || (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t, - ts->id, sm->sm_time); -#endif sm->sm_timeshift = now - sm->sm_time; +#ifdef TSHFT_TRACE + { + time_t pts = 0; + if (sm->sm_type == SMT_PACKET) + pts = ((th_pkt_t*)sm->sm_data)->pkt_pts; + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t" pts=%"PRItime_t " shift=%"PRIu64, + ts->id, sm->sm_time, pts, sm->sm_timeshift ); + } +#endif streaming_target_deliver2(ts->output, sm); last_time = sm->sm_time; sm = NULL; @@ -493,10 +584,12 @@ void *timeshift_reader ( void *p ) } /* Terminate */ - if (!cur_file || end) { + if (!cur_file || end != 0) { + if (!end) + end = (cur_file > 0) ? 1 : -1; /* Back to live */ - if (cur_speed > 0) { + if (end == 1) { tvhlog(LOG_DEBUG, "timeshift", "ts %d eob revert to live mode", ts->id); ts->state = TS_LIVE; cur_speed = 100; @@ -508,13 +601,15 @@ void *timeshift_reader ( void *p ) timeshift_filemgr_flush(ts, NULL); /* Pause */ - } else if (cur_speed < 0) { + } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d sob pause stream", ts->id); - cur_speed = 0; - ts->state = TS_PAUSE; - ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); + cur_speed = 0; + ts->state = TS_PAUSE; + pause_time = now; + ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); } + ctrl = NULL; /* Flush unwanted */ } else if (ts->ondemand && cur_file) { @@ -526,7 +621,8 @@ void *timeshift_reader ( void *p ) } /* Cleanup */ - if (sm) streaming_msg_free(sm); + if (sm) streaming_msg_free(sm); + if (ctrl) streaming_msg_free(ctrl); #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d exit reader thread", ts->id); #endif diff --git a/src/tvheadend.h b/src/tvheadend.h index a6a9a0ab..0df50c13 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -216,6 +216,7 @@ typedef struct signal_status { typedef struct streaming_skip { enum { + SMT_SKIP_ERROR, SMT_SKIP_REL_TIME, SMT_SKIP_ABS_TIME, SMT_SKIP_REL_SIZE, From 11576caf97cab1eab0367a0efaab3bb0f8f0f2d5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 21:26:40 +0000 Subject: [PATCH 253/503] timeshift: enable timeshift by default in the build. --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 6aa50abf..d8cb0189 100755 --- a/configure +++ b/configure @@ -20,11 +20,11 @@ OPTIONS=( "v4l:yes" "linuxdvb:yes" "dvbscan:yes" + "timeshift:yes" "imagecache:auto" "avahi:auto" "zlib:auto" "libav:auto" - "timeshift:no" "bundle:no" "dvbcsa:no" ) From 8be121f558604687675d15404a4517aeb6877efc Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 9 Jan 2013 21:42:09 +0000 Subject: [PATCH 254/503] make: add option to reconfigure with previous options. --- Makefile | 6 +++++- support/configure.inc | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c42e63d5..6f4e2ab3 100644 --- a/Makefile +++ b/Makefile @@ -228,7 +228,7 @@ DEPS = ${OBJS:%.o=%.d} all: ${PROG} # Special -.PHONY: clean distclean check_config +.PHONY: clean distclean check_config reconfigure # Check configure output is valid check_config: @@ -236,6 +236,10 @@ check_config: || echo "./configure output is old, please re-run" @test $(CURDIR)/.config.mk -nt $(CURDIR)/configure +# Recreate configuration +reconfigure: + $(CURDIR)/configure $(CONFIGURE_ARGS) + # Binary ${PROG}: check_config $(OBJS) $(ALLDEPS) $(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS) diff --git a/support/configure.inc b/support/configure.inc index 0130880c..6536fed6 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -456,6 +456,7 @@ function write_config CONFIG_MK=${ROOTDIR}/.config.mk cat > ${CONFIG_MK} < Date: Wed, 9 Jan 2013 23:23:21 +0100 Subject: [PATCH 255/503] timeshift: Fix possible deadlock (due to missing unlock) --- src/timeshift/timeshift_reader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 4f6754f9..1f772ffe 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -396,7 +396,6 @@ void *timeshift_reader ( void *p ) } else { tvhlog(LOG_ERR, "timeshift", "ts %d failed to get current file", ts->id); skip = NULL; - break; } pthread_mutex_unlock(&ts->rdwr_mutex); } else { From c5d889f692cd3ecaae84555f8e05af41235079be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 9 Jan 2013 23:01:11 +0100 Subject: [PATCH 256/503] libav: link with libavcodec --- configure | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure b/configure index d8cb0189..870f2a4a 100755 --- a/configure +++ b/configure @@ -108,6 +108,14 @@ fi if enabled_or_auto libav; then has_libav=true + if $has_libav && ! check_pkg libavcodec "<=55.0.0"; then + has_libav=false + fi + + if $has_libav && ! check_pkg libavcodec ">=52.96.0"; then + has_libav=false + fi + if $has_libav && ! check_pkg libavutil ">=50.43.0"; then has_libav=false fi From ac22d71a0c6fb00700860dc8f58ef5b617d07682 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 10 Jan 2013 10:20:47 +0000 Subject: [PATCH 257/503] support: formatting of configure output. --- support/configure.inc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/support/configure.inc b/support/configure.inc index 6536fed6..0460937a 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -43,7 +43,8 @@ # ########################################################################### # Output -TAB=" \\033[40G" +TAB=" \\033[50G" +TAB2=" \\033[50G" # Text conversion function toupper @@ -224,14 +225,14 @@ function check_pkg local ver=$* # Version test - ver=$(echo $ver | sed 's/>=/ --atleast-version /'\ + cver=$(echo $ver | sed 's/>=/ --atleast-version /'\ | sed 's/<=/ --max-version /'\ | sed 's/==/ --exact-version /') - echo -ne "checking for pkg $pkg $ver ...${TAB}" + echo -ne "checking for pkg $pkg $ver ...${TAB2}" # Check for package - if pkg-config $pkg $ver; then + if pkg-config $pkg $cver; then echo "ok" enable_pkg $pkg else @@ -268,7 +269,7 @@ function check_cc_header local nam=$2 [ -z "$nam" ] && nam=$hdr - echo -ne "checking for cc $hdr.h ...${TAB}" + echo -ne "checking for cc $hdr.h ...${TAB2}" # Enable if supported if check_cc "#include <$1.h>"; then @@ -287,7 +288,7 @@ function check_cc_snippet local snp=$2 local opt=$3 - echo -ne "checking for cc $nam ...${TAB}" + echo -ne "checking for cc $nam ...${TAB2}" # Check if supported if check_cc "$snp" "$opt"; then @@ -306,7 +307,7 @@ function check_cc_option local nam=$2 [ -z "$nam" ] && nam=$opt - echo -ne "checking for cc -m$opt ...${TAB}" + echo -ne "checking for cc -m$opt ...${TAB2}" # Enable if supported if check_cc "" -m${opt}; then @@ -325,7 +326,7 @@ function check_cc_lib local nam=$2 [ -z "$nam" ] && nam=$opt - echo -ne "checking for cc -l$opt ...${TAB}" + echo -ne "checking for cc -l$opt ...${TAB2}" # Enable if supported if check_cc "" -l${opt}; then @@ -361,7 +362,7 @@ function check_py_import local nam=$2 [ -z "$nam" ] && nam=py_${hdr} - echo -ne "checking for py module $hdr ...${TAB}" + echo -ne "checking for py module $hdr ...${TAB2}" # Enable if supported if check_py "import $hdr"; then @@ -382,7 +383,7 @@ function check_bin local bin=$1 local nam=$2 [ -z "$nam" ] && nam=bin_${bin} - echo -ne "checking for $bin ...${TAB}" + echo -ne "checking for $bin ...${TAB2}" if which $bin &> /dev/null; then echo "ok" From 053b86356af004c10501a69c0aef57fc6828cf48 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 10 Jan 2013 11:02:15 +0000 Subject: [PATCH 258/503] Fix #1491 - remove redundant include. --- debian/changelog | 4 ++-- src/epggrab.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 78fa0626..8e3365bc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ -tvheadend (3.3) unstable; urgency=low +tvheadend (3.3.324~gac22d71~precise) precise; urgency=low * The full changelog can be found at http://www.lonelycoder.com/tvheadend/download - -- Andreas Öman Tue, 18 Sep 2012 12:45:49 +0100 + -- Adam Sutton Thu, 10 Jan 2013 10:29:13 +0000 diff --git a/src/epggrab.c b/src/epggrab.c index aa085a07..09dfc122 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include From 14fb1f9e1d149530f19425295250d0cc30c34a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Thu, 10 Jan 2013 19:28:25 +0100 Subject: [PATCH 259/503] HTSP: In response to subscribe() message, return if we support timeshift or not --- src/htsp_server.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index 00eda674..b59f35d9 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1282,6 +1282,11 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) if(normts) htsmsg_add_u32(rep, "normts", 1); +#if ENABLE_TIMESHIFT + if(timeshiftPeriod) + htsmsg_add_u32(rep, "timeshiftPeriod", timeshiftPeriod); +#endif + htsp_reply(htsp, in, rep); /* Initialize the HTSP subscription structure */ From ad2a737a115ee55c68fac805563bc412109cbf26 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Thu, 10 Jan 2013 20:59:14 +0000 Subject: [PATCH 260/503] Fix typo - ENABLE_ defines need to be tested with #if. Thanks to Glandos for spotting. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index c546d7a1..c6e0254d 100644 --- a/src/main.c +++ b/src/main.c @@ -387,7 +387,7 @@ main(int argc, char **argv) "to your Tvheadend installation until you edit\n" "the access-control from within the Tvheadend UI", OPT_BOOL, &opt_firstrun }, -#ifdef ENABLE_LINUXDVB +#if ENABLE_LINUXDVB { 'a', "adapters", "Use only specified DVB adapters", OPT_STR, &opt_dvb_adapters }, #endif From 60371b07f8e48ef5001027251e877db252054637 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Thu, 10 Jan 2013 21:08:10 +0000 Subject: [PATCH 261/503] Second attempt at protecting TRANSMISSION_MODE_4K from users with old versions of the DVB API. We just assume that it was introduced with the current API (5.3) as it is very unlikely anyone will actually come across such a broadcast. --- src/dvb/dvb_tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 5c1398c4..ecc540b8 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -632,7 +632,7 @@ static const fe_guard_interval_t guard_interval_tab [4] = { static const fe_transmit_mode_t transmission_mode_tab [4] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, -#if DVB_API_VERSION >= 5 +#if DVB_VER_ATLEAST(5,3) TRANSMISSION_MODE_4K, #else TRANSMISSION_MODE_AUTO, /* For older DVB API versions - hope the device can detect */ From d31afe099e4b9b058481cb64bd9f9606b64a9eb9 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Fri, 11 Jan 2013 01:34:11 +0400 Subject: [PATCH 262/503] fix processing big non english eit packets increase size of buffers for localized strings because in utf-8 they may be bigger than 256 bytes --- src/epggrab/module/eit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index cb5bffd3..d06a2264 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -228,7 +228,7 @@ static int _eit_desc_short_event { int r; char lang[4]; - char buf[256]; + char buf[512]; if ( len < 5 ) return -1; @@ -270,8 +270,8 @@ static int _eit_desc_ext_event ( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev ) { int r, nitem; - char ikey[256], ival[256]; - char buf[256], lang[4]; + char ikey[512], ival[512]; + char buf[512], lang[4]; if (len < 6) return -1; @@ -441,7 +441,7 @@ static int _eit_desc_crid { int r; uint8_t type; - char buf[257], *crid; + char buf[512], *crid; int clen; while (len > 3) { From efee88c8a28189b06037fb022c31507bdaea7b43 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 10 Jan 2013 14:51:53 +0000 Subject: [PATCH 263/503] dvr: report error if a recordings file is missing. --- src/dvr/dvr.h | 2 +- src/dvr/dvr_db.c | 10 ++++++---- src/htsp_server.c | 4 +++- src/webui/extjs.c | 23 ++++++++++++----------- src/webui/static/app/dvr.js | 27 +++++++++++++++++++++++---- 5 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 4c2174aa..1ec73fb2 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -303,7 +303,7 @@ dvr_entry_t *dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e); dvr_entry_t *dvr_entry_find_by_episode(epg_broadcast_t *e); -off_t dvr_get_filesize(dvr_entry_t *de); +int64_t dvr_get_filesize(dvr_entry_t *de); dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 1b4edf2d..d975b1f5 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -68,6 +68,8 @@ dvr_entry_status(dvr_entry_t *de) } case DVR_COMPLETED: + if(dvr_get_filesize(de) == -1) + return "File Missing"; if(de->de_last_error) return streaming_code2txt(de->de_last_error); else @@ -97,7 +99,7 @@ dvr_entry_schedstatus(dvr_entry_t *de) else return "recording"; case DVR_COMPLETED: - if(de->de_last_error) + if(de->de_last_error || dvr_get_filesize(de) == -1) return "completedError"; else return "completed"; @@ -1375,16 +1377,16 @@ dvr_query_sort(dvr_query_result_t *dqr) /** * */ -off_t +int64_t dvr_get_filesize(dvr_entry_t *de) { struct stat st; if(de->de_filename == NULL) - return 0; + return -1; if(stat(de->de_filename, &st) != 0) - return 0; + return -1; return st.st_size; } diff --git a/src/htsp_server.c b/src/htsp_server.c index b59f35d9..5cb05edc 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -579,7 +579,9 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method) break; case DVR_COMPLETED: s = "completed"; - if(de->de_last_error) + if(dvr_get_filesize(de) == -1) + error = "File missing"; + else if(de->de_last_error) error = streaming_code2txt(de->de_last_error); break; case DVR_MISSED_TIME: diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 2b6fff45..76a12886 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1348,7 +1348,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, dvr_entry_t *de; int start = 0, end, limit, i; const char *s; - off_t fsize; + int64_t fsize = 0; char buf[100]; if((s = http_arg_get(&hc->hc_req_args, "start")) != NULL) @@ -1418,16 +1418,14 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, if(de->de_sched_state == DVR_COMPLETED) { fsize = dvr_get_filesize(de); - if(fsize > 0) { - char url[100]; - htsmsg_add_s64(m, "filesize", fsize); - - snprintf(url, sizeof(url), "dvrfile/%d", de->de_id); - htsmsg_add_str(m, "url", url); + if (fsize > 0) { + char url[100]; + htsmsg_add_s64(m, "filesize", fsize); + snprintf(url, sizeof(url), "dvrfile/%d", de->de_id); + htsmsg_add_str(m, "url", url); } } - htsmsg_add_msg(array, NULL, m); } @@ -1446,7 +1444,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, static int is_dvr_entry_finished(dvr_entry_t *entry) { dvr_entry_sched_state_t state = entry->de_sched_state; - return state == DVR_COMPLETED; + return state == DVR_COMPLETED && !entry->de_last_error && dvr_get_filesize(entry) != -1; } static int is_dvr_entry_upcoming(dvr_entry_t *entry) @@ -1458,8 +1456,11 @@ static int is_dvr_entry_upcoming(dvr_entry_t *entry) static int is_dvr_entry_failed(dvr_entry_t *entry) { - dvr_entry_sched_state_t state = entry->de_sched_state; - return state == DVR_MISSED_TIME || state == DVR_NOSTATE; + if (is_dvr_entry_finished(entry)) + return 0; + if (is_dvr_entry_upcoming(entry)) + return 0; + return 1; } static int diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 215fdf66..02021eb3 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -182,6 +182,13 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) { } } + function renderSize(value) + { + if (value == null) + return ''; + return parseInt(value / 1000000) + ' MB'; + } + function renderPri(value) { return tvheadend.dvrprio.getById(value).data.name; } @@ -201,11 +208,12 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) { id : 'pri', header : "Priority", dataIndex : 'pri', - renderer : renderPri + renderer : renderPri, + hidden : iconCls != 'clock', }, { width : 100, id : 'start', - header : "Start", + header : iconCls == 'clock' ? "Start" : "Date/Time", dataIndex : 'start', renderer : renderDate }, { @@ -221,6 +229,13 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) { header : "Duration", dataIndex : 'duration', renderer : renderDuration + }, { + width : 100, + id : 'filesize', + header : "Filesize", + dataIndex : 'filesize', + renderer : renderSize, + hidden : iconCls != 'television' }, { width : 250, id : 'channel', @@ -244,12 +259,14 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) { return value; } }, - dataIndex : 'config_name' + dataIndex : 'config_name', + hidden: iconCls != 'clock' }, { width : 200, id : 'status', header : "Status", - dataIndex : 'status' + dataIndex : 'status', + hidden: iconCls != 'exclamation' } ]); function addEntry() { @@ -608,6 +625,8 @@ tvheadend.dvr = function() { name : 'status' }, { name : 'schedstate' + }, { + name : 'error' }, { name : 'creator' }, { From accc01db56b50cdf303d382dd8647323c704e262 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 10 Jan 2013 17:04:31 +0000 Subject: [PATCH 264/503] Fix #1083 - add inotify monitoring of recordings. Should a recording be moved (within same dir) the DVR DB will be updated. Should it be moved (out of directory) or deleted entirely, it will simply be marked as missing. The parent directories are also monitored should they be moved or deleted. --- Makefile | 4 + configure | 11 ++ src/dvr/dvr.h | 14 ++ src/dvr/dvr_db.c | 24 +++- src/dvr/dvr_inotify.c | 291 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 src/dvr/dvr_inotify.c diff --git a/Makefile b/Makefile index 6f4e2ab3..b337e7dc 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,10 @@ SRCS-${CONFIG_LINUXDVB} += \ src/webui/extjs_dvb.c \ src/muxes.c \ +# Inotify +SRCS-${CONFIG_INOTIFY} += \ + src/dvr/dvr_inotify.c \ + # V4L SRCS-${CONFIG_V4L} += \ src/v4l.c \ diff --git a/configure b/configure index 870f2a4a..267187ab 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ OPTIONS=( "avahi:auto" "zlib:auto" "libav:auto" + "inotify:auto" "bundle:no" "dvbcsa:no" ) @@ -135,6 +136,16 @@ if enabled_or_auto libav; then fi fi +# +# Inotify +# +if enabled_or_auto inotify; then + if check_cc_header "sys/inotify" inotify_h; then + enable inotify + elif enabled inotify; then + die "Inotify support not found (use --disable-inotify)" + fi +fi # # DVB scan diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 1ec73fb2..0e56c468 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -189,6 +189,13 @@ typedef struct dvr_entry { struct muxer *de_mux; + /** + * Inotify + */ +#if ENABLE_INOTIFY + LIST_ENTRY(dvr_entry) de_inotify_link; +#endif + } dvr_entry_t; @@ -378,4 +385,11 @@ dvr_prio_t dvr_pri2val(const char *s); const char *dvr_val2pri(dvr_prio_t v); +/** + * Inotify support + */ +void dvr_inotify_init ( void ); +void dvr_inotify_add ( dvr_entry_t *de ); +void dvr_inotify_del ( dvr_entry_t *de ); + #endif /* DVR_H */ diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index d975b1f5..6ab51aca 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -40,6 +40,18 @@ struct dvr_entry_list dvrentries; static void dvr_timer_expire(void *aux); static void dvr_timer_start_recording(void *aux); +/* + * Completed + */ +static void +_dvr_entry_completed(dvr_entry_t *de) +{ + de->de_sched_state = DVR_COMPLETED; +#if ENABLE_INOTIFY + dvr_inotify_add(de); +#endif +} + /** * Return printable status for a dvr entry */ @@ -224,7 +236,7 @@ dvr_entry_link(dvr_entry_t *de) if(de->de_filename == NULL) de->de_sched_state = DVR_MISSED_TIME; else - de->de_sched_state = DVR_COMPLETED; + _dvr_entry_completed(de); gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, de->de_stop + cfg->dvr_retention_days * 86400); @@ -436,6 +448,10 @@ dvr_entry_remove(dvr_entry_t *de) hts_settings_remove("dvr/log/%d", de->de_id); htsp_dvr_entry_delete(de); + +#if ENABLE_INOTIFY + dvr_inotify_del(de); +#endif gtimer_disarm(&de->de_timer); @@ -759,7 +775,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode) if (de->de_rec_state == DVR_RS_PENDING || de->de_rec_state == DVR_RS_WAIT_PROGRAM_START) de->de_sched_state = DVR_MISSED_TIME; else - de->de_sched_state = DVR_COMPLETED; + _dvr_entry_completed(de); dvr_rec_unsubscribe(de, stopcode); @@ -1037,6 +1053,7 @@ dvr_init(void) } } + dvr_inotify_init(); dvr_autorec_init(); dvr_db_load(); dvr_autorec_update(); @@ -1424,6 +1441,9 @@ void dvr_entry_delete(dvr_entry_t *de) { if(de->de_filename != NULL) { +#if ENABLE_INOTIFY + dvr_inotify_del(de); +#endif if(unlink(de->de_filename) && errno != ENOENT) tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s", de->de_filename, strerror(errno)); diff --git a/src/dvr/dvr_inotify.c b/src/dvr/dvr_inotify.c new file mode 100644 index 00000000..22c6d22b --- /dev/null +++ b/src/dvr/dvr_inotify.c @@ -0,0 +1,291 @@ +/* + * Digital Video Recorder - inotify processing + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "redblack.h" +#include "dvr/dvr.h" +#include "htsp_server.h" + +/* inotify limits */ +#define EVENT_SIZE ( sizeof (struct inotify_event) ) +#define EVENT_BUF_LEN ( 10 * ( EVENT_SIZE + 16 ) ) +#define EVENT_MASK IN_CREATE | IN_DELETE | IN_DELETE_SELF |\ + IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO + +static int _inot_fd; +static RB_HEAD(,dvr_inotify_entry) _inot_tree; + +typedef struct dvr_inotify_entry +{ + RB_ENTRY(dvr_inotify_entry) link; + char *path; + int fd; + struct dvr_entry_list entries; +} dvr_inotify_entry_t; + +static void* _dvr_inotify_thread ( void *p ); + +static int _str_cmp ( void *a, void *b ) +{ + return strcmp(((dvr_inotify_entry_t*)a)->path, ((dvr_inotify_entry_t*)b)->path); +} + +/** + * Initialise threads + */ +void dvr_inotify_init ( void ) +{ + pthread_t tid; + + _inot_fd = inotify_init(); + + pthread_create(&tid, NULL, _dvr_inotify_thread, NULL); +} + +/** + * Add an entry for monitoring + */ +void dvr_inotify_add ( dvr_entry_t *de ) +{ + static dvr_inotify_entry_t *skel = NULL; + dvr_inotify_entry_t *e; + char *path; + struct stat st; + + if (!de->de_filename || stat(de->de_filename, &st)) + return; + + path = strdup(de->de_filename); + + if (!skel) + skel = calloc(1, sizeof(dvr_inotify_entry_t)); + skel->path = dirname(path); + + if (stat(skel->path, &st)) + return; + + e = RB_INSERT_SORTED(&_inot_tree, skel, link, _str_cmp); + if (!e) { + e = skel; + skel = NULL; + e->path = strdup(e->path); + e->fd = inotify_add_watch(_inot_fd, e->path, EVENT_MASK); + assert(e->fd != -1); + } + + LIST_INSERT_HEAD(&e->entries, de, de_inotify_link); + + free(path); +} + +/* + * Delete an entry from the monitor + */ +void dvr_inotify_del ( dvr_entry_t *de ) +{ + dvr_entry_t *det; + dvr_inotify_entry_t *e; + RB_FOREACH(e, &_inot_tree, link) { + LIST_FOREACH(det, &e->entries, de_inotify_link) + if (det == de) break; + if (det) break; + } + + if (e && det) { + LIST_REMOVE(det, de_inotify_link); + if (LIST_FIRST(&e->entries) == NULL) { + RB_REMOVE(&_inot_tree, e, link); + inotify_rm_watch(_inot_fd, e->fd); + free(e->path); + free(e); + } + } +} + +/* + * Find inotify entry + */ +static dvr_inotify_entry_t * +_dvr_inotify_find + ( int fd ) +{ + dvr_inotify_entry_t *e = NULL; + RB_FOREACH(e, &_inot_tree, link) + if (e->fd == fd) + break; + return e; +} + +/* + * Find DVR entry + */ +static dvr_entry_t * +_dvr_inotify_find2 + ( dvr_inotify_entry_t *die, const char *name ) +{ + dvr_entry_t *de = NULL; + char path[512]; + + snprintf(path, sizeof(path), "%s/%s", die->path, name); + + LIST_FOREACH(de, &die->entries, de_inotify_link) + if (!strcmp(path, de->de_filename)) + break; + + return de; +} + +/* + * File moved + */ +static void +_dvr_inotify_moved + ( int fd, const char *from, const char *to ) +{ + dvr_inotify_entry_t *die; + dvr_entry_t *de; + + if (!(die = _dvr_inotify_find(fd))) + return; + + if (!(de = _dvr_inotify_find2(die, from))) + return; + + if (to) { + char path[512]; + snprintf(path, sizeof(path), "%s/%s", die->path, to); + tvh_str_update(&de->de_filename, path); + dvr_entry_save(de); + } else + dvr_inotify_del(de); + + htsp_dvr_entry_update(de); + dvr_entry_notify(de); +} + +/* + * File deleted + */ +static void +_dvr_inotify_delete + ( int fd, const char *path ) +{ + _dvr_inotify_moved(fd, path, NULL); +} + +/* + * Directory moved + */ +static void +_dvr_inotify_moved_all + ( int fd, const char *to ) +{ + dvr_entry_t *de; + dvr_inotify_entry_t *die; + + if (!(die = _dvr_inotify_find(fd))) + return; + + while ((de = LIST_FIRST(&die->entries))) { + htsp_dvr_entry_update(de); + dvr_entry_notify(de); + dvr_inotify_del(de); + } +} + +/* + * Directory deleted + */ +static void +_dvr_inotify_delete_all + ( int fd ) +{ + _dvr_inotify_moved_all(fd, NULL); +} + +/* + * Process events + */ +void* _dvr_inotify_thread ( void *p ) +{ + int i, len; + char buf[EVENT_BUF_LEN]; + const char *from; + int fromfd; + int cookie; + + while (1) { + + /* Read events */ + fromfd = 0; + cookie = 0; + from = NULL; + i = 0; + len = read(_inot_fd, buf, EVENT_BUF_LEN); + + /* Process */ + pthread_mutex_lock(&global_lock); + while ( i < len ) { + struct inotify_event *ev = (struct inotify_event*)&buf[i]; + i += EVENT_SIZE + ev->len; + + /* Moved */ + if (ev->mask & IN_MOVED_FROM) { + from = ev->name; + fromfd = ev->wd; + cookie = ev->cookie; + continue; + + } else if ((ev->mask & IN_MOVED_TO) && from && ev->cookie == cookie) { + _dvr_inotify_moved(ev->wd, from, ev->name); + from = NULL; + + /* Removed */ + } else if (ev->mask & IN_DELETE) { + _dvr_inotify_delete(ev->wd, ev->name); + + /* Moved self */ + } else if (ev->mask & IN_MOVE_SELF) { + _dvr_inotify_moved_all(ev->wd, NULL); + + /* Removed self */ + } else if (ev->mask & IN_DELETE_SELF) { + _dvr_inotify_delete_all(ev->wd); + } + + if (from) { + _dvr_inotify_moved(fromfd, from, NULL); + from = NULL; + cookie = 0; + } + } + if (from) + _dvr_inotify_moved(fromfd, from, NULL); + pthread_mutex_unlock(&global_lock); + } + + return NULL; +} + From d85e922e84be3ac710540191710f4a522fcc8960 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 10 Jan 2013 17:10:28 +0000 Subject: [PATCH 265/503] dvbscan: switch to using the new linuxtv scan git repo. --- .gitignore | 1 - .gitmodules | 3 +++ configure | 18 ------------------ data/dvb-scan | 1 + 4 files changed, 4 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 160000 data/dvb-scan diff --git a/.gitignore b/.gitignore index 2a4ca61c..7b634ed0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ src/version.c .cproject .project .settings -data/dvb-scan *.pyc .*.sw[op] diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..c5d345fa --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "data/dvb-scan"] + path = data/dvb-scan + url = git://linuxtv.org/dtv-scan-tables.git diff --git a/configure b/configure index 267187ab..f4a5193e 100755 --- a/configure +++ b/configure @@ -147,24 +147,6 @@ if enabled_or_auto inotify; then fi fi -# -# DVB scan -# -if enabled linuxdvb && enabled dvbscan; then - if [ ! -d ${ROOTDIR}/data/dvb-scan ]; then - if ! enabled bin_bzip2; then - die "Bzip2 not found, fetch dvb-scan files (use --disable-dvbscan)" - fi - echo -n "Fetching dvb-scan files... " - if ${ROOTDIR}/support/getmuxlist &> /dev/null; then - echo "done" - else - echo - die "Failed to fetch dvb-scan files (use --disable-dvbscan to skip)" - fi - fi -fi - # # libdvbcsa # diff --git a/data/dvb-scan b/data/dvb-scan new file mode 160000 index 00000000..c57839aa --- /dev/null +++ b/data/dvb-scan @@ -0,0 +1 @@ +Subproject commit c57839aad2260306e6adecc0058fb683a8b34bc4 From 7f54c1c0e45e09b1d3786bc485461124a42fb949 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 10:01:51 +0000 Subject: [PATCH 266/503] timeshift: ensure that timeshift_enabled flag is properly honoured. --- src/htsp_server.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 5cb05edc..fe135815 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -282,7 +282,8 @@ htsp_subscription_destroy(htsp_connection_t *htsp, htsp_subscription_t *hs) tsfix_destroy(hs->hs_tsfix); htsp_flush_queue(htsp, &hs->hs_q); #if ENABLE_TIMESHIFT - if(hs->hs_tshift) timeshift_destroy(hs->hs_tshift); + if(hs->hs_tshift) + timeshift_destroy(hs->hs_tshift); #endif free(hs); } @@ -1241,7 +1242,7 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) { uint32_t chid, sid, weight, req90khz, normts; #if ENABLE_TIMESHIFT - uint32_t timeshiftPeriod; + uint32_t timeshiftPeriod = 0; #endif channel_t *ch; htsp_subscription_t *hs; @@ -1265,9 +1266,11 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) normts = htsmsg_get_u32_or_default(in, "normts", 0); #if ENABLE_TIMESHIFT - timeshiftPeriod = htsmsg_get_u32_or_default(in, "timeshiftPeriod", 0); - if (!timeshift_unlimited_period) - timeshiftPeriod = MIN(timeshiftPeriod, timeshift_max_period); + if (timeshift_enabled) { + timeshiftPeriod = htsmsg_get_u32_or_default(in, "timeshiftPeriod", 0); + if (!timeshift_unlimited_period) + timeshiftPeriod = MIN(timeshiftPeriod, timeshift_max_period); + } #endif /* From 9a655a6218f3d385319933ff2bc68641cacf647c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 11:06:30 +0000 Subject: [PATCH 267/503] build: Some tidying up of the build system mainly this is just cleaning up the output from the build commands but also added debian/changelog to ignore file to stop it being accidentally committed back. --- .gitignore | 1 + Makefile | 4 +-- debian/changelog | 4 +-- support/changelog | 1 + support/configure.inc | 72 +++++++++++++++++++++---------------------- 5 files changed, 42 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 7b634ed0..846be4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ src/version.c *.pyc .*.sw[op] +debian/changelog debian/files debian/tvheadend debian/tvheadend-dbg diff --git a/Makefile b/Makefile index b337e7dc..cd011db2 100644 --- a/Makefile +++ b/Makefile @@ -52,9 +52,9 @@ MKBUNDLE = $(PYTHON) $(CURDIR)/support/mkbundle # ifndef V -ECHO = printf "$(1)\t\t%s\n" $(2) +ECHO = printf "%-16s%s\n" $(1) $(2) BRIEF = CC MKBUNDLE CXX -MSG = $@ +MSG = $(subst $(CURDIR)/,,$@) $(foreach VAR,$(BRIEF), \ $(eval $(VAR) = @$$(call ECHO,$(VAR),$$(MSG)); $($(VAR)))) endif diff --git a/debian/changelog b/debian/changelog index 8e3365bc..78fa0626 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ -tvheadend (3.3.324~gac22d71~precise) precise; urgency=low +tvheadend (3.3) unstable; urgency=low * The full changelog can be found at http://www.lonelycoder.com/tvheadend/download - -- Adam Sutton Thu, 10 Jan 2013 10:29:13 +0000 + -- Andreas Öman Tue, 18 Sep 2012 12:45:49 +0100 diff --git a/support/changelog b/support/changelog index dcfc8788..3bd008c4 100755 --- a/support/changelog +++ b/support/changelog @@ -9,6 +9,7 @@ DIST=$2 VER=$3 # Defaults +[ -z "$CHANGELOG" ] && CHANGELOG=$(dirname $0)/../debian/changelog [ -z "$DEBEMAIL" ] && DEBEMAIL="andreas@lonelycoder.com" [ -z "$DEBFULLNAME" ] && DEBFULLNAME="Andreas Öman" [ -z "$VER" ] && VER=$($(dirname $0)/version) diff --git a/support/configure.inc b/support/configure.inc index 0460937a..6d852482 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -43,8 +43,7 @@ # ########################################################################### # Output -TAB=" \\033[50G" -TAB2=" \\033[50G" +TAB=" %-50s" # Text conversion function toupper @@ -148,36 +147,37 @@ function disabled_or_auto # Show help function show_help { - local opt= val= + local opt= val= fmt="%-30s" echo "Usage: $0 [options]" echo "" echo "Miscellaneous" - echo -e " --help${TAB}Print this message" + printf " $fmt Print this message\n" "--help" echo "" echo "Installation Paths" - echo -e " --prefix=DIR${TAB}Installation root [$prefix]" - echo -e " --bindir=DIR${TAB}Install binaries in DIR [$bindir]" - echo -e " --libdir=DIR${TAB}Install libraries in DIR [$libdir]" - echo -e " --mandir=DIR${TAB}Install man pages in DIR [$mandir]" - echo -e " --datadir=DIR${TAB}Install data files in DIR [$datadir]" + printf " $fmt Installation root [$prefix]\n" "--prefix=DIR$" + printf " $fmt Install binaries in DIR [$bindir]\n" "--bindir=DIR" + printf " $fmt Install libraries in DIR [$libdir]\n" "--libdir=DIR" + printf " $fmt Install man pages in DIR [$mandir]\n" "--mandir=DIR" + printf " $fmt Install data files in DIR [$datadir]\n" "--datadir=DIR" echo "" echo "Compiler/Arch" - echo -e " --cc=CC${TAB}Build using compile [$CC]" - echo -e " --cpu=CPU${TAB}Build and optimize for specific CPU" - echo -e " --arch=ARCH${TAB}Build for architecture [$ARCH]" - echo -e " --platform=PLATFORM${TAB}Build for platform [$PLATFORM]" - echo -e " --python=PYTHON${TAB}Use python binary [$PYTHON]" + printf " $fmt Build using compiler [$CC]\n" "--cc=CC" + printf " $fmt Build and optimize for specific CPU\n" "--cpu=CPU" + printf " $fmt Build for architecture [$ARCH]\n" "--arch=ARCH" + printf " $fmt Build for platform [$PLATFORM]\n" "--platform=PLATFORM" + printf " $fmt Use python binary [$PYTHON]\n" "--python=PYTHON" echo "" echo "Options" for opt in ${OPTIONS[*]}; do val=${opt#*:} opt=${opt%:*} if [ "$val" == "yes" ]; then - echo -e " --disable-${opt}${TAB}Enable $opt [$val]" + printf " $fmt Disable ${opt} [${val}]\n" "--disable-${opt}" elif [ "$val" == "no" ]; then - echo -e " --enable-${opt}${TAB}Enable $opt [$val]" + printf " $fmt Enable ${opt} [${val}]\n" "--enable-${opt}" else - echo -e " --(en|dis)able-${opt}${TAB}Enable $opt [$val]" + printf " $fmt Disable ${opt} [${val}]\n" "--disable-${opt}" + printf " $fmt Enable ${opt} [${val}]\n" "--enable-${opt}" fi done exit 0 @@ -229,7 +229,7 @@ function check_pkg | sed 's/<=/ --max-version /'\ | sed 's/==/ --exact-version /') - echo -ne "checking for pkg $pkg $ver ...${TAB2}" + printf "$TAB" "checking for pkg $pkg $ver ..." # Check for package if pkg-config $pkg $cver; then @@ -269,7 +269,7 @@ function check_cc_header local nam=$2 [ -z "$nam" ] && nam=$hdr - echo -ne "checking for cc $hdr.h ...${TAB2}" + printf "$TAB" "checking for cc $hdr.h ..." # Enable if supported if check_cc "#include <$1.h>"; then @@ -288,7 +288,7 @@ function check_cc_snippet local snp=$2 local opt=$3 - echo -ne "checking for cc $nam ...${TAB2}" + printf "$TAB" "checking for cc $nam ..." # Check if supported if check_cc "$snp" "$opt"; then @@ -307,7 +307,7 @@ function check_cc_option local nam=$2 [ -z "$nam" ] && nam=$opt - echo -ne "checking for cc -m$opt ...${TAB2}" + printf "$TAB" "checking for cc -m$opt ..." # Enable if supported if check_cc "" -m${opt}; then @@ -326,7 +326,7 @@ function check_cc_lib local nam=$2 [ -z "$nam" ] && nam=$opt - echo -ne "checking for cc -l$opt ...${TAB2}" + printf "$TAB" "checking for cc -l$opt ..." # Enable if supported if check_cc "" -l${opt}; then @@ -362,7 +362,7 @@ function check_py_import local nam=$2 [ -z "$nam" ] && nam=py_${hdr} - echo -ne "checking for py module $hdr ...${TAB2}" + printf "$TAB" "checking for py module $hdr ..." # Enable if supported if check_py "import $hdr"; then @@ -383,7 +383,7 @@ function check_bin local bin=$1 local nam=$2 [ -z "$nam" ] && nam=bin_${bin} - echo -ne "checking for $bin ...${TAB2}" + printf "$TAB" "checking for $bin ..." if which $bin &> /dev/null; then echo "ok" @@ -401,17 +401,17 @@ function check_bin # Print config function print_config { - local pkg= + local pkg= fmt=" %-40s %s\n" # Compiler settings echo "" echo "Compiler:" - echo -e " Using C compiler:${TAB}${CC}" - echo -e " Build for arch:${TAB}${ARCH}" + printf "$fmt" "Using C compiler:" "${CC}" + printf "$fmt" "Build for arch:" "${ARCH}" echo "" echo "Binaries:" - echo -e " Using PYTHON:${TAB}${PYTHON}" + printf "$fmt" "Using PYTHON:" "${PYTHON}" echo "" # Options @@ -420,9 +420,9 @@ function print_config k=${opt%:*} v=${opt#*:} if [ "$v" == "yes" ]; then - echo -e " $k:${TAB}yes" + printf "$fmt" "$k" "yes" else - echo -e " $k:${TAB}no" + printf "$fmt" "$k" "no" fi done echo "" @@ -430,17 +430,17 @@ function print_config # Packages echo "Packages:" for pkg in ${PACKAGES[*]}; do - echo -e " ${pkg}:${TAB}$(pkg-config --modversion $pkg)" + printf "$fmt" "${pkg}" "$(pkg-config --modversion $pkg)" done echo "" # Installation echo "Installation paths:" - echo -e " Prefix:${TAB}${prefix}" - echo -e " Binaries:${TAB}${bindir}" - echo -e " Libraries:${TAB}${libdir}" - echo -e " Data files:${TAB}${datadir}" - echo -e " Man pages:${TAB}${mandir}" + printf "$fmt" "Prefix:" "${prefix}" + printf "$fmt" "Binaries:" "${bindir}" + printf "$fmt" "Libraries:" "${libdir}" + printf "$fmt" "Data files:" "${datadir}" + printf "$fmt" "Man pages:" "${mandir}" echo "" } From 979c9d5c7e0551a69a41a2bc2691e87746b49eba Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 11:35:41 +0000 Subject: [PATCH 268/503] Fix #1493 - timeshift: fix deadlock issue if streaming fails to start. --- src/timeshift.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/timeshift.c b/src/timeshift.c index 2abe6678..b068c40f 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -175,11 +175,14 @@ timeshift_destroy(streaming_target_t *pad) /* Must hold global lock */ lock_assert(&global_lock); - /* Ensure the thread exits */ + /* Ensure the threads exits */ // Note: this is a workaround for the fact the Q might have been flushed // in reader thread (VERY unlikely) + pthread_mutex_lock(&ts->state_mutex); sm = streaming_msg_create(SMT_EXIT); streaming_target_deliver2(&ts->wr_queue.sq_st, sm); + timeshift_write_exit(ts->rd_pipe.wr); + pthread_mutex_unlock(&ts->state_mutex); /* Wait for all threads */ pthread_join(ts->rd_thread, NULL); From 622531e385a5913105665caaa5e49d2b8a2c34b5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 12:26:30 +0000 Subject: [PATCH 269/503] eit: fix a bug in EIT extended descriptor parsing. --- src/epggrab/module/eit.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index d06a2264..103464fe 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -269,9 +269,10 @@ static int _eit_desc_short_event static int _eit_desc_ext_event ( epggrab_module_t *mod, uint8_t *ptr, int len, eit_event_t *ev ) { - int r, nitem; + int r, ilen; char ikey[512], ival[512]; char buf[512], lang[4]; + uint8_t *iptr; if (len < 6) return -1; @@ -286,28 +287,34 @@ static int _eit_desc_ext_event ptr += 3; /* Key/Value items */ - nitem = *ptr; + ilen = *ptr; len -= 1; ptr += 1; - if (len < (2 * nitem) + 1) return -1; + iptr = ptr; + if (len < ilen) return -1; - while (nitem--) { + /* Skip past */ + ptr += ilen; + len -= ilen; + + /* Process */ + while (ilen) { /* Key */ if ( (r = _eit_get_string_with_len(mod, ikey, sizeof(ikey), - ptr, len, ev->default_charset)) < 0 ) - return -1; - - len -= r; - ptr += r; + iptr, ilen, ev->default_charset)) < 0 ) + break; + + ilen -= r; + iptr += r; /* Value */ if ( (r = _eit_get_string_with_len(mod, ival, sizeof(ival), - ptr, len, ev->default_charset)) < 0 ) - return -1; + iptr, ilen, ev->default_charset)) < 0 ) + break; - len -= r; - ptr += r; + ilen -= r; + iptr += r; /* Store */ // TODO: extend existing? @@ -323,9 +330,7 @@ static int _eit_desc_ext_event if ( _eit_get_string_with_len(mod, buf, sizeof(buf), ptr, len, - ev->default_charset) < 0 ) { - return -1; - } else { + ev->default_charset) > 0 ) { if (!ev->desc) ev->desc = lang_str_create(); lang_str_append(ev->desc, buf, lang); } From bb51160721e81d3801d478c9935d631f132ebb8e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 13:30:29 +0000 Subject: [PATCH 270/503] dvb: ignore disabled adapters when finding EPG services. --- src/dvb/dvb_service.c | 3 ++- src/service.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 31f1b9e7..6717f5c5 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -351,9 +351,10 @@ dvb_service_find3 service_t *svc; if (tdmi) { LIST_FOREACH(svc, &tdmi->tdmi_transports, s_group_link) { + if (sid != svc->s_dvb_service_id) continue; if (enabled && !svc->s_enabled) continue; if (epgprimary && !service_is_primary_epg(svc)) continue; - if (sid == svc->s_dvb_service_id) return svc; + return svc; } } else if (tda) { LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { diff --git a/src/service.c b/src/service.c index b0ad8fc2..67353021 100644 --- a/src/service.c +++ b/src/service.c @@ -1180,6 +1180,9 @@ service_is_primary_epg(service_t *svc) if (!svc || !svc->s_ch) return 0; LIST_FOREACH(t, &svc->s_ch->ch_services, s_ch_link) { if (!t->s_dvb_mux_instance) continue; + if (!t->s_dvb_mux_instance->tdmi_enabled) continue; + if (!t->s_dvb_mux_instance->tdmi_adapter->tda_enabled) continue; + if (!t->s_dvb_mux_instance->tdmi_adapter->tda_rootpath) continue; if (!t->s_enabled || !t->s_dvb_eit_enable) continue; if (!ret || service_get_prio(t) < service_get_prio(ret)) ret = t; From 1de1b68c7e6e4396233157e6f0f07f333e1dad49 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 20:50:19 +0000 Subject: [PATCH 271/503] build: ensure git submodule with dvb-scan data is updated. --- configure | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/configure b/configure index f4a5193e..075e5348 100755 --- a/configure +++ b/configure @@ -41,6 +41,8 @@ parse_args $* # Checks # ########################################################################### +echo "Checking support/features" + # # Compiler # @@ -168,6 +170,17 @@ if enabled_or_auto imagecache; then fi fi +# +# DVB scan +# +if enabled linuxdvb && enabled dvbscan; then + [ ! -d ${ROOTDIR}/data/dvb-scan/.git ] && rm -rf $ROOTDIR/data/dvb-scan/* + printf "${TAB}" "fetching dvb-scan files ..." + git submodule update --init &> /dev/null\ + || die "failed (use --disable-dvbscan)" + echo "ok" +fi + # ########################################################################### # Write config # ########################################################################### From 7227c128eba6967950ae5be5dfd6b881d640d0f6 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 21:34:40 +0000 Subject: [PATCH 272/503] build: remove use of submodule for dvb-scan data Revert back to manually fetching, though now its a git report its somewhat simpler and we'll avoid using a separate script. --- .gitignore | 2 ++ .gitmodules | 3 --- configure | 13 ++++++++++--- data/dvb-scan | 1 - support/getmuxlist | 41 ----------------------------------------- support/mkbundle | 4 ++++ support/posix.mk | 1 + 7 files changed, 17 insertions(+), 48 deletions(-) delete mode 160000 data/dvb-scan delete mode 100755 support/getmuxlist diff --git a/.gitignore b/.gitignore index 846be4e0..d7e34f72 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ build.* src/version.c +data/dvb-scan + .cproject .project .settings diff --git a/.gitmodules b/.gitmodules index c5d345fa..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "data/dvb-scan"] - path = data/dvb-scan - url = git://linuxtv.org/dtv-scan-tables.git diff --git a/configure b/configure index 075e5348..0445170d 100755 --- a/configure +++ b/configure @@ -174,10 +174,17 @@ fi # DVB scan # if enabled linuxdvb && enabled dvbscan; then - [ ! -d ${ROOTDIR}/data/dvb-scan/.git ] && rm -rf $ROOTDIR/data/dvb-scan/* printf "${TAB}" "fetching dvb-scan files ..." - git submodule update --init &> /dev/null\ - || die "failed (use --disable-dvbscan)" + if [ -d ${ROOTDIR}/data/dvb-scan/.git ]; then + (cd ${ROOTDIR}/data/dvb-scan; git pull) &> /dev/null + else + URL=git://linuxtv.org/dtv-scan-tables.git + git clone $URL ${ROOTDIR}/data/dvb-scan &> /dev/null + if [ $? -ne 0 ]; then + echo "fail" + die "Failed to fetch dvb-scan data (use --disable-dvbscan)" + fi + fi echo "ok" fi diff --git a/data/dvb-scan b/data/dvb-scan deleted file mode 160000 index c57839aa..00000000 --- a/data/dvb-scan +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c57839aad2260306e6adecc0058fb683a8b34bc4 diff --git a/support/getmuxlist b/support/getmuxlist deleted file mode 100755 index c2b1b7a8..00000000 --- a/support/getmuxlist +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# -# Retrieve the latest dvb-apps scan files -# - -URL=http://linuxtv.org/hg/dvb-apps/archive/tip.tar.bz2 -TMP=/tmp/getmuxlist.$$ -TVH=$(cd $(dirname $0)/..; pwd)/data/dvb-scan - -function die -{ - [ ! -z "$1" ] && echo $1 || echo - rm -rf $TMP - rm -rf $TVH - exit 1 -} - -# Get files -rm -rf $TMP -mkdir -p $TMP -cd $TMP -echo -n "fetching scan files ... " -(wget -O - -q $URL | tar xj) 2> /dev/null ||\ -(curl $URL | tar xj) 2> /dev/null -cd dvb-apps* 2> /dev/null || die "failed" -echo "done" - -# Copy to TVH -echo -n "moving into tvh data/ directory ... " -rm -rf $TVH -mkdir -p $TVH -mv ./util/scan/* $TVH -echo "done" - -# Cleanup -echo -n "cleaning up ... " -for f in $TVH/*; do - [ -f $f ] && rm -f $f -done -rm -rf $TMP -echo "done" diff --git a/support/mkbundle b/support/mkbundle index 8818c592..7e32d657 100755 --- a/support/mkbundle +++ b/support/mkbundle @@ -46,11 +46,15 @@ for path in args: t = ents while True: (d,n) = rsplit(n) + if d.startswith('.'): + fs = [] + break if d not in t: t[d] = {} t = t[d] if not n: break for f in fs: + if f.startswith('.'): continue t[f] = None # Output a file diff --git a/support/posix.mk b/support/posix.mk index 62819206..2994fc5a 100644 --- a/support/posix.mk +++ b/support/posix.mk @@ -13,6 +13,7 @@ install: ${PROG} ${MAN} cp -r $$bundle/* ${DESTDIR}${datadir}/tvheadend/$$bundle ;\ done + find ${DESTDIR} -name .git -exec rm -rf {} \; uninstall: rm -f ${DESTDIR}${bindir)/tvheadend From a1461fb58ae7ec324387203afbfeb6844d0132d2 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 11 Jan 2013 22:09:27 +0000 Subject: [PATCH 273/503] build: correction to ensure old data is removed. --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 0445170d..4bdd99ce 100755 --- a/configure +++ b/configure @@ -178,6 +178,7 @@ if enabled linuxdvb && enabled dvbscan; then if [ -d ${ROOTDIR}/data/dvb-scan/.git ]; then (cd ${ROOTDIR}/data/dvb-scan; git pull) &> /dev/null else + rm -rf ${ROOTDIR}/data/dvb-scan &> /dev/null URL=git://linuxtv.org/dtv-scan-tables.git git clone $URL ${ROOTDIR}/data/dvb-scan &> /dev/null if [ $? -ne 0 ]; then From 9300506e4f8c52ba116295cc3daa04491412c102 Mon Sep 17 00:00:00 2001 From: BtbN Date: Fri, 11 Jan 2013 23:11:52 +0100 Subject: [PATCH 274/503] Add a new button to service list which auto-mapps channels by service-name --- src/webui/static/app/dvb.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index d351c98c..00839b1f 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -574,7 +574,6 @@ tvheadend.dvb_services = function(adapterId) { 'Please select at least one item to delete'); } } - ; function saveChanges() { var mr = store.getModifiedRecords(); @@ -600,6 +599,14 @@ tvheadend.dvb_services = function(adapterId) { }); } + function mapSelected() { + grid.selModel.each(function(rec) { + if(!rec.get('channelname')) + rec.set('channelname', rec.get('svcname')); + return true; + }); + } + var saveBtn = new Ext.Toolbar.Button({ tooltip : 'Save any changes made (Changed cells have red borders).', iconCls : 'save', @@ -618,10 +625,22 @@ tvheadend.dvb_services = function(adapterId) { disabled : true }); + var mapBtn = new Ext.Toolbar.Button({ + tooltip : 'Map selected services to channels based on their name. Does nothing if selected item is already mapped.', + iconCls : 'clone', + text : "Map selected", + handler : mapSelected, + disabled : true + }); + var selModel = new Ext.grid.RowSelectionModel({ singleSelect : false }); + selModel.on('selectionchange', function(s) { + mapBtn.setDisabled(s.getCount() == 0); + }); + var grid = new Ext.grid.EditorGridPanel({ stripeRows : true, title : 'Services', @@ -633,7 +652,7 @@ tvheadend.dvb_services = function(adapterId) { forceFit : true }, selModel : selModel, - tbar : [ saveBtn, rejectBtn ] + tbar : [ saveBtn, rejectBtn, '-', mapBtn ] }); return grid; } From 2df7b8d63b8019289a9cb141fa7c568292d8bf99 Mon Sep 17 00:00:00 2001 From: Jason Millard Date: Fri, 11 Jan 2013 22:08:28 -0500 Subject: [PATCH 275/503] add cflags support to configure script --- support/configure.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/support/configure.inc b/support/configure.inc index 6d852482..42975e6f 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -14,7 +14,6 @@ [ -z "$CPU" ] && CPU=generic [ -z "$ARCH" ] && ARCH=`uname -m` [ -z "$OSENV" ] && OSENV=posix -[ -z "$CC" ] && CC=cc [ -z "$PYTHON" ] && PYTHON=python # Paths @@ -162,6 +161,7 @@ function show_help echo "" echo "Compiler/Arch" printf " $fmt Build using compiler [$CC]\n" "--cc=CC" + printf " $fmt Build using C flags\n" "--cflags=CFLAGS" printf " $fmt Build and optimize for specific CPU\n" "--cpu=CPU" printf " $fmt Build for architecture [$ARCH]\n" "--arch=ARCH" printf " $fmt Build for platform [$PLATFORM]\n" "--platform=PLATFORM" @@ -198,7 +198,7 @@ function parse_args *dir|prefix) eval "$opt=$val" ;; - cc|arch|cpu|platform|python) + cc|cflags|arch|cpu|platform|python) eval "`toupper $opt`=$val" ;; enable-*) @@ -407,6 +407,9 @@ function print_config echo "" echo "Compiler:" printf "$fmt" "Using C compiler:" "${CC}" + if [ "${CFLAGS}" != "" ]; then + printf "$fmt" "Using C flags:" "${CFLAGS}" + fi printf "$fmt" "Build for arch:" "${ARCH}" echo "" From 87a79003a9971236c9f6d3911f7918933f92f97e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 12 Jan 2013 13:26:47 +0000 Subject: [PATCH 276/503] timeshift: fix start of buffer bug and add some useful debug. --- src/timeshift/timeshift_reader.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 1f772ffe..59bdd38d 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -404,6 +404,8 @@ void *timeshift_reader ( void *p ) } } + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64, ts->id, last_time); + /* OK */ if (skip) { /* Adjust time */ @@ -465,10 +467,13 @@ void *timeshift_reader ( void *p ) req_time = last_time + ((cur_speed < 0) ? -1 : 1); else req_time = skip_time; + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to %"PRId64" from %"PRId64, ts->id, req_time, last_time); /* Find */ end = _timeshift_skip(ts, req_time, last_time, cur_file, &tsf, &tsi); + if (tsi) + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip found pkt @ %"PRId64, ts->id, tsi->time); /* File changed (close) */ if ((tsf != cur_file) && (fd != -1)) { @@ -557,7 +562,9 @@ void *timeshift_reader ( void *p ) ((cur_speed > 0) && (sm->sm_time <= deliver))))) { sm->sm_timeshift = now - sm->sm_time; -#ifdef TSHFT_TRACE +#ifndef TSHFT_TRACE + if (skip) +#endif { time_t pts = 0; if (sm->sm_type == SMT_PACKET) @@ -565,7 +572,6 @@ void *timeshift_reader ( void *p ) tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t" pts=%"PRItime_t " shift=%"PRIu64, ts->id, sm->sm_time, pts, sm->sm_timeshift ); } -#endif streaming_target_deliver2(ts->output, sm); last_time = sm->sm_time; sm = NULL; @@ -604,7 +610,7 @@ void *timeshift_reader ( void *p ) tvhlog(LOG_DEBUG, "timeshift", "ts %d sob pause stream", ts->id); cur_speed = 0; ts->state = TS_PAUSE; - pause_time = now; + pause_time = last_time; ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); } From b66edd471d345bc6286d234715b28d52a0e44b1b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 12 Jan 2013 13:29:02 +0000 Subject: [PATCH 277/503] timeshift: fix printing error. --- src/timeshift/timeshift_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 59bdd38d..7430c6f5 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -460,7 +460,7 @@ void *timeshift_reader ( void *p ) /* Rewind or Fast forward (i-frame only) */ if (skip || keyframe_mode) { timeshift_file_t *tsf = NULL; - time_t req_time; + int64_t req_time; /* Time */ if (!skip) @@ -569,7 +569,7 @@ void *timeshift_reader ( void *p ) time_t pts = 0; if (sm->sm_type == SMT_PACKET) pts = ((th_pkt_t*)sm->sm_data)->pkt_pts; - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRItime_t" pts=%"PRItime_t " shift=%"PRIu64, + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, ts->id, sm->sm_time, pts, sm->sm_timeshift ); } streaming_target_deliver2(ts->output, sm); From 5ba4b661167ea6a2903955517f64831673a1725a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sat, 12 Jan 2013 15:50:32 +0100 Subject: [PATCH 278/503] libav: detect versions that are to old for libav muxing --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 4bdd99ce..e9bc7e4e 100755 --- a/configure +++ b/configure @@ -127,7 +127,7 @@ if enabled_or_auto libav; then has_libav=false fi - if $has_libav && ! check_pkg libavformat ">=50.43.0"; then + if $has_libav && ! check_pkg libavformat ">=53.10.0"; then has_libav=false fi From 687d0471e82695d5ac2cde6972539bdc2fdc4a33 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 12 Jan 2013 20:13:33 +0000 Subject: [PATCH 279/503] build: ensure proper path is cleaned of .git files. --- support/posix.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/posix.mk b/support/posix.mk index 2994fc5a..f77677d7 100644 --- a/support/posix.mk +++ b/support/posix.mk @@ -13,7 +13,7 @@ install: ${PROG} ${MAN} cp -r $$bundle/* ${DESTDIR}${datadir}/tvheadend/$$bundle ;\ done - find ${DESTDIR} -name .git -exec rm -rf {} \; + find ${DESTDIR}${datadir}/tvheadend -name .git -exec rm -rf {} \; &>/dev/null || /bin/true uninstall: rm -f ${DESTDIR}${bindir)/tvheadend From cb7a879d6a11af621e7ca475655c884af526d1bf Mon Sep 17 00:00:00 2001 From: oneadvent Date: Sat, 12 Jan 2013 18:36:16 -0600 Subject: [PATCH 280/503] adding help file for timeshift tab (under configure) --- docs/html/config_timeshift.html | 37 +++++++++++++++++++++++++++++++ src/webui/static/app/timeshift.js | 4 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 docs/html/config_timeshift.html diff --git a/docs/html/config_timeshift.html b/docs/html/config_timeshift.html new file mode 100644 index 00000000..4c3420e3 --- /dev/null +++ b/docs/html/config_timeshift.html @@ -0,0 +1,37 @@ +
+ +

+ This tab is used to configure timeshift properties. + +

+ Configuration options: +

+
Enabled +
Turn on and off timeshift. + +
On-Demand +
Turn this on to only begin timeshift recording when pause is pressed. + Leave unchecked for always timeshifting. (Rewinding even if you haven't pressed + pause.) + +
Storage Path: +
Where should the timeshift data be stored. + +
Max. Period (mins): +
Specify the number of minutes to put into the timeshift. + +
Unlimited: +
If checked, this overrides Max Period (mins) and will continue to timeshift until your + hard drive or other storage medium fills up. + +
Max. Size (MB) +
Specifies in Megabytes how big the timeshift file should get. + +
Unlimited: +
If checked, this overrides Max Size (MB) and will continue to timeshift until your + hard drive or other storage medium fills up + +
+ Changes to any of these settings must be confirmed by pressing the + 'Save configuration' button before taking effect. +
diff --git a/src/webui/static/app/timeshift.js b/src/webui/static/app/timeshift.js index 7f27ba4f..e1a78aa9 100644 --- a/src/webui/static/app/timeshift.js +++ b/src/webui/static/app/timeshift.js @@ -47,7 +47,7 @@ tvheadend.timeshift = function() { }); var timeshiftUnlPeriod = new Ext.form.Checkbox({ - fieldLabel: ' unlimited', + fieldLabel: ' Unlimited', name: 'timeshift_unlimited_period', Width: 300 }); @@ -60,7 +60,7 @@ tvheadend.timeshift = function() { }); var timeshiftUnlSize = new Ext.form.Checkbox({ - fieldLabel: ' unlimited', + fieldLabel: ' Unlimited', name: 'timeshift_unlimited_size', Width: 300 }); From bd9a52fc5218ef05dbe3d1b9aa692cb39db61c31 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Sun, 13 Jan 2013 12:46:31 +0400 Subject: [PATCH 281/503] add saving prevcaid to service config --- src/dvb/dvb_service.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 6717f5c5..da746544 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -171,6 +171,9 @@ dvb_service_save(service_t *t) if(t->s_default_authority) htsmsg_add_str(m, "default_authority", t->s_default_authority); + if(t->s_prefcapid) + htsmsg_add_u32(m, "prefcapid", t->s_prefcapid); + pthread_mutex_lock(&t->s_stream_mutex); psi_save_service_settings(m, t); pthread_mutex_unlock(&t->s_stream_mutex); From d08c0b665af08a071d483e34caf184d023f3400a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 13 Jan 2013 22:11:13 +0000 Subject: [PATCH 282/503] docs: update timeshift documentation. --- docs/html/config_timeshift.html | 34 +++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/html/config_timeshift.html b/docs/html/config_timeshift.html index 4c3420e3..a23fda29 100644 --- a/docs/html/config_timeshift.html +++ b/docs/html/config_timeshift.html @@ -10,28 +10,42 @@
Turn on and off timeshift.
On-Demand -
Turn this on to only begin timeshift recording when pause is pressed. - Leave unchecked for always timeshifting. (Rewinding even if you haven't pressed - pause.) +
Turn this on to start timeshift buffer on pause. In this mode + you cannot rewind the buffer (it always begins on the currently playing + frame). + + Without this option there will be a permanent, cicular, buffer upto + the limits defined below.
Storage Path: -
Where should the timeshift data be stored. +
Where the timeshift data will be stored. If nothing is specified this + will default to CONF_DIR/timeshift/buffer
Max. Period (mins): -
Specify the number of minutes to put into the timeshift. +
Specify the maximum time period that will be buffered for any given + (client) subscription.
Unlimited: -
If checked, this overrides Max Period (mins) and will continue to timeshift until your - hard drive or other storage medium fills up. +
If checked, this allows the timeshift buffer to grow unbounded until + your storage media runs out of space (WARNING: this could be dangerous!). + Changes to any of these settings must be confirmed by pressing the 'Save configuration' button before taking effect. + + NOTE: These settings represent server side maximums, however the clients can + request smaller buffers or even not to create a buffer at all (for example + should they not support timeshifting). From a2fab105a1a8387b47d1ebcd5b019757510b5db3 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 13 Jan 2013 22:19:52 +0000 Subject: [PATCH 283/503] Fix #1529 - webUI should show timeshift periods as minutes. --- docs/html/config_timeshift.html | 2 +- src/webui/extjs.c | 4 ++-- src/webui/static/app/timeshift.js | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/html/config_timeshift.html b/docs/html/config_timeshift.html index a23fda29..3265118b 100644 --- a/docs/html/config_timeshift.html +++ b/docs/html/config_timeshift.html @@ -30,7 +30,7 @@ your storage media runs out of space (WARNING: this could be dangerous!). Changes to any of these settings must be confirmed by pressing the diff --git a/src/timeshift.h b/src/timeshift.h index 5281e8b5..342c7c66 100644 --- a/src/timeshift.h +++ b/src/timeshift.h @@ -27,6 +27,9 @@ extern uint32_t timeshift_max_period; extern int timeshift_unlimited_size; extern size_t timeshift_max_size; +extern size_t timeshift_total_size; +extern pthread_mutex_t timeshift_size_lock; + void timeshift_init ( void ); void timeshift_term ( void ); void timeshift_save ( void ); diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 2e2661bd..fc097bc4 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -38,6 +38,9 @@ static pthread_t timeshift_reaper_thread; static pthread_mutex_t timeshift_reaper_lock; static pthread_cond_t timeshift_reaper_cond; +pthread_mutex_t timeshift_size_lock; +size_t timeshift_total_size; + /* ************************************************************************** * File reaper thread * *************************************************************************/ @@ -72,6 +75,10 @@ static void* timeshift_reaper_callback ( void *p ) if (errno != ENOTEMPTY) tvhlog(LOG_ERR, "timeshift", "failed to remove %s [e=%s]", dpath, strerror(errno)); + pthread_mutex_lock(×hift_size_lock); + assert(tsf->size >= timeshift_total_size); + timeshift_total_size -= tsf->size; + pthread_mutex_unlock(×hift_size_lock); /* Free memory */ while ((ti = TAILQ_FIRST(&tsf->iframes))) { @@ -218,8 +225,13 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) } /* Check size */ - // TODO: need to implement this - + pthread_mutex_lock(×hift_size_lock); + if (!timeshift_unlimited_size && timeshift_total_size >= timeshift_max_size) { + tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); + ts->full = 1; + } + pthread_mutex_unlock(×hift_size_lock); + /* Create new file */ tsf_tmp = NULL; if (!ts->full) { @@ -304,6 +316,10 @@ void timeshift_filemgr_init ( void ) timeshift_filemgr_get_root(path, sizeof(path)); rmtree(path); + /* Size processing */ + timeshift_total_size = 0; + pthread_mutex_init(×hift_size_lock, NULL); + /* Start the reaper thread */ timeshift_reaper_run = 1; pthread_mutex_init(×hift_reaper_lock, NULL); diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index 0fb3129f..8bf6be03 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -224,6 +224,9 @@ static inline ssize_t _process_msg0 if (err > 0) { tsf->last = sm->sm_time; tsf->size += err; + pthread_mutex_lock(×hift_size_lock); + timeshift_total_size += err; + pthread_mutex_unlock(×hift_size_lock); } return err; } diff --git a/src/webui/static/app/timeshift.js b/src/webui/static/app/timeshift.js index 059aae78..02335006 100644 --- a/src/webui/static/app/timeshift.js +++ b/src/webui/static/app/timeshift.js @@ -56,15 +56,13 @@ tvheadend.timeshift = function() { fieldLabel: 'Max. Size (MB)', name: 'timeshift_max_size', allowBlank: false, - width: 300, - hidden : true + width: 300 }); var timeshiftUnlSize = new Ext.form.Checkbox({ fieldLabel: '   (unlimited)', name: 'timeshift_unlimited_size', - Width: 300, - hidden : true + Width: 300 }); /* **************************************************************** From 71b09488a170514f538261aad904718ee37831b1 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 00:58:12 +0100 Subject: [PATCH 292/503] .c files are not executable --- src/capmt.c | 0 src/dvr/dvr_rec.c | 0 src/streaming.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/capmt.c mode change 100755 => 100644 src/dvr/dvr_rec.c mode change 100755 => 100644 src/streaming.c diff --git a/src/capmt.c b/src/capmt.c old mode 100755 new mode 100644 diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c old mode 100755 new mode 100644 diff --git a/src/streaming.c b/src/streaming.c old mode 100755 new mode 100644 From 294b8d92c2d3a9bdd115788b8293b785cfd3f5af Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 02:18:13 +0100 Subject: [PATCH 293/503] Make tcp_server_create IPv6 aware --- src/tcp.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index a97b079b..ab3d432f 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -478,21 +478,55 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) int fd, x; struct epoll_event e; tcp_server_t *ts; - struct sockaddr_in s; + struct addrinfo hints, *res, *ressave, *use = NULL; + char *portBuf = (char*)malloc(6); int one = 1; + int zero = 0; + memset(&e, 0, sizeof(e)); - fd = tvh_socket(AF_INET, SOCK_STREAM, 0); + + snprintf(portBuf, 6, "%d", port); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + x = getaddrinfo(NULL, portBuf, &hints, &res); + free(portBuf); + + if(x != 0) + return NULL; + + ressave = res; + while(res) + { + if(res->ai_family == AF_INET6) + { + use = res; + break; + } + else if(use == NULL) + { + use = res; + } + res = res->ai_next; + } + + fd = tvh_socket(use->ai_family, use->ai_socktype, use->ai_protocol); if(fd == -1) return NULL; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); + if(use->ai_family == AF_INET6) + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(int)); - memset(&s, 0, sizeof(s)); - s.sin_family = AF_INET; - s.sin_port = htons(port); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); - x = bind(fd, (struct sockaddr *)&s, sizeof(s)); - if(x < 0) { + x = bind(fd, use->ai_addr, use->ai_addrlen); + freeaddrinfo(ressave); + + if(x != 0) + { close(fd); return NULL; } @@ -504,11 +538,10 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) ts->start = start; ts->opaque = opaque; - e.events = EPOLLIN; e.data.ptr = ts; - epoll_ctl(tcp_server_epoll_fd, EPOLL_CTL_ADD, fd, &e); + return ts; } From abd5487cc7e6256345be19f1b10e77e1271f9915 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 02:41:16 +0100 Subject: [PATCH 294/503] Make tcp code use sockaddr_storage instead of sockaddr_in --- src/tcp.c | 8 ++++---- src/tcp.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index ab3d432f..84313663 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -359,8 +359,8 @@ typedef struct tcp_server_launch_t { tcp_server_callback_t *start; void *opaque; int fd; - struct sockaddr_in peer; - struct sockaddr_in self; + struct sockaddr_storage peer; + struct sockaddr_storage self; } tcp_server_launch_t; @@ -443,7 +443,7 @@ tcp_server_loop(void *aux) tsl = malloc(sizeof(tcp_server_launch_t)); tsl->start = ts->start; tsl->opaque = ts->opaque; - slen = sizeof(struct sockaddr_in); + slen = sizeof(struct sockaddr_storage); tsl->fd = accept(ts->serverfd, (struct sockaddr *)&tsl->peer, &slen); @@ -455,7 +455,7 @@ tcp_server_loop(void *aux) } - slen = sizeof(struct sockaddr_in); + slen = sizeof(struct sockaddr_storage); if(getsockname(tsl->fd, (struct sockaddr *)&tsl->self, &slen)) { close(tsl->fd); free(tsl); diff --git a/src/tcp.h b/src/tcp.h index e9d9534c..b7a7c876 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -27,8 +27,8 @@ int tcp_connect(const char *hostname, int port, char *errbuf, size_t errbufsize, int timeout); typedef void (tcp_server_callback_t)(int fd, void *opaque, - struct sockaddr_in *peer, - struct sockaddr_in *self); + struct sockaddr_storage *peer, + struct sockaddr_storage *self); void *tcp_server_create(int port, tcp_server_callback_t *start, void *opaque); From b08a3c40d7829f3671fdddfc1840bd16960891c6 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 03:05:21 +0100 Subject: [PATCH 295/503] Make http server IPv6 ready --- src/http.c | 24 ++++++++++++++---------- src/http.h | 4 ++-- src/tcp.c | 25 +++++++++++++++++++++++++ src/tcp.h | 2 ++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/http.c b/src/http.c index 610e72e3..c668a02f 100644 --- a/src/http.c +++ b/src/http.c @@ -247,9 +247,12 @@ void http_error(http_connection_t *hc, int error) { const char *errtxt = http_rc2str(error); + char *addrstr = (char*)malloc(50); + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50); tvhlog(LOG_ERR, "HTTP", "%s: %s -- %d", - inet_ntoa(hc->hc_peer->sin_addr), hc->hc_url, error); + addrstr, hc->hc_url, error); + free(addrstr); htsbuf_queue_flush(&hc->hc_reply); @@ -315,10 +318,13 @@ http_access_verify(http_connection_t *hc, int mask) { const char *ticket_id = http_arg_get(&hc->hc_req_args, "ticket"); - if(!access_ticket_verify(ticket_id, hc->hc_url)) { + if(!access_ticket_verify(ticket_id, hc->hc_url)) + { + char *addrstr = (char*)malloc(50); + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50); tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s", - inet_ntoa(hc->hc_peer->sin_addr), ticket_id, - hc->hc_url); + addrstr, ticket_id, hc->hc_url); + free(addrstr); return 0; } @@ -504,11 +510,9 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill) if(hc->hc_username != NULL) { hc->hc_representative = strdup(hc->hc_username); } else { - hc->hc_representative = malloc(30); + hc->hc_representative = malloc(50); /* Not threadsafe ? */ - snprintf(hc->hc_representative, 30, - "%s", inet_ntoa(hc->hc_peer->sin_addr)); - + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, hc->hc_representative, 50); } switch(hc->hc_version) { @@ -777,8 +781,8 @@ http_serve_requests(http_connection_t *hc, htsbuf_queue_t *spill) * */ static void -http_serve(int fd, void *opaque, struct sockaddr_in *peer, - struct sockaddr_in *self) +http_serve(int fd, void *opaque, struct sockaddr_storage *peer, + struct sockaddr_storage *self) { htsbuf_queue_t spill; http_connection_t hc; diff --git a/src/http.h b/src/http.h index 2a29b8a9..9e3d061c 100644 --- a/src/http.h +++ b/src/http.h @@ -39,8 +39,8 @@ typedef struct http_arg { typedef struct http_connection { int hc_fd; - struct sockaddr_in *hc_peer; - struct sockaddr_in *hc_self; + struct sockaddr_storage *hc_peer; + struct sockaddr_storage *hc_self; char *hc_representative; char *hc_url; diff --git a/src/tcp.c b/src/tcp.c index 84313663..3653e8b2 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -344,6 +344,31 @@ tcp_read_timeout(int fd, void *buf, size_t len, int timeout) } +/** + * + */ +char * +tcp_get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen) +{ + if(sa == NULL || s == NULL) + return NULL; + + switch(sa->sa_family) + { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), s, maxlen); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), s, maxlen); + break; + default: + strncpy(s, "Unknown AF", maxlen); + return NULL; + } + + return s; +} + /** * */ diff --git a/src/tcp.h b/src/tcp.h index b7a7c876..187a9d57 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -44,4 +44,6 @@ int tcp_write_queue(int fd, htsbuf_queue_t *q); int tcp_read_timeout(int fd, void *buf, size_t len, int timeout); +char *tcp_get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen); + #endif /* TCP_H_ */ From 0a4f32bfba5defb663363ab8e101891eb7a86aac Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 03:25:21 +0100 Subject: [PATCH 296/503] Make htsp_server and webui IPv6 ready --- src/htsp_server.c | 21 ++++++++++++--------- src/webui/comet.c | 15 ++++++++++++--- src/webui/webui.c | 13 ++++++++++--- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 75b15d41..e3f289ab 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -115,7 +115,7 @@ typedef struct htsp_connection { LIST_ENTRY(htsp_connection) htsp_link; int htsp_fd; - struct sockaddr_in *htsp_peer; + struct sockaddr_storage *htsp_peer; uint32_t htsp_version; @@ -464,19 +464,22 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) htsmsg_add_str(out, "channelName", ch->ch_name); if(ch->ch_icon != NULL) { uint32_t id; - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrlen; if ((id = imagecache_get_id(ch->ch_icon))) { size_t p = 0; char url[256]; + char buf[50]; if (htsp->htsp_version < 8) { addrlen = sizeof(addr); getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen); + tcp_get_ip_str((struct sockaddr*)&addr, buf, 50); strcpy(url, "http://"); p = strlen(url); - inet_ntop(AF_INET, &addr.sin_addr, url+p, sizeof(url)-p); - p = strlen(url); - p += snprintf(url+p, sizeof(url)-p, ":%hd%s", + p += snprintf(url+p, sizeof(url)-p, "%s%s%s:%hd%s", + (addr.ss_family == AF_INET6)?"[":"", + buf, + (addr.ss_family == AF_INET6)?"]":"", tvheadend_webui_port, tvheadend_webroot ?: ""); } @@ -1871,14 +1874,14 @@ htsp_write_scheduler(void *aux) * */ static void -htsp_serve(int fd, void *opaque, struct sockaddr_in *source, - struct sockaddr_in *self) +htsp_serve(int fd, void *opaque, struct sockaddr_storage *source, + struct sockaddr_storage *self) { htsp_connection_t htsp; - char buf[30]; + char buf[50]; htsp_subscription_t *s; - snprintf(buf, sizeof(buf), "%s", inet_ntoa(source->sin_addr)); + tcp_get_ip_str((struct sockaddr*)source, buf, 50); memset(&htsp, 0, sizeof(htsp_connection_t)); diff --git a/src/webui/comet.c b/src/webui/comet.c index a540dbf6..aaf42ae0 100644 --- a/src/webui/comet.c +++ b/src/webui/comet.c @@ -32,6 +32,7 @@ #include "http.h" #include "webui/webui.h" #include "access.h" +#include "tcp.h" static pthread_mutex_t comet_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t comet_cond = PTHREAD_COND_INITIALIZER; @@ -153,16 +154,24 @@ comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb) static void comet_serverIpPort(http_connection_t *hc, comet_mailbox_t *cmb) { - char buf[INET_ADDRSTRLEN + 1]; + char buf[50]; + uint32_t port; - inet_ntop(AF_INET, &hc->hc_self->sin_addr, buf, sizeof(buf)); + tcp_get_ip_str((struct sockaddr*)hc->hc_self, buf, 50); + + if(hc->hc_self->ss_family == AF_INET) + port = ((struct sockaddr_in*)hc->hc_self)->sin_port; + else if(hc->hc_self->ss_family == AF_INET6) + port = ((struct sockaddr_in6*)hc->hc_self)->sin6_port; + else + port = 0; htsmsg_t *m = htsmsg_create_map(); htsmsg_add_str(m, "notificationClass", "setServerIpPort"); htsmsg_add_str(m, "ip", buf); - htsmsg_add_u32(m, "port", ntohs(hc->hc_self->sin_port)); + htsmsg_add_u32(m, "port", ntohs(port)); if(cmb->cmb_messages == NULL) cmb->cmb_messages = htsmsg_create_list(); diff --git a/src/webui/webui.c b/src/webui/webui.c index 3e1e525c..a7262115 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -45,6 +45,7 @@ #include "dvb/dvb.h" #include "dvb/dvb_support.h" #include "imagecache.h" +#include "tcp.h" /** * @@ -563,6 +564,7 @@ http_stream_service(http_connection_t *hc, service_t *service) const char *str; size_t qsize; const char *name; + char addrbuf[50]; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -589,8 +591,9 @@ http_stream_service(http_connection_t *hc, service_t *service) flags = 0; } + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50); s = subscription_create_from_service(service, "HTTP", st, flags, - inet_ntoa(hc->hc_peer->sin_addr), + addrbuf, hc->hc_username, http_arg_get(&hc->hc_args, "User-Agent")); if(s) { @@ -624,10 +627,12 @@ http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi) th_subscription_t *s; streaming_queue_t sq; const char *name; + char addrbuf[50]; streaming_queue_init(&sq, SMT_PACKET); + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50); s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st, - inet_ntoa(hc->hc_peer->sin_addr), + addrbuf, hc->hc_username, http_arg_get(&hc->hc_args, "User-Agent")); name = strdupa(tdmi->tdmi_identifier); @@ -661,6 +666,7 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) char *str; size_t qsize; const char *name; + char addrbuf[50]; mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux")); if(mc == MC_UNKNOWN) { @@ -687,8 +693,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) flags = 0; } + tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50); s = subscription_create_from_channel(ch, priority, "HTTP", st, flags, - inet_ntoa(hc->hc_peer->sin_addr), + addrbuf, hc->hc_username, http_arg_get(&hc->hc_args, "User-Agent")); From 8ba3bbdc58c492ead416b3094526a231a909b589 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 05:32:02 +0100 Subject: [PATCH 297/503] Make access system ipv6 aware --- src/access.c | 158 +++++++++++++++++++++++++++++++++++++++++++-------- src/access.h | 2 + 2 files changed, 136 insertions(+), 24 deletions(-) diff --git a/src/access.c b/src/access.c index 7814025e..4be611df 100644 --- a/src/access.c +++ b/src/access.c @@ -149,6 +149,70 @@ access_ticket_verify(const char *id, const char *resource) return 0; } +/** + * + */ +static int +netmask_verify(access_entry_t *ae, struct sockaddr *src) +{ + int isv4v6 = 0; + uint32_t v4v6 = 0; + + if(src->sa_family == AF_INET6) + { + struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr); + uint32_t *a32 = (uint32_t*)in6->s6_addr; + if(a32[0] == 0 && a32[1] == 0 && ntohl(a32[2]) == 0x0000FFFFu) + { + isv4v6 = 1; + v4v6 = ntohl(a32[3]); + } + } + + if(ae->ae_ipv6 == 0 && src->sa_family == AF_INET) + { + struct sockaddr_in *in4 = (struct sockaddr_in *)src; + uint32_t b = ntohl(in4->sin_addr.s_addr); + return (b & ae->ae_netmask) == ae->ae_network; + } + else if(ae->ae_ipv6 == 0 && isv4v6) + { + return (v4v6 & ae->ae_netmask) == ae->ae_network; + } + else if(ae->ae_ipv6 && isv4v6) + { + return 0; + } + else if(ae->ae_ipv6 && src->sa_family == AF_INET6) + { + struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr); + uint8_t *a8 = (uint8_t*)in6->s6_addr; + uint8_t *m8 = (uint8_t*)ae->ae_ip6.s6_addr; + int slen = ae->ae_prefixlen; + uint32_t apos = 0; + uint8_t lastMask = (0xFFu << (8 - (slen % 8))); + + if(slen < 0 || slen > 128) + return 0; + + while(slen >= 8) + { + if(a8[apos] != m8[apos]) + return 0; + + apos += 1; + slen -= 8; + } + + if(slen == 0) + return 1; + + return (a8[apos] & lastMask) == (m8[apos] & lastMask); + } + + return 0; +} + /** * */ @@ -157,8 +221,6 @@ access_verify(const char *username, const char *password, struct sockaddr *src, uint32_t mask) { uint32_t bits = 0; - struct sockaddr_in *si = (struct sockaddr_in *)src; - uint32_t b = ntohl(si->sin_addr.s_addr); access_entry_t *ae; if(username != NULL && superuser_username != NULL && @@ -182,7 +244,7 @@ access_verify(const char *username, const char *password, continue; /* username/password mismatch */ } - if((b & ae->ae_netmask) != ae->ae_network) + if(!netmask_verify(ae, src)) continue; /* IP based access mismatches */ bits |= ae->ae_rights; @@ -200,8 +262,6 @@ access_get_hashed(const char *username, const uint8_t digest[20], const uint8_t *challenge, struct sockaddr *src, int *entrymatch) { - struct sockaddr_in *si = (struct sockaddr_in *)src; - uint32_t b = ntohl(si->sin_addr.s_addr); access_entry_t *ae; SHA_CTX shactx; uint8_t d[20]; @@ -226,7 +286,7 @@ access_get_hashed(const char *username, const uint8_t digest[20], if(!ae->ae_enabled) continue; - if((b & ae->ae_netmask) != ae->ae_network) + if(!netmask_verify(ae, src)) continue; /* IP based access mismatches */ SHA1_Init(&shactx); @@ -253,8 +313,6 @@ access_get_hashed(const char *username, const uint8_t digest[20], uint32_t access_get_by_addr(struct sockaddr *src) { - struct sockaddr_in *si = (struct sockaddr_in *)src; - uint32_t b = ntohl(si->sin_addr.s_addr); access_entry_t *ae; uint32_t r = 0; @@ -263,7 +321,7 @@ access_get_by_addr(struct sockaddr *src) if(ae->ae_username[0] != '*') continue; - if((b & ae->ae_netmask) != ae->ae_network) + if(!netmask_verify(ae, src)) continue; /* IP based access mismatches */ r |= ae->ae_rights; @@ -300,21 +358,50 @@ access_set_prefix(access_entry_t *ae, const char *prefix) return; strcpy(buf, prefix); - p = strchr(buf, '/'); - if(p) { - *p++ = 0; - prefixlen = atoi(p); - if(prefixlen > 32) - return; - } else { - prefixlen = 32; + + if(strchr(buf, ':') != NULL) + ae->ae_ipv6 = 1; + else + ae->ae_ipv6 = 0; + + if(ae->ae_ipv6) + { + p = strchr(buf, '/'); + if(p) + { + *p++ = 0; + prefixlen = atoi(p); + if(prefixlen > 128) + return; + } else { + prefixlen = 128; + } + + ae->ae_prefixlen = prefixlen; + inet_pton(AF_INET6, buf, &ae->ae_ip6); + + ae->ae_netmask = 0xffffffff; + ae->ae_network = 0x00000000; } + else + { + p = strchr(buf, '/'); + if(p) + { + *p++ = 0; + prefixlen = atoi(p); + if(prefixlen > 32) + return; + } else { + prefixlen = 32; + } - ae->ae_ip.s_addr = inet_addr(buf); - ae->ae_prefixlen = prefixlen; + ae->ae_ip.s_addr = inet_addr(buf); + ae->ae_prefixlen = prefixlen; - ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0; - ae->ae_network = ntohl(ae->ae_ip.s_addr) & ae->ae_netmask; + ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0; + ae->ae_network = ntohl(ae->ae_ip.s_addr) & ae->ae_netmask; + } } @@ -378,6 +465,7 @@ static htsmsg_t * access_record_build(access_entry_t *ae) { htsmsg_t *e = htsmsg_create_map(); + char addrbuf[50]; char buf[100]; htsmsg_add_u32(e, "enabled", !!ae->ae_enabled); @@ -386,7 +474,13 @@ access_record_build(access_entry_t *ae) htsmsg_add_str(e, "password", ae->ae_password); htsmsg_add_str(e, "comment", ae->ae_comment); - snprintf(buf, sizeof(buf), "%s/%d", inet_ntoa(ae->ae_ip), ae->ae_prefixlen); + if(ae->ae_ipv6) + { + inet_ntop(AF_INET6, &ae->ae_ip6, addrbuf, 50); + snprintf(buf, sizeof(buf), "%s/%d", addrbuf, ae->ae_prefixlen); + } + else + snprintf(buf, sizeof(buf), "%s/%d", inet_ntoa(ae->ae_ip), ae->ae_prefixlen); htsmsg_add_str(e, "prefix", buf); htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0); @@ -558,17 +652,33 @@ access_init(int createdefault) ae = access_entry_find(NULL, 1); free(ae->ae_comment); - ae->ae_comment = strdup("Default access entry"); + ae->ae_comment = strdup("Default IPv6 access entry"); ae->ae_enabled = 1; ae->ae_rights = 0xffffffff; + ae->ae_ipv6 = 1; + + r = access_record_build(ae); + dtable_record_store(dt, ae->ae_id, r); + htsmsg_destroy(r); + + ae = access_entry_find(NULL, 1); + + free(ae->ae_comment); + ae->ae_comment = strdup("Default IPv4 access entry"); + + ae->ae_enabled = 1; + ae->ae_rights = 0xffffffff; + + ae->ae_ipv6 = 0; + r = access_record_build(ae); dtable_record_store(dt, ae->ae_id, r); htsmsg_destroy(r); tvhlog(LOG_WARNING, "accesscontrol", - "Created default wide open access controle entry"); + "Created default wide open access controle entrys"); } /* Load superuser account */ diff --git a/src/access.h b/src/access.h index 35bb1d83..3562b0ac 100644 --- a/src/access.h +++ b/src/access.h @@ -31,7 +31,9 @@ typedef struct access_entry { char *ae_username; char *ae_password; char *ae_comment; + int ae_ipv6; struct in_addr ae_ip; + struct in6_addr ae_ip6; int ae_prefixlen; int ae_enabled; From a3ff11f7823940b33285f3fa54ed9cd7670ffb4d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 11:01:21 +0000 Subject: [PATCH 298/503] timeshift: add periodic status message indicating buffer state etc. --- src/dvr/dvr_rec.c | 1 + src/htsp_server.c | 39 ++++++++++++------- src/plumbing/globalheaders.c | 2 + src/plumbing/tsfix.c | 1 + src/streaming.c | 11 +++++- src/timeshift.h | 8 ++++ src/timeshift/timeshift_reader.c | 67 +++++++++++++++++++++++++++++--- src/timeshift/timeshift_writer.c | 1 + src/tvheadend.h | 6 ++- src/webui/webui.c | 1 + 10 files changed, 115 insertions(+), 22 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 0c98f533..39b929e7 100755 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -541,6 +541,7 @@ dvr_thread(void *aux) case SMT_SPEED: case SMT_SKIP: case SMT_SIGNAL_STATUS: + case SMT_TIMESHIFT_STATUS: break; case SMT_EXIT: diff --git a/src/htsp_server.c b/src/htsp_server.c index 75b15d41..308f64ae 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2169,11 +2169,7 @@ const static char frametypearray[PKT_NTYPES] = { * Build a htsmsg from a th_pkt and enqueue it on our HTSP service */ static void -#if ENABLE_TIMESHIFT -htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt, uint64_t timeshift) -#else htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) -#endif { htsmsg_t *m; htsp_msg_t *hm; @@ -2201,12 +2197,6 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt) htsmsg_add_u32(m, "stream", pkt->pkt_componentindex); htsmsg_add_u32(m, "com", pkt->pkt_commercial); -#if ENABLE_TIMESHIFT - if (timeshift) - htsmsg_add_s64(m, "timeshift", timeshift); -#endif - - if(pkt->pkt_pts != PTS_UNSET) { int64_t pts = hs->hs_90khz ? pkt->pkt_pts : ts_rescale(pkt->pkt_pts, 1000000); htsmsg_add_s64(m, "pts", pts); @@ -2451,6 +2441,25 @@ htsp_subscription_skip(htsp_subscription_t *hs, streaming_skip_t *skip) htsp_send(hs->hs_htsp, m, NULL, &hs->hs_q, 0); } +/** + * + */ +#if ENABLE_TIMESHIFT +static void +htsp_subscription_timeshift_status(htsp_subscription_t *hs, timeshift_status_t *status) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "method", "timeshiftStatus"); + htsmsg_add_u32(m, "full", status->full); + htsmsg_add_s64(m, "shift", hs->hs_90khz ? status->shift : ts_rescale(status->shift, 1000000)); + if (status->pts_start != PTS_UNSET) + htsmsg_add_s64(m, "start", hs->hs_90khz ? status->pts_start : ts_rescale(status->pts_start, 1000000)) ; + if (status->pts_end != PTS_UNSET) + htsmsg_add_s64(m, "end", hs->hs_90khz ? status->pts_end : ts_rescale(status->pts_end, 1000000)) ; + htsp_send(hs->hs_htsp, m, NULL, &hs->hs_q, 0); +} +#endif + /** * */ @@ -2461,11 +2470,7 @@ htsp_streaming_input(void *opaque, streaming_message_t *sm) switch(sm->sm_type) { case SMT_PACKET: -#if ENABLE_TIMESHIFT - htsp_stream_deliver(hs, sm->sm_data, sm->sm_timeshift); -#else htsp_stream_deliver(hs, sm->sm_data); -#endif // reference is transfered sm->sm_data = NULL; break; @@ -2503,6 +2508,12 @@ htsp_streaming_input(void *opaque, streaming_message_t *sm) case SMT_SPEED: htsp_subscription_speed(hs, sm->sm_code); break; + + case SMT_TIMESHIFT_STATUS: +#if ENABLE_TIMESHIFT + htsp_subscription_timeshift_status(hs, sm->sm_data); +#endif + break; } streaming_msg_free(sm); } diff --git a/src/plumbing/globalheaders.c b/src/plumbing/globalheaders.c index 509da87b..7eda3b02 100644 --- a/src/plumbing/globalheaders.c +++ b/src/plumbing/globalheaders.c @@ -257,6 +257,7 @@ gh_hold(globalheaders_t *gh, streaming_message_t *sm) case SMT_MPEGTS: case SMT_SPEED: case SMT_SKIP: + case SMT_TIMESHIFT_STATUS: streaming_target_deliver2(gh->gh_output, sm); break; } @@ -287,6 +288,7 @@ gh_pass(globalheaders_t *gh, streaming_message_t *sm) case SMT_MPEGTS: case SMT_SKIP: case SMT_SPEED: + case SMT_TIMESHIFT_STATUS: streaming_target_deliver2(gh->gh_output, sm); break; diff --git a/src/plumbing/tsfix.c b/src/plumbing/tsfix.c index c54b76fc..858e59ed 100644 --- a/src/plumbing/tsfix.c +++ b/src/plumbing/tsfix.c @@ -371,6 +371,7 @@ tsfix_input(void *opaque, streaming_message_t *sm) case SMT_MPEGTS: case SMT_SPEED: case SMT_SKIP: + case SMT_TIMESHIFT_STATUS: break; } diff --git a/src/streaming.c b/src/streaming.c index 3173d3b0..2e0cace3 100755 --- a/src/streaming.c +++ b/src/streaming.c @@ -23,6 +23,7 @@ #include "packet.h" #include "atomic.h" #include "service.h" +#include "timeshift.h" void streaming_pad_init(streaming_pad_t *sp) @@ -139,7 +140,6 @@ streaming_msg_create(streaming_message_type_t type) sm->sm_type = type; #if ENABLE_TIMESHIFT sm->sm_time = 0; - sm->sm_timeshift = 0; #endif return sm; } @@ -195,7 +195,6 @@ streaming_msg_clone(streaming_message_t *src) dst->sm_type = src->sm_type; #if ENABLE_TIMESHIFT dst->sm_time = src->sm_time; - dst->sm_timeshift = src->sm_timeshift; #endif switch(src->sm_type) { @@ -220,6 +219,11 @@ streaming_msg_clone(streaming_message_t *src) memcpy(dst->sm_data, src->sm_data, sizeof(signal_status_t)); break; + case SMT_TIMESHIFT_STATUS: + dst->sm_data = malloc(sizeof(timeshift_status_t)); + memcpy(dst->sm_data, src->sm_data, sizeof(timeshift_status_t)); + break; + case SMT_SPEED: case SMT_STOP: case SMT_SERVICE_STATUS: @@ -286,6 +290,9 @@ streaming_msg_free(streaming_message_t *sm) case SMT_SKIP: case SMT_SIGNAL_STATUS: +#if ENABLE_TIMESHIFT + case SMT_TIMESHIFT_STATUS: +#endif free(sm->sm_data); break; diff --git a/src/timeshift.h b/src/timeshift.h index 342c7c66..3c6fc0ab 100644 --- a/src/timeshift.h +++ b/src/timeshift.h @@ -30,6 +30,14 @@ extern size_t timeshift_max_size; extern size_t timeshift_total_size; extern pthread_mutex_t timeshift_size_lock; +typedef struct timeshift_status +{ + int full; + int64_t shift; + int64_t pts_start; + int64_t pts_end; +} timeshift_status_t; + void timeshift_init ( void ); void timeshift_term ( void ); void timeshift_save ( void ); diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 5bb509a1..ad948d96 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -187,6 +187,36 @@ static streaming_message_t *_timeshift_find_sstart return ti ? ti->data : NULL; } +static timeshift_index_iframe_t *_timeshift_first_frame + ( timeshift_t *ts ) +{ + int end; + timeshift_index_iframe_t *tsi = NULL; + timeshift_file_t *tsf = timeshift_filemgr_last(ts); + while (tsf && !tsi) { + if (!(tsi = TAILQ_FIRST(&tsf->iframes))) + tsf = timeshift_filemgr_next(tsf, &end, 0); + } + if (tsf) + tsf->refcount--; + return tsi; +} + +static timeshift_index_iframe_t *_timeshift_last_frame + ( timeshift_t *ts ) +{ + int end; + timeshift_index_iframe_t *tsi = NULL; + timeshift_file_t *tsf = timeshift_filemgr_get(ts, ts->ondemand); + while (tsf && !tsi) { + if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) + tsf = timeshift_filemgr_prev(tsf, &end, 0); + } + if (tsf) + tsf->refcount--; + return tsi; +} + static int _timeshift_skip ( timeshift_t *ts, int64_t req_time, int64_t cur_time, timeshift_file_t *cur_file, timeshift_file_t **new_file, @@ -351,8 +381,8 @@ static int _timeshift_flush_to_live if (!*sm) break; if ((*sm)->sm_type == SMT_PACKET) { pts = ((th_pkt_t*)(*sm)->sm_data)->pkt_pts; - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, - ts->id, (*sm)->sm_time, pts, (*sm)->sm_timeshift ); + tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t, + ts->id, (*sm)->sm_time, pts); } streaming_target_deliver2(ts->output, *sm); *sm = NULL; @@ -360,7 +390,6 @@ static int _timeshift_flush_to_live return 0; } - /* ************************************************************************** * Thread * *************************************************************************/ @@ -380,6 +409,7 @@ void *timeshift_reader ( void *p ) streaming_message_t *sm = NULL, *ctrl = NULL; timeshift_index_iframe_t *tsi = NULL; streaming_skip_t *skip = NULL; + time_t last_status = 0; /* Poll */ struct epoll_event ev = { 0 }; @@ -391,6 +421,11 @@ void *timeshift_reader ( void *p ) /* Output */ while (run) { + // Note: Previously we allowed unlimited wait, but we now must wake periodically + // to output status message + if (wait < 0 || wait > 1000) + wait = 1000; + /* Wait for data */ if(wait) nfds = epoll_wait(efd, &ev, 1, wait); @@ -565,6 +600,28 @@ void *timeshift_reader ( void *p ) } } + /* Status message */ + if (now >= (last_status + 1000000)) { + streaming_message_t *tsm; + timeshift_status_t *status; + timeshift_index_iframe_t *fst, *lst; + status = calloc(1, sizeof(timeshift_status_t)); + fst = _timeshift_first_frame(ts); + lst = _timeshift_last_frame(ts); + status->full = ts->full; + status->shift = ts->state <= TS_LIVE ? 0 : ts_rescale_i(now - last_time, 1000000); + if (lst && fst && lst != fst && ts->pts_delta != PTS_UNSET) { + status->pts_start = ts_rescale_i(fst->time - ts->pts_delta, 1000000); + status->pts_end = ts_rescale_i(lst->time - ts->pts_delta, 1000000); + } else { + status->pts_start = PTS_UNSET; + status->pts_end = PTS_UNSET; + } + tsm = streaming_msg_create_data(SMT_TIMESHIFT_STATUS, status); + streaming_target_deliver2(ts->output, tsm); + last_status = now; + } + /* Done */ if (!run || !cur_file || ((ts->state != TS_PLAY && !skip))) { pthread_mutex_unlock(&ts->state_mutex); @@ -644,16 +701,16 @@ void *timeshift_reader ( void *p ) (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { - sm->sm_timeshift = now - sm->sm_time; #ifndef TSHFT_TRACE if (skip) #endif { time_t pts = 0; + int64_t delta = now - sm->sm_time; if (sm->sm_type == SMT_PACKET) pts = ((th_pkt_t*)sm->sm_data)->pkt_pts; tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, - ts->id, sm->sm_time, pts, sm->sm_timeshift ); + ts->id, sm->sm_time, pts, delta); } streaming_target_deliver2(ts->output, sm); last_time = sm->sm_time; diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index 8bf6be03..bf944dbe 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -257,6 +257,7 @@ static void _process_msg /* Status */ case SMT_NOSTART: case SMT_SERVICE_STATUS: + case SMT_TIMESHIFT_STATUS: break; /* Store */ diff --git a/src/tvheadend.h b/src/tvheadend.h index d8446be7..7b9e95be 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -322,6 +322,11 @@ typedef enum { */ SMT_SKIP, + /** + * Timeshift status + */ + SMT_TIMESHIFT_STATUS, + } streaming_message_type_t; #define SMT_TO_MASK(x) (1 << ((unsigned int)x)) @@ -359,7 +364,6 @@ typedef struct streaming_message { streaming_message_type_t sm_type; #if ENABLE_TIMESHIFT int64_t sm_time; - uint64_t sm_timeshift; #endif union { void *sm_data; diff --git a/src/webui/webui.c b/src/webui/webui.c index 3e1e525c..29fddf05 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -247,6 +247,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, case SMT_SKIP: case SMT_SPEED: case SMT_SIGNAL_STATUS: + case SMT_TIMESHIFT_STATUS: break; case SMT_NOSTART: From 815fb013aeff8318f3398327c062bf42c5da8969 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 12:22:31 +0000 Subject: [PATCH 299/503] timeshift: forgot to include subscriptionId in timeshiftStatus msg. --- src/htsp_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/htsp_server.c b/src/htsp_server.c index 308f64ae..79f1c946 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2450,6 +2450,7 @@ htsp_subscription_timeshift_status(htsp_subscription_t *hs, timeshift_status_t * { htsmsg_t *m = htsmsg_create_map(); htsmsg_add_str(m, "method", "timeshiftStatus"); + htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); htsmsg_add_u32(m, "full", status->full); htsmsg_add_s64(m, "shift", hs->hs_90khz ? status->shift : ts_rescale(status->shift, 1000000)); if (status->pts_start != PTS_UNSET) From 3b758e5ce7e6587aeffb7280b863c636ad0f3b20 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 12:32:54 +0100 Subject: [PATCH 300/503] Add commandline option to switch on IPv6 support --- src/main.c | 7 ++++--- src/tcp.c | 8 ++++++-- src/tcp.h | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index c6e0254d..40261213 100644 --- a/src/main.c +++ b/src/main.c @@ -359,7 +359,8 @@ main(int argc, char **argv) opt_debug = 0, opt_syslog = 0, opt_uidebug = 0, - opt_abort = 0; + opt_abort = 0, + opt_ipv6 = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, @@ -391,8 +392,8 @@ main(int argc, char **argv) { 'a', "adapters", "Use only specified DVB adapters", OPT_STR, &opt_dvb_adapters }, #endif - { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, + { '6', "ipv6", "Listen on IPv6", OPT_BOOL, &opt_ipv6 }, { 0, "http_port", "Specify alternative http port", OPT_INT, &tvheadend_webui_port }, { 0, "http_root", "Specify alternative http webroot", @@ -606,7 +607,7 @@ main(int argc, char **argv) timeshift_init(); #endif - tcp_server_init(); + tcp_server_init(opt_ipv6); http_server_init(); webui_init(); diff --git a/src/tcp.c b/src/tcp.c index 3653e8b2..5ccf1bac 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -35,6 +35,7 @@ #include "tcp.h" #include "tvheadend.h" +int tcp_preferred_address_family = AF_INET; /** * @@ -526,7 +527,7 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) ressave = res; while(res) { - if(res->ai_family == AF_INET6) + if(res->ai_family == tcp_preferred_address_family) { use = res; break; @@ -574,10 +575,13 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) * */ void -tcp_server_init(void) +tcp_server_init(int opt_ipv6) { pthread_t tid; + if(opt_ipv6) + tcp_preferred_address_family = AF_INET6; + tcp_server_epoll_fd = epoll_create(10); pthread_create(&tid, NULL, tcp_server_loop, NULL); } diff --git a/src/tcp.h b/src/tcp.h index 187a9d57..0c061e43 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -21,7 +21,9 @@ #include "htsbuf.h" -void tcp_server_init(void); +extern int tcp_preferred_address_family; + +void tcp_server_init(int opt_ipv6); int tcp_connect(const char *hostname, int port, char *errbuf, size_t errbufsize, int timeout); From 89c3271368cd2807e1976f8dc7314bbe4ca32b27 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 15:27:44 +0100 Subject: [PATCH 301/503] Convert prefix to a , seperated list of multiple prefixes --- src/access.c | 246 +++++++++++++++++++++++++++++++-------------------- src/access.h | 21 +++-- 2 files changed, 163 insertions(+), 104 deletions(-) diff --git a/src/access.c b/src/access.c index 4be611df..514e8707 100644 --- a/src/access.c +++ b/src/access.c @@ -155,6 +155,7 @@ access_ticket_verify(const char *id, const char *resource) static int netmask_verify(access_entry_t *ae, struct sockaddr *src) { + access_ipmask_t *ai; int isv4v6 = 0; uint32_t v4v6 = 0; @@ -169,45 +170,48 @@ netmask_verify(access_entry_t *ae, struct sockaddr *src) } } - if(ae->ae_ipv6 == 0 && src->sa_family == AF_INET) + LIST_FOREACH(ai, &ae->ae_ipmasks, ai_link) { - struct sockaddr_in *in4 = (struct sockaddr_in *)src; - uint32_t b = ntohl(in4->sin_addr.s_addr); - return (b & ae->ae_netmask) == ae->ae_network; - } - else if(ae->ae_ipv6 == 0 && isv4v6) - { - return (v4v6 & ae->ae_netmask) == ae->ae_network; - } - else if(ae->ae_ipv6 && isv4v6) - { - return 0; - } - else if(ae->ae_ipv6 && src->sa_family == AF_INET6) - { - struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr); - uint8_t *a8 = (uint8_t*)in6->s6_addr; - uint8_t *m8 = (uint8_t*)ae->ae_ip6.s6_addr; - int slen = ae->ae_prefixlen; - uint32_t apos = 0; - uint8_t lastMask = (0xFFu << (8 - (slen % 8))); - - if(slen < 0 || slen > 128) - return 0; - - while(slen >= 8) + if(ai->ai_ipv6 == 0 && src->sa_family == AF_INET) { - if(a8[apos] != m8[apos]) - return 0; - - apos += 1; - slen -= 8; + struct sockaddr_in *in4 = (struct sockaddr_in *)src; + uint32_t b = ntohl(in4->sin_addr.s_addr); + if((b & ai->ai_netmask) == ai->ai_network) + return 1; } + else if(ai->ai_ipv6 == 0 && isv4v6) + { + if((v4v6 & ai->ai_netmask) == ai->ai_network) + return 1; + } + else if(ai->ai_ipv6 && isv4v6) + { + continue; + } + else if(ai->ai_ipv6 && src->sa_family == AF_INET6) + { + struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr); + uint8_t *a8 = (uint8_t*)in6->s6_addr; + uint8_t *m8 = (uint8_t*)ai->ai_ip6.s6_addr; + int slen = ai->ai_prefixlen; + uint32_t apos = 0; + uint8_t lastMask = (0xFFu << (8 - (slen % 8))); - if(slen == 0) - return 1; + if(slen < 0 || slen > 128) + continue; - return (a8[apos] & lastMask) == (m8[apos] & lastMask); + while(slen >= 8) + { + if(a8[apos] != m8[apos]) + continue; + + apos += 1; + slen -= 8; + } + + if(slen == 0 || (a8[apos] & lastMask) == (m8[apos] & lastMask)) + return 1; + } } return 0; @@ -351,56 +355,89 @@ static void access_set_prefix(access_entry_t *ae, const char *prefix) { char buf[100]; + char tokbuf[4096]; int prefixlen; - char *p; + char *p, *tok, *saveptr; + access_ipmask_t *ai; - if(strlen(prefix) > 90) - return; - - strcpy(buf, prefix); - - if(strchr(buf, ':') != NULL) - ae->ae_ipv6 = 1; - else - ae->ae_ipv6 = 0; - - if(ae->ae_ipv6) + while((ai = LIST_FIRST(&ae->ae_ipmasks)) != NULL) { - p = strchr(buf, '/'); - if(p) - { - *p++ = 0; - prefixlen = atoi(p); - if(prefixlen > 128) - return; - } else { - prefixlen = 128; - } - - ae->ae_prefixlen = prefixlen; - inet_pton(AF_INET6, buf, &ae->ae_ip6); - - ae->ae_netmask = 0xffffffff; - ae->ae_network = 0x00000000; + LIST_REMOVE(ai, ai_link); + free(ai); } - else + + strncpy(tokbuf, prefix, 4095); + tokbuf[4095] = 0; + tok = strtok_r(tokbuf, ",;| ", &saveptr); + + while(tok != NULL) { - p = strchr(buf, '/'); - if(p) + ai = calloc(1, sizeof(access_ipmask_t)); + + if(strlen(tok) > 90 || strlen(tok) == 0) { - *p++ = 0; - prefixlen = atoi(p); - if(prefixlen > 32) - return; - } else { - prefixlen = 32; + free(ai); + tok = strtok_r(NULL, ",;| ", &saveptr); + continue; } - ae->ae_ip.s_addr = inet_addr(buf); - ae->ae_prefixlen = prefixlen; + strcpy(buf, tok); - ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0; - ae->ae_network = ntohl(ae->ae_ip.s_addr) & ae->ae_netmask; + if(strchr(buf, ':') != NULL) + ai->ai_ipv6 = 1; + else + ai->ai_ipv6 = 0; + + if(ai->ai_ipv6) + { + p = strchr(buf, '/'); + if(p) + { + *p++ = 0; + prefixlen = atoi(p); + if(prefixlen > 128) + { + free(ai); + tok = strtok_r(NULL, ",;| ", &saveptr); + continue; + } + } else { + prefixlen = 128; + } + + ai->ai_prefixlen = prefixlen; + inet_pton(AF_INET6, buf, &ai->ai_ip6); + + ai->ai_netmask = 0xffffffff; + ai->ai_network = 0x00000000; + } + else + { + p = strchr(buf, '/'); + if(p) + { + *p++ = 0; + prefixlen = atoi(p); + if(prefixlen > 32) + { + free(ai); + tok = strtok_r(NULL, ",;| ", &saveptr); + continue; + } + } else { + prefixlen = 32; + } + + ai->ai_ip.s_addr = inet_addr(buf); + ai->ai_prefixlen = prefixlen; + + ai->ai_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0; + ai->ai_network = ntohl(ai->ai_ip.s_addr) & ai->ai_netmask; + } + + LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + + tok = strtok_r(NULL, ",;| ", &saveptr); } } @@ -450,6 +487,14 @@ access_entry_find(const char *id, int create) static void access_entry_destroy(access_entry_t *ae) { + access_ipmask_t *ai; + + while((ai = LIST_FIRST(&ae->ae_ipmasks)) != NULL) + { + LIST_REMOVE(ai, ai_link); + free(ai); + } + free(ae->ae_id); free(ae->ae_username); free(ae->ae_password); @@ -465,8 +510,12 @@ static htsmsg_t * access_record_build(access_entry_t *ae) { htsmsg_t *e = htsmsg_create_map(); + access_ipmask_t *ai; char addrbuf[50]; - char buf[100]; + char buf[4096]; + int pos = 0; + + memset(buf, 0, 4096); htsmsg_add_u32(e, "enabled", !!ae->ae_enabled); @@ -474,14 +523,22 @@ access_record_build(access_entry_t *ae) htsmsg_add_str(e, "password", ae->ae_password); htsmsg_add_str(e, "comment", ae->ae_comment); - if(ae->ae_ipv6) + LIST_FOREACH(ai, &ae->ae_ipmasks, ai_link) { - inet_ntop(AF_INET6, &ae->ae_ip6, addrbuf, 50); - snprintf(buf, sizeof(buf), "%s/%d", addrbuf, ae->ae_prefixlen); + if(sizeof(buf)-pos <= 0) + break; + + if(ai->ai_ipv6) + { + inet_ntop(AF_INET6, &ai->ai_ip6, addrbuf, 50); + pos += snprintf(buf+pos, sizeof(buf)-pos, ",%s/%d", addrbuf, ai->ai_prefixlen); + } + else + { + pos += snprintf(buf+pos, sizeof(buf)-pos, ",%s/%d", inet_ntoa(ai->ai_ip), ai->ai_prefixlen); + } } - else - snprintf(buf, sizeof(buf), "%s/%d", inet_ntoa(ae->ae_ip), ae->ae_prefixlen); - htsmsg_add_str(e, "prefix", buf); + htsmsg_add_str(e, "prefix", buf + 1); htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0); htsmsg_add_u32(e, "dvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0); @@ -631,6 +688,7 @@ access_init(int createdefault) dtable_t *dt; htsmsg_t *r, *m; access_entry_t *ae; + access_ipmask_t *ai; const char *s; static struct { @@ -652,33 +710,25 @@ access_init(int createdefault) ae = access_entry_find(NULL, 1); free(ae->ae_comment); - ae->ae_comment = strdup("Default IPv6 access entry"); + ae->ae_comment = strdup("Default access entry"); ae->ae_enabled = 1; ae->ae_rights = 0xffffffff; - ae->ae_ipv6 = 1; + ai = calloc(1, sizeof(access_ipmask_t)); + ai->ai_ipv6 = 0; + LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); - r = access_record_build(ae); - dtable_record_store(dt, ae->ae_id, r); - htsmsg_destroy(r); - - ae = access_entry_find(NULL, 1); - - free(ae->ae_comment); - ae->ae_comment = strdup("Default IPv4 access entry"); - - ae->ae_enabled = 1; - ae->ae_rights = 0xffffffff; - - ae->ae_ipv6 = 0; + ai = calloc(1, sizeof(access_ipmask_t)); + ai->ai_ipv6 = 1; + LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); r = access_record_build(ae); dtable_record_store(dt, ae->ae_id, r); htsmsg_destroy(r); tvhlog(LOG_WARNING, "accesscontrol", - "Created default wide open access controle entrys"); + "Created default wide open access controle entry"); } /* Load superuser account */ diff --git a/src/access.h b/src/access.h index 3562b0ac..466e380f 100644 --- a/src/access.h +++ b/src/access.h @@ -20,6 +20,20 @@ #define ACCESS_H_ +typedef struct access_ipmask { + LIST_ENTRY(access_ipmask) ai_link; + + int ai_ipv6; + + struct in_addr ai_ip; + struct in6_addr ai_ip6; + + int ai_prefixlen; + + uint32_t ai_network; + uint32_t ai_netmask; +} access_ipmask_t; + TAILQ_HEAD(access_entry_queue, access_entry); extern struct access_entry_queue access_entries; @@ -31,17 +45,12 @@ typedef struct access_entry { char *ae_username; char *ae_password; char *ae_comment; - int ae_ipv6; - struct in_addr ae_ip; - struct in6_addr ae_ip6; - int ae_prefixlen; int ae_enabled; uint32_t ae_rights; - uint32_t ae_network; /* derived from ae_ip */ - uint32_t ae_netmask; /* derived from ae_prefixlen */ + LIST_HEAD(, access_ipmask) ae_ipmasks; } access_entry_t; TAILQ_HEAD(access_ticket_queue, access_ticket); From 0400599d606e5781eb776f4d49e3ab4bdfca326a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 16:25:07 +0000 Subject: [PATCH 302/503] http: fix bug that causes tablemgr updates to hang Also added additional updates based on comet data do multiple clients would receive an update. --- src/webui/extjs.c | 8 ++++---- src/webui/static/app/tableeditor.js | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 1012eaa1..f6b33c7c 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -294,10 +294,10 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque) if(in != NULL) htsmsg_destroy(in); - if(out != NULL) { - htsmsg_json_serialize(out, hq, 0); - htsmsg_destroy(out); - } + if(out == NULL) + out = htsmsg_create_map(); + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); http_output_content(hc, "text/x-json; charset=UTF-8"); return 0; } diff --git a/src/webui/static/app/tableeditor.js b/src/webui/static/app/tableeditor.js index c3a1972d..dc04d1af 100644 --- a/src/webui/static/app/tableeditor.js +++ b/src/webui/static/app/tableeditor.js @@ -13,6 +13,11 @@ tvheadend.tableEditor = function(title, dtable, cm, rec, plugins, store, op : "get" } }); + + tvheadend.comet.on(dtable, function(m){ + if (m.reload) + store.reload(); + }); } function addRecord() { @@ -65,7 +70,6 @@ tvheadend.tableEditor = function(title, dtable, cm, rec, plugins, store, Ext.MessageBox.alert('Server Error', 'Unable to delete'); }, success : function(response, options) { - store.reload(); } }) } @@ -88,6 +92,7 @@ tvheadend.tableEditor = function(title, dtable, cm, rec, plugins, store, entries : Ext.encode(out) }, success : function(response, options) { + // Note: this call is mostly redundant (comet update will pick it up anyway) store.commitChanges(); }, failure : function(response, options) { From e3e2a12debcc4bcbc3ba676a0769dbdae2acb581 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 16:34:57 +0000 Subject: [PATCH 303/503] init: add ipv6 option to config file and start scripts. --- debian/tvheadend.default | 4 ++++ debian/tvheadend.init | 1 + debian/tvheadend.upstart | 1 + 3 files changed, 6 insertions(+) diff --git a/debian/tvheadend.default b/debian/tvheadend.default index 4d919538..4617c07b 100644 --- a/debian/tvheadend.default +++ b/debian/tvheadend.default @@ -25,6 +25,10 @@ TVH_CONF_DIR="" # set as "0,1" TVH_ADAPTERS="" +# TVH_IPV6 +# if set to 1 will enable IPv6 support +TVH_IPV6=0 + # TVH_HTTP_PORT # if set to "" will use binary default TVH_HTTP_PORT="" diff --git a/debian/tvheadend.init b/debian/tvheadend.init index 9e3bcb97..1fe83698 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -33,6 +33,7 @@ ARGS="-f" [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" +[ "$TVH_IPV6" == "1" ] && ARGS="$ARGS -6" [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" diff --git a/debian/tvheadend.upstart b/debian/tvheadend.upstart index 5b8b6512..9fe9baf7 100644 --- a/debian/tvheadend.upstart +++ b/debian/tvheadend.upstart @@ -22,6 +22,7 @@ script [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" + [ "$TVH_IPV6" == "1" ] && ARGS="$ARGS -6" [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" From 4dd787640f6a77b095537af59158c191f53b268c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 16:47:12 +0000 Subject: [PATCH 304/503] access: some minor cosmetics ensure that the list/string order is not reversed each time you save it (looks odd). --- src/access.c | 25 ++++++++++++++----------- src/access.h | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/access.c b/src/access.c index 514e8707..a595986d 100644 --- a/src/access.c +++ b/src/access.c @@ -170,7 +170,7 @@ netmask_verify(access_entry_t *ae, struct sockaddr *src) } } - LIST_FOREACH(ai, &ae->ae_ipmasks, ai_link) + TAILQ_FOREACH(ai, &ae->ae_ipmasks, ai_link) { if(ai->ai_ipv6 == 0 && src->sa_family == AF_INET) { @@ -360,9 +360,9 @@ access_set_prefix(access_entry_t *ae, const char *prefix) char *p, *tok, *saveptr; access_ipmask_t *ai; - while((ai = LIST_FIRST(&ae->ae_ipmasks)) != NULL) + while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL) { - LIST_REMOVE(ai, ai_link); + TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link); free(ai); } @@ -435,7 +435,7 @@ access_set_prefix(access_entry_t *ae, const char *prefix) ai->ai_network = ntohl(ai->ai_ip.s_addr) & ai->ai_netmask; } - LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + TAILQ_INSERT_TAIL(&ae->ae_ipmasks, ai, ai_link); tok = strtok_r(NULL, ",;| ", &saveptr); } @@ -475,6 +475,7 @@ access_entry_find(const char *id, int create) ae->ae_username = strdup("*"); ae->ae_password = strdup("*"); ae->ae_comment = strdup("New entry"); + TAILQ_INIT(&ae->ae_ipmasks); TAILQ_INSERT_TAIL(&access_entries, ae, ae_link); return ae; } @@ -489,9 +490,9 @@ access_entry_destroy(access_entry_t *ae) { access_ipmask_t *ai; - while((ai = LIST_FIRST(&ae->ae_ipmasks)) != NULL) + while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL) { - LIST_REMOVE(ai, ai_link); + TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link); free(ai); } @@ -523,7 +524,7 @@ access_record_build(access_entry_t *ae) htsmsg_add_str(e, "password", ae->ae_password); htsmsg_add_str(e, "comment", ae->ae_comment); - LIST_FOREACH(ai, &ae->ae_ipmasks, ai_link) + TAILQ_FOREACH(ai, &ae->ae_ipmasks, ai_link) { if(sizeof(buf)-pos <= 0) break; @@ -715,13 +716,15 @@ access_init(int createdefault) ae->ae_enabled = 1; ae->ae_rights = 0xffffffff; - ai = calloc(1, sizeof(access_ipmask_t)); - ai->ai_ipv6 = 0; - LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + TAILQ_INIT(&ae->ae_ipmasks); ai = calloc(1, sizeof(access_ipmask_t)); ai->ai_ipv6 = 1; - LIST_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + TAILQ_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + + ai = calloc(1, sizeof(access_ipmask_t)); + ai->ai_ipv6 = 0; + TAILQ_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); r = access_record_build(ae); dtable_record_store(dt, ae->ae_id, r); diff --git a/src/access.h b/src/access.h index 466e380f..2ecc9873 100644 --- a/src/access.h +++ b/src/access.h @@ -21,7 +21,7 @@ typedef struct access_ipmask { - LIST_ENTRY(access_ipmask) ai_link; + TAILQ_ENTRY(access_ipmask) ai_link; int ai_ipv6; @@ -50,7 +50,7 @@ typedef struct access_entry { uint32_t ae_rights; - LIST_HEAD(, access_ipmask) ae_ipmasks; + TAILQ_HEAD(, access_ipmask) ae_ipmasks; } access_entry_t; TAILQ_HEAD(access_ticket_queue, access_ticket); From 8d2da9389df581923c462ca04c7ee35a072e675a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 17:28:51 +0000 Subject: [PATCH 305/503] access: fix infinite loop bug in ipv6 check Thanks to BtBN for spotting this (though it was his code!). --- src/access.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/access.c b/src/access.c index a595986d..3e7614d5 100644 --- a/src/access.c +++ b/src/access.c @@ -203,11 +203,13 @@ netmask_verify(access_entry_t *ae, struct sockaddr *src) while(slen >= 8) { if(a8[apos] != m8[apos]) - continue; + break; apos += 1; slen -= 8; } + if(slen >= 8) + continue; if(slen == 0 || (a8[apos] & lastMask) == (m8[apos] & lastMask)) return 1; From d82362ac73f66a2b7715e3b6f79564b620a620c8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 17:29:40 +0000 Subject: [PATCH 306/503] build: remove changelog from git. --- debian/changelog | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 debian/changelog diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 78fa0626..00000000 --- a/debian/changelog +++ /dev/null @@ -1,6 +0,0 @@ -tvheadend (3.3) unstable; urgency=low - - * The full changelog can be found at - http://www.lonelycoder.com/tvheadend/download - - -- Andreas Öman Tue, 18 Sep 2012 12:45:49 +0100 From c1312e160a3103152498fbebdde25714d236673a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 15 Jan 2013 17:35:23 +0000 Subject: [PATCH 307/503] timeshift: fix silly logic error in assert() call. --- src/timeshift/timeshift_filemgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index fc097bc4..9260e81c 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -76,7 +76,7 @@ static void* timeshift_reaper_callback ( void *p ) tvhlog(LOG_ERR, "timeshift", "failed to remove %s [e=%s]", dpath, strerror(errno)); pthread_mutex_lock(×hift_size_lock); - assert(tsf->size >= timeshift_total_size); + assert(tsf->size <= timeshift_total_size); timeshift_total_size -= tsf->size; pthread_mutex_unlock(×hift_size_lock); From d9128b146df027697f9486a962e73c631d1db1b3 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Wed, 16 Jan 2013 02:10:55 +0400 Subject: [PATCH 308/503] fix check of empty description this is need because _eit_get_string_with_len function return length of string with size-byte. --- src/epggrab/module/eit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 103464fe..0ca20563 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -330,7 +330,7 @@ static int _eit_desc_ext_event if ( _eit_get_string_with_len(mod, buf, sizeof(buf), ptr, len, - ev->default_charset) > 0 ) { + ev->default_charset) > 1 ) { if (!ev->desc) ev->desc = lang_str_create(); lang_str_append(ev->desc, buf, lang); } From d5858b0b50e6073a2f520ecaf01338c93fb7aefb Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 15 Jan 2013 23:53:55 +0100 Subject: [PATCH 309/503] Fix assertion in reaper thread --- src/timeshift/timeshift_filemgr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 9260e81c..4b90eb60 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -149,7 +149,12 @@ void timeshift_filemgr_close ( timeshift_file_t *tsf ) { ssize_t r = timeshift_write_eof(tsf->fd); if (r > 0) + { tsf->size += r; + pthread_mutex_lock(×hift_size_lock); + timeshift_total_size += r; + pthread_mutex_unlock(×hift_size_lock); + } close(tsf->fd); tsf->fd = -1; } From 5d18ae613dff53d42d0471a9ed85dfb5bf2d3c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 16 Jan 2013 10:19:02 +0100 Subject: [PATCH 310/503] Avoid reordering teletext streams multiple times... ...thus causing continious service restarts This happens if multiple teletext descriptors point to the same page --- src/psi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/psi.c b/src/psi.c index 7ed7a8dd..374815bf 100644 --- a/src/psi.c +++ b/src/psi.c @@ -347,9 +347,9 @@ psi_desc_teletext(service_t *t, const uint8_t *ptr, int size, if((st = service_stream_find(t, pid)) == NULL) { r |= PMT_UPDATE_NEW_STREAM; st = service_stream_create(t, pid, SCT_TEXTSUB); + st->es_delete_me = 1; } - st->es_delete_me = 0; lang = lang_code_get2((const char*)ptr, 3); if(memcmp(st->es_lang,lang,3)) { @@ -362,10 +362,12 @@ psi_desc_teletext(service_t *t, const uint8_t *ptr, int size, st->es_parent_pid = parent_pid; } - if(st->es_position != *position) { + // Check es_delete_me so we only compute position once per PMT update + if(st->es_position != *position && st->es_delete_me) { st->es_position = *position; r |= PMT_REORDERED; } + st->es_delete_me = 0; (*position)++; } ptr += 5; From a57eb8b193dffbf0738007fd0776a191fc509261 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 16 Jan 2013 09:40:45 +0000 Subject: [PATCH 311/503] epggrab: add some additional checks in the EIT code for empty strings. --- src/epggrab/module/eit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 0ca20563..c9169fb8 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -242,7 +242,7 @@ static int _eit_desc_short_event if ( (r = _eit_get_string_with_len(mod, buf, sizeof(buf), ptr, len, ev->default_charset)) < 0 ) { return -1; - } else { + } else if ( r > 1 ) { if (!ev->title) ev->title = lang_str_create(); lang_str_add(ev->title, buf, lang, 0); } @@ -255,7 +255,7 @@ static int _eit_desc_short_event if ( (r = _eit_get_string_with_len(mod, buf, sizeof(buf), ptr, len, ev->default_charset)) < 0 ) { return -1; - } else { + } else if ( r > 1 ) { if (!ev->summary) ev->summary = lang_str_create(); lang_str_add(ev->summary, buf, lang, 0); } @@ -460,6 +460,7 @@ static int _eit_desc_crid ptr+1, len-1, ev->default_charset); if (r < 0) return -1; + if (r == 0) continue; /* Episode */ if (type == 0x1 || type == 0x31) { From b7ad9f3ad0069ffb03615d6341a365dc420bd45e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 16 Jan 2013 09:47:35 +0000 Subject: [PATCH 312/503] cleanup: remove old files. --- support/dataroot/bundle.c | 4 ---- support/dataroot/datadir.c | 10 ---------- support/dataroot/wd.c | 16 ---------------- 3 files changed, 30 deletions(-) delete mode 100644 support/dataroot/bundle.c delete mode 100644 support/dataroot/datadir.c delete mode 100644 support/dataroot/wd.c diff --git a/support/dataroot/bundle.c b/support/dataroot/bundle.c deleted file mode 100644 index 72abeff0..00000000 --- a/support/dataroot/bundle.c +++ /dev/null @@ -1,4 +0,0 @@ -const char *tvheadend_dataroot(void) -{ - return (void *)0; -} diff --git a/support/dataroot/datadir.c b/support/dataroot/datadir.c deleted file mode 100644 index ffb519b4..00000000 --- a/support/dataroot/datadir.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "config.h" -#include "filebundle.h" - -filebundle_entry_t *filebundle_root = NULL; - -const char *tvheadend_dataroot(void) -{ - return TVHEADEND_DATADIR; -} - diff --git a/support/dataroot/wd.c b/support/dataroot/wd.c deleted file mode 100644 index a8470e90..00000000 --- a/support/dataroot/wd.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include - -#include "filebundle.h" - -filebundle_entry_t *filebundle_root = NULL; - -const char *tvheadend_dataroot(void) -{ - static char cwd[256] = { 0 }; - if (!*cwd) { - assert(getcwd(cwd, 254)); - } - return cwd; -} From 14fcdcf2fc9ab884b3180a6e0175edae34c43548 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 16 Jan 2013 09:47:52 +0000 Subject: [PATCH 313/503] support: minor aesthetic change. --- support/configure.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/configure.inc b/support/configure.inc index 42975e6f..6b2dcece 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -42,7 +42,7 @@ # ########################################################################### # Output -TAB=" %-50s" +TAB=" %-50s" # Text conversion function toupper From 9deeeebfc2eba03c3df756c26be72a8c6f53477d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 18 Jan 2013 11:08:15 +0000 Subject: [PATCH 314/503] dvb: fix disabling of bad muxes caused by dodgy hardware/firmware because some tuners, mostly USB, can result in ioctl() failures due to inability to communicate with driver and this was not properly trapped muxes were being disabled. Better solution is to reject bad muxes being entered in the first place based on the tuning capability of the device. Ofc if the tuner fails to report that correctly (probably) we'll still be buggered. --- src/dvb/dvb_fe.c | 1 - src/dvb/dvb_multiplex.c | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 7120ab5c..b1f9a314 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -510,7 +510,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) } /* Mark as bad */ - dvb_mux_set_enable(tdmi, 0); return SM_CODE_TUNING_FAILED; } diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 37d3ca17..74c07341 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -162,6 +162,14 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, lock_assert(&global_lock); + /* Reject out of range */ + if ((dmc->dmc_fe_params.frequency < tda->tda_fe_info->frequency_min) || + (dmc->dmc_fe_params.frequency > tda->tda_fe_info->frequency_max)) { + tvhlog(LOG_DEBUG, "dvb", "mux rejected with frequency %d", + dmc->dmc_fe_params.frequency); + return NULL; + } + /* HACK - we hash/compare based on 2KHz spacing and compare on +/-500Hz */ LIST_FOREACH(tdmi, &tda->tda_mux_list, tdmi_adapter_hash_link) { if(tdmi_compare_key(&tdmi->tdmi_conf, dmc)) @@ -1171,7 +1179,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL); if(tdmi == NULL) - return "Mux already exist"; + return "Mux already exists or bad parameters"; return NULL; } From 73652e9377df5aa0e58d22dd4064b572d2d9f17d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 18 Jan 2013 14:20:10 +0000 Subject: [PATCH 315/503] Fix #1545 - ensure files only created as required in on-demand mode. --- src/timeshift/timeshift_reader.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index ad948d96..1750a137 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -207,7 +207,7 @@ static timeshift_index_iframe_t *_timeshift_last_frame { int end; timeshift_index_iframe_t *tsi = NULL; - timeshift_file_t *tsf = timeshift_filemgr_get(ts, ts->ondemand); + timeshift_file_t *tsf = timeshift_filemgr_get(ts, 0); while (tsf && !tsi) { if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) tsf = timeshift_filemgr_prev(tsf, &end, 0); @@ -280,7 +280,7 @@ static int _timeshift_skip } end = -1; } else { - tsf = timeshift_filemgr_get(ts, ts->ondemand); + tsf = timeshift_filemgr_get(ts, 0); tsi = NULL; while (tsf && !tsi) { if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) @@ -481,7 +481,7 @@ void *timeshift_reader ( void *p ) ts->id); timeshift_writer_flush(ts); pthread_mutex_lock(&ts->rdwr_mutex); - if ((cur_file = timeshift_filemgr_get(ts, ts->ondemand))) { + if ((cur_file = timeshift_filemgr_get(ts, 1))) { cur_off = cur_file->size; pause_time = cur_file->last; last_time = pause_time; @@ -542,7 +542,7 @@ void *timeshift_reader ( void *p ) /* Live playback (stage1) */ if (ts->state == TS_LIVE) { pthread_mutex_lock(&ts->rdwr_mutex); - if ((cur_file = timeshift_filemgr_get(ts, ts->ondemand))) { + if ((cur_file = timeshift_filemgr_get(ts, !ts->ondemand))) { cur_off = cur_file->size; last_time = cur_file->last; } else { @@ -552,16 +552,19 @@ void *timeshift_reader ( void *p ) pthread_mutex_unlock(&ts->rdwr_mutex); } - tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64, ts->id, last_time); - skip_time += (skip->type == SMT_SKIP_ABS_TIME) ? ts->pts_delta : last_time; + /* May have failed */ + if (skip) { + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64, ts->id, last_time); + skip_time += (skip->type == SMT_SKIP_ABS_TIME) ? ts->pts_delta : last_time; - /* Live (stage2) */ - if (ts->state == TS_LIVE) { - if (skip_time >= now) { - tvhlog(LOG_DEBUG, "timeshift", "ts %d skip ignored, already live", ts->id); - skip = NULL; - } else { - ts->state = TS_PLAY; + /* Live (stage2) */ + if (ts->state == TS_LIVE) { + if (skip_time >= now) { + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip ignored, already live", ts->id); + skip = NULL; + } else { + ts->state = TS_PLAY; + } } } From 7f89b3d552748f242f150505d44c104926f27285 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 18 Jan 2013 15:55:29 +0000 Subject: [PATCH 316/503] dvb: undo mod to frequency range testing and add additional tuning check The previous check was garbage because for -S adapters the frequency range reported by the adapter is in local units (i.e. hi/low band taken into account) and not simple kHz. This makes checking more difficult and while its possible I think this should be left for dvb rewrite. Instead I've re-instated the old code, but muxes are only disable if the return is EINVAL, which should only be returned if the DVB layer decides the params are out of range. --- src/dvb/dvb_fe.c | 2 ++ src/dvb/dvb_multiplex.c | 10 +--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index b1f9a314..a4e58089 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -510,6 +510,8 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) } /* Mark as bad */ + if (errno == EINVAL) + dvb_mux_set_enable(tdmi, 0); return SM_CODE_TUNING_FAILED; } diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 74c07341..37d3ca17 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -162,14 +162,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, lock_assert(&global_lock); - /* Reject out of range */ - if ((dmc->dmc_fe_params.frequency < tda->tda_fe_info->frequency_min) || - (dmc->dmc_fe_params.frequency > tda->tda_fe_info->frequency_max)) { - tvhlog(LOG_DEBUG, "dvb", "mux rejected with frequency %d", - dmc->dmc_fe_params.frequency); - return NULL; - } - /* HACK - we hash/compare based on 2KHz spacing and compare on +/-500Hz */ LIST_FOREACH(tdmi, &tda->tda_mux_list, tdmi_adapter_hash_link) { if(tdmi_compare_key(&tdmi->tdmi_conf, dmc)) @@ -1179,7 +1171,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL); if(tdmi == NULL) - return "Mux already exists or bad parameters"; + return "Mux already exist"; return NULL; } From 2dcbc3632736a2323ecdeb6f1853e0b134836f3c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 18 Jan 2013 17:22:44 +0000 Subject: [PATCH 317/503] Fix #1547 - timeshift: do not create directories until needed This tries to minimise what is written to disk (inc dirs) when using on-demand mode. This could result in the failure to write the buffer not being detected till later and not entirely sure how clients might handle that. --- src/timeshift.c | 10 +++------- src/timeshift/timeshift_filemgr.c | 9 +++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/timeshift.c b/src/timeshift.c index d0cdd856..2ecc39b4 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -215,7 +215,8 @@ timeshift_destroy(streaming_target_t *pad) if (ts->smt_start) streaming_start_unref(ts->smt_start); - free(ts->path); + if (ts->path) + free(ts->path); free(ts); } @@ -228,20 +229,15 @@ timeshift_destroy(streaming_target_t *pad) streaming_target_t *timeshift_create (streaming_target_t *out, time_t max_time) { - char buf[512]; timeshift_t *ts = calloc(1, sizeof(timeshift_t)); /* Must hold global lock */ lock_assert(&global_lock); - /* Create directories */ - if (timeshift_filemgr_makedirs(timeshift_index, buf, sizeof(buf))) - return NULL; - /* Setup structure */ TAILQ_INIT(&ts->files); ts->output = out; - ts->path = strdup(buf); + ts->path = NULL; ts->max_time = max_time; ts->state = TS_INIT; ts->full = 0; diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 4b90eb60..635f4769 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -240,6 +240,15 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Create new file */ tsf_tmp = NULL; if (!ts->full) { + + /* Create directories */ + if (!ts->path) { + if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) + return NULL; + ts->path = strdup(path); + } + + /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d create file %s", ts->id, path); From 07c75503cced7fec4544ac185e8fd70ad6017111 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Sat, 19 Jan 2013 19:01:52 +0000 Subject: [PATCH 318/503] Protect dvr_inotify_init() in the same way as the other dvr_inotify_* functions --- src/dvr/dvr_db.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 6ab51aca..b7c66d68 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -1053,7 +1053,9 @@ dvr_init(void) } } +#if ENABLE_INOTIFY dvr_inotify_init(); +#endif dvr_autorec_init(); dvr_db_load(); dvr_autorec_update(); From 87822f001f31cb5698e5092dd978cfb0f3bad129 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 00:42:17 +0000 Subject: [PATCH 319/503] Fix #1549 - webui: stop tabs being re-added. --- src/webui/static/app/tvheadend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 96bb66a3..cfaab734 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -242,6 +242,8 @@ tvheadend.VLC = function(url) { */ function accessUpdate(o) { tvheadend.accessUpdate = o; + if (!tvheadend.capabilities) + return; if (o.dvr == true && tvheadend.dvrpanel == null) { tvheadend.dvrpanel = new tvheadend.dvr; @@ -260,8 +262,6 @@ function accessUpdate(o) { new tvheadend.iptv, new tvheadend.acleditor ] }); tvheadend.rootTabPanel.add(tvheadend.confpanel); - } - if (tvheadend.capabilities && tvheadend.confpanel) { if (tvheadend.capabilities.indexOf('linuxdvb') != -1 || tvheadend.capabilities.indexOf('v4l') != -1) { tvheadend.confpanel.add(new tvheadend.tvadapters); From 97c739b5726e10c26fa3fe22309001cf8f49ea47 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 01:30:24 +0000 Subject: [PATCH 320/503] Fix #1517 - escape regexp special chars before adding autorec rule. This stops autorec rules entered from event failing to detect events. --- src/dvr/dvr_autorec.c | 6 +++++- src/tvheadend.h | 2 ++ src/utils.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 0b6a5c91..9da3e3d7 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -559,9 +559,11 @@ void dvr_autorec_add_series_link ( const char *dvr_config_name, epg_broadcast_t *event, const char *creator, const char *comment ) { + char *title; if (!event || !event->episode) return; + title = regexp_escape(epg_broadcast_get_title(event, NULL)); _dvr_autorec_add(dvr_config_name, - epg_broadcast_get_title(event, NULL), + title, event->channel, NULL, 0, // tag/content type NULL, @@ -569,6 +571,8 @@ void dvr_autorec_add_series_link event->serieslink, 0, NULL, creator, comment); + if (title) + free(title); } diff --git a/src/tvheadend.h b/src/tvheadend.h index 7b9e95be..6b0f3ab5 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -561,6 +561,8 @@ int makedirs ( const char *path, int mode ); int rmtree ( const char *path ); +char *regexp_escape ( const char *str ); + /* printing */ #if __SIZEOF_LONG__ == 8 #define PRItime_t PRId64 diff --git a/src/utils.c b/src/utils.c index 47b2cff2..a30ea823 100644 --- a/src/utils.c +++ b/src/utils.c @@ -403,3 +403,36 @@ rmtree ( const char *path ) err = rmdir(path); return err; } + +char * +regexp_escape(const char* str) +{ + const char *a; + char *tmp, *b; + if (!str) + return NULL; + a = str; + b = tmp = malloc(strlen(str) * 2); + while (*a) { + switch (*a) { + case '?': + case '+': + case '.': + case '(': + case ')': + case '[': + case ']': + case '*': + *b = '\\'; + b++; + /* -fallthrough */ + default: + break; + } + *b = *a; + b++; + a++; + } + *b = 0; + return tmp; +} From f021ec9592ea33b2def1f317c939d7de30cb9440 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 19:48:47 +0000 Subject: [PATCH 321/503] dvr: stop inotify code asserting, output error instead. --- src/dvr/dvr_inotify.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr_inotify.c b/src/dvr/dvr_inotify.c index 22c6d22b..7d6f177c 100644 --- a/src/dvr/dvr_inotify.c +++ b/src/dvr/dvr_inotify.c @@ -60,6 +60,11 @@ void dvr_inotify_init ( void ) pthread_t tid; _inot_fd = inotify_init(); + if (_inot_fd == -1) { + tvhlog(LOG_ERR, "dvr", "failed to initialise inotify (err=%s)", + strerror(errno)); + return; + } pthread_create(&tid, NULL, _dvr_inotify_thread, NULL); } @@ -74,6 +79,9 @@ void dvr_inotify_add ( dvr_entry_t *de ) char *path; struct stat st; + if (_inot_fd == -1) + return; + if (!de->de_filename || stat(de->de_filename, &st)) return; @@ -92,7 +100,13 @@ void dvr_inotify_add ( dvr_entry_t *de ) skel = NULL; e->path = strdup(e->path); e->fd = inotify_add_watch(_inot_fd, e->path, EVENT_MASK); - assert(e->fd != -1); + if (e->fd == -1) { + tvhlog(LOG_ERR, "dvr", "failed to add inotify watch to %s (err=%s)", + e->path, strerror(errno)); + free(path); + dvr_inotify_del(de); + return; + } } LIST_INSERT_HEAD(&e->entries, de, de_inotify_link); From bda8422d3553a5132da1da115590a46785020add Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 20:02:42 +0000 Subject: [PATCH 322/503] Fix #1550 - dvr: ensure genre is not lost on autorec update --- src/dvr/dvr_autorec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 9da3e3d7..09cd228f 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -390,7 +390,8 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values, } } - dae->dae_content_type.code = htsmsg_get_u32_or_default(values, "contenttype", 0); + if (!htsmsg_get_u32(values, "contenttype", &u32)) + dae->dae_content_type.code = u32; if((s = htsmsg_get_str(values, "approx_time")) != NULL) { if(strchr(s, ':') != NULL) { From 39a2272b56fd6faeabf766b31e6c1c2b3d3b5f91 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 23:24:16 +0000 Subject: [PATCH 323/503] Fix #1524 - webui: ignore mux updates from different adapter. These were causing the store to be unecessarily reloaded and as a result the enabled flag would get cleared in the middle of editing. --- src/dvb/dvb_multiplex.c | 2 ++ src/webui/static/app/dvb.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 37d3ca17..9341988d 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1010,6 +1010,8 @@ dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi) htsmsg_t *m = htsmsg_create_map(); char buf[100]; + htsmsg_add_str(m, "adapterId", tdmi->tdmi_adapter->tda_identifier); + htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled); htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 4ef9063e..5710640a 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -120,6 +120,9 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { tvheadend.comet.on('dvbMux', function(m) { + if(m.adapterId !== adapterId) + return; + r = store.getById(m.id) if (typeof r === 'undefined') { store.reload(); From 762b153bc17f1bf7001bf39b4b8ce62ba84b8e8c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 18 Jan 2013 16:59:51 +0000 Subject: [PATCH 324/503] Fix #339 - added configurable grace period per adapter. --- src/dvb/dvb.h | 3 +++ src/dvb/dvb_adapter.c | 19 +++++++++++++++++++ src/dvb/dvb_service.c | 2 ++ src/webui/extjs_dvb.c | 4 ++++ src/webui/static/app/dvb.js | 6 +++++- 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 5ff64f63..14e8ef68 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -217,6 +217,7 @@ typedef struct th_dvb_adapter { uint32_t tda_diseqc_repeats; uint32_t tda_disable_pmt_monitor; int32_t tda_full_mux_rx; + uint32_t tda_grace_period; char *tda_displayname; char *tda_fe_path; @@ -374,6 +375,8 @@ void dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on); void dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int r); +void dvb_adapter_set_grace_period(th_dvb_adapter_t *tda, uint32_t p); + void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src); void dvb_adapter_clean(th_dvb_adapter_t *tda); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 6a1b1cc5..86e42c31 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -106,6 +106,7 @@ tda_save(th_dvb_adapter_t *tda) htsmsg_add_u32(m, "skip_initialscan", tda->tda_skip_initialscan); htsmsg_add_u32(m, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); htsmsg_add_s32(m, "full_mux_rx", tda->tda_full_mux_rx); + htsmsg_add_u32(m, "grace_period", tda->tda_grace_period); hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier); htsmsg_destroy(m); } @@ -424,6 +425,22 @@ dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int on) tda_save(tda); } +/** + * + */ +void +dvb_adapter_set_grace_period(th_dvb_adapter_t *tda, uint32_t p) +{ + if (tda->tda_grace_period == p) + return; + + tvhlog(LOG_NOTICE, "dvb", + "Adapter \"%s\" set grace period to %d", tda->tda_displayname, p); + + tda->tda_grace_period = p; + tda_save(tda); +} + /** * @@ -776,6 +793,8 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority); htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan); htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor); + if (htsmsg_get_u32(c, "grace_period", &tda->tda_grace_period)) + tda->tda_grace_period = 10; if (htsmsg_get_s32(c, "full_mux_rx", &tda->tda_full_mux_rx)) if (!htsmsg_get_u32(c, "disable_full_mux_rx", &u32) && u32) tda->tda_full_mux_rx = 0; diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 5ca5a29e..c927ec3e 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -339,6 +339,8 @@ dvb_service_setsourceinfo(service_t *t, struct source_info *si) static int dvb_grace_period(service_t *t) { + if (t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_adapter) + return t->s_dvb_mux_instance->tdmi_adapter->tda_grace_period ?: 10; return 10; } diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 0919e5ac..4aefa6b7 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -159,6 +159,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "nitoid", tda->tda_nitoid); htsmsg_add_u32(r, "disable_pmt_monitor", tda->tda_disable_pmt_monitor); htsmsg_add_u32(r, "full_mux_rx", tda->tda_full_mux_rx+1); + htsmsg_add_u32(r, "grace_period", tda->tda_grace_period); htsmsg_add_str(r, "diseqcversion", ((const char *[]){"DiSEqC 1.0 / 2.0", "DiSEqC 1.1 / 2.1"}) @@ -207,6 +208,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) s = http_arg_get(&hc->hc_req_args, "full_mux_rx"); dvb_adapter_set_full_mux_rx(tda, atoi(s)-1); + s = http_arg_get(&hc->hc_req_args, "grace_period"); + dvb_adapter_set_grace_period(tda, atoi(s)); + if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL) dvb_adapter_set_nitoid(tda, atoi(s)); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 5710640a..eeca7090 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1165,7 +1165,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { }, [ 'name', 'enabled', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion', 'diseqcrepeats', 'qmon', 'skip_checksubscr', 'poweroff', 'sidtochan', 'nitoid', 'extrapriority', - ,'disable_pmt_monitor', 'full_mux_rx', 'idleclose' ]); + ,'disable_pmt_monitor', 'full_mux_rx', 'idleclose', 'grace_period' ]); function saveConfForm() { confform.getForm().submit({ @@ -1228,6 +1228,10 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { fields: [ 'num', 'str' ], store : [ [0, 'Auto'], [1, 'Off'], [2, 'On'] ] }), + new Ext.form.NumberField({ + fieldLabel: 'Grace Period', + name: 'grace_period' + }), new Ext.form.Checkbox({ fieldLabel : 'Disable PMT monitoring', name : 'disable_pmt_monitor' From 27e0eb09a2b91d7813a8cbde56c90be630c69e8a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 19 Jan 2013 00:57:22 +0000 Subject: [PATCH 325/503] service: added s_is_enabled() function for checking enable chain --- src/dvb/dvb_service.c | 12 ++++++++++++ src/iptv_input.c | 9 +++++++++ src/service.h | 1 + src/v4l.c | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index c927ec3e..a91e92d5 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -135,6 +135,17 @@ dvb_service_refresh(service_t *t) tda->tda_open_service(tda, t); } +/** + * + */ +static int +dvb_service_is_enabled(service_t *t) +{ + th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + return tda->tda_enabled && tdmi->tdmi_enabled && t->s_enabled; +} + /** * @@ -432,6 +443,7 @@ dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, t->s_setsourceinfo = dvb_service_setsourceinfo; t->s_quality_index = dvb_service_quality; t->s_grace_period = dvb_grace_period; + t->s_is_enabled = dvb_service_is_enabled; t->s_dvb_mux_instance = tdmi; LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, s_group_link); diff --git a/src/iptv_input.c b/src/iptv_input.c index 314e5b59..6c39a866 100644 --- a/src/iptv_input.c +++ b/src/iptv_input.c @@ -461,6 +461,14 @@ iptv_service_quality(service_t *t) return 100; } +/** + * + */ +static int +iptv_service_is_enabled(service_t *t) +{ + return t->s_enabled; +} /** * Generate a descriptive name for the source @@ -543,6 +551,7 @@ iptv_service_find(const char *id, int create) t->s_config_save = iptv_service_save; t->s_setsourceinfo = iptv_service_setsourceinfo; t->s_quality_index = iptv_service_quality; + t->s_is_enabled = iptv_service_is_enabled; t->s_grace_period = iptv_grace_period; t->s_dtor = iptv_service_dtor; t->s_iptv_fd = -1; diff --git a/src/service.h b/src/service.h index 700c150e..05321cde 100644 --- a/src/service.h +++ b/src/service.h @@ -249,6 +249,7 @@ typedef struct service { * subscription scheduling. */ int s_enabled; + int (*s_is_enabled)(struct service *t); /** * Last PCR seen, we use it for a simple clock for rawtsinput.c diff --git a/src/v4l.c b/src/v4l.c index a640a387..1a48fed8 100644 --- a/src/v4l.c +++ b/src/v4l.c @@ -304,6 +304,15 @@ v4l_service_quality(service_t *t) return 100; } +/** + * + */ +static int +v4l_service_is_enabled(service_t *t) +{ + return t->s_enabled; +} + /** * @@ -372,6 +381,7 @@ v4l_service_find(v4l_adapter_t *va, const char *id, int create) t->s_config_save = v4l_service_save; t->s_setsourceinfo = v4l_service_setsourceinfo; t->s_quality_index = v4l_service_quality; + t->s_is_enabled = v4l_service_is_enabled; t->s_grace_period = v4l_grace_period; t->s_iptv_fd = -1; t->s_v4l_adapter = va; From 1d6356a87b95bc2dbc8ca4645670a045fbbd1149 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 20 Jan 2013 11:41:21 +0000 Subject: [PATCH 326/503] Issue #1532 - service: ensure that only fully enabled services are used. This uses the new s_is_enabled() callback routine to check if the service chain is completely enabled. For DVB services this will also check the container mux and adapter. --- src/service.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/service.c b/src/service.c index 67353021..a22eec77 100644 --- a/src/service.c +++ b/src/service.c @@ -335,7 +335,7 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo, cnt = 0; LIST_FOREACH(t, &ch->ch_services, s_ch_link) { - if(!t->s_enabled) { + if(!t->s_is_enabled(t)) { if(loginfo != NULL) { tvhlog(LOG_NOTICE, "Service", "%s: Skipping \"%s\" -- not enabled", loginfo, service_nicename(t)); @@ -1179,11 +1179,7 @@ service_is_primary_epg(service_t *svc) service_t *ret = NULL, *t; if (!svc || !svc->s_ch) return 0; LIST_FOREACH(t, &svc->s_ch->ch_services, s_ch_link) { - if (!t->s_dvb_mux_instance) continue; - if (!t->s_dvb_mux_instance->tdmi_enabled) continue; - if (!t->s_dvb_mux_instance->tdmi_adapter->tda_enabled) continue; - if (!t->s_dvb_mux_instance->tdmi_adapter->tda_rootpath) continue; - if (!t->s_enabled || !t->s_dvb_eit_enable) continue; + if (!t->s_is_enabled(t) || !t->s_dvb_eit_enable) continue; if (!ret || service_get_prio(t) < service_get_prio(ret)) ret = t; } From a9584156a3567b85dc5a4b4dea36fd45be88fc8e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 20 Jan 2013 12:41:58 +0000 Subject: [PATCH 327/503] Fix #1542 - timeshift: stop crash if speed/skip request w/o service This can happen because XBMC may send requests even though the sub has not yet started and the relevant links been made. --- src/subscriptions.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/subscriptions.c b/src/subscriptions.c index 8dff4acf..548fa7e9 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -643,6 +643,8 @@ subscription_set_speed ( th_subscription_t *s, int speed ) streaming_message_t *sm; service_t *t = s->ths_service; + if (!t) return; + pthread_mutex_lock(&t->s_stream_mutex); sm = streaming_msg_create_code(SMT_SPEED, speed); @@ -661,6 +663,8 @@ subscription_set_skip ( th_subscription_t *s, const streaming_skip_t *skip ) streaming_message_t *sm; service_t *t = s->ths_service; + if (!t) return; + pthread_mutex_lock(&t->s_stream_mutex); sm = streaming_msg_create(SMT_SKIP); From a0a6632299d5a329758d518b9981d0915a79e642 Mon Sep 17 00:00:00 2001 From: Mariusz Bialonczyk Date: Sun, 20 Jan 2013 18:00:26 +0100 Subject: [PATCH 328/503] Fix #1467 - capmt: try to reconnect when oscam was restarted --- src/capmt.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index 7f88377d..56229604 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -264,6 +264,14 @@ capmt_send_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len) tvhlog(LOG_DEBUG, "capmt", "%s: added: i=%d", __FUNCTION__, i); } + // check if the socket is still alive by writing 0 bytes + if (capmt->capmt_sock[i] > 0) { + if (write(capmt->capmt_sock[i], NULL, 0) < 0) + capmt->capmt_sock[i] = 0; + else if (found) + return 0; + } + // opening socket and sending if (capmt->capmt_sock[i] == 0) { capmt->capmt_sock[i] = tvh_socket(AF_LOCAL, SOCK_STREAM, 0); @@ -663,7 +671,6 @@ capmt_table_input(struct th_descrambler *td, struct service *t, capmt_t *capmt = ct->ct_capmt; int adapter_num = t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num; int total_caids = 0, current_caid = 0; - int i; caid_t *c; @@ -844,17 +851,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, buf[9] = pmtversion; pmtversion = (pmtversion + 1) & 0x1F; - int found = 0; - if (capmt->capmt_oscam) { - for (i = 0; i < MAX_SOCKETS; i++) { - if (capmt->sids[i] == sid) { - found = 1; - break; - } - } - } - if ((capmt->capmt_oscam && !found) || !capmt->capmt_oscam) - capmt_send_msg(capmt, sid, buf, pos); + capmt_send_msg(capmt, sid, buf, pos); break; } default: From efd38b81b3d5339e31171db2ca6f5d3664d952fd Mon Sep 17 00:00:00 2001 From: Georgi Chorbadzhiyski Date: Tue, 22 Jan 2013 16:50:44 +0200 Subject: [PATCH 329/503] cwc: Fix Bulcrypt filters. They are still not 100% optimal but now at least they don't send lots of unneeded EMMs. --- src/cwc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index 79c10008..e5c2046c 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1880,19 +1880,15 @@ cwc_emm_bulcrypt(cwc_t *cwc, uint8_t *data, int len) int match = 0; switch (data[0]) { - case 0x82: /* unique */ - case 0x85: /* unique */ + case 0x82: /* unique - bulcrypt (1 card) */ + case 0x8a: /* unique - polaris (1 card) */ + case 0x85: /* unique - bulcrypt (4 cards) */ + case 0x8b: /* unique - polaris (4 cards) */ match = len >= 10 && memcmp(data + 3, cwc->cwc_ua + 2, 3) == 0; break; - case 0x84: /* shared */ + case 0x84: /* shared - (1024 cards) */ match = len >= 10 && memcmp(data + 3, cwc->cwc_ua + 2, 2) == 0; break; - case 0x8b: /* shared-unknown */ - match = len >= 10 && memcmp(data + 4, cwc->cwc_ua + 2, 2) == 0; - break; - case 0x8a: /* global */ - match = len >= 10 && memcmp(data + 4, cwc->cwc_ua + 2, 1) == 0; - break; } if (match) From 94704a7dfa5893dfc7051c217a445b0779dda417 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 6 Dec 2012 20:39:41 +0100 Subject: [PATCH 330/503] Save channels upon add from web-ui --- src/channels.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/channels.c b/src/channels.c index 970da8e6..f33fdac6 100644 --- a/src/channels.c +++ b/src/channels.c @@ -200,7 +200,9 @@ channel_create2(const char *name, int number) channel_t * channel_create ( void ) { - return channel_create2(NULL, 0); + channel_t *ch = channel_create2(NULL, 0); + channel_save(ch); + return ch; } /** From ed207e69a742f587aaf375a8e0cc4d18930407b8 Mon Sep 17 00:00:00 2001 From: Vuolter Date: Thu, 24 Jan 2013 21:58:54 +0100 Subject: [PATCH 331/503] stv090x snr support --- src/dvb/dvb_adapter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 86e42c31..a1ba8ae8 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -591,7 +591,8 @@ tda_add(int adapter_num) dvb_adapter_checkspeed(tda); /* Adapters known to provide valid SNR */ - if(strcasestr(fe_info.name, "Sony CXD2820R")) + if(strcasestr(fe_info.name, "Sony CXD2820R") || + strcasestr(fe_info.name, "stv090x")) tda->tda_snr_valid = 1; /* Store */ From f6ee30c3c6a88953d5a17c330f6b257079801369 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Fri, 25 Jan 2013 09:00:28 +0100 Subject: [PATCH 332/503] Keep recordings when channel is deleted When a channel is deleted, don't delete all existing recordings. --- src/channels.c | 1 - src/dvr/dvr.h | 4 ++++ src/dvr/dvr_db.c | 23 +++++++++++++---------- src/dvr/dvr_rec.c | 4 ++-- src/htsp_server.c | 3 ++- src/webui/extjs.c | 2 +- src/webui/simpleui.c | 2 +- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/channels.c b/src/channels.c index 970da8e6..bf085b0a 100644 --- a/src/channels.c +++ b/src/channels.c @@ -114,7 +114,6 @@ chidcmp(const channel_t *a, const channel_t *b) return a->ch_id - b->ch_id; } - /** * */ diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 0e56c468..03d1d9ce 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -114,6 +114,8 @@ typedef struct dvr_entry { channel_t *de_channel; LIST_ENTRY(dvr_entry) de_channel_link; + char *de_channel_name; + gtimer_t de_timer; /** @@ -198,6 +200,8 @@ typedef struct dvr_entry { } dvr_entry_t; +#define DVR_CH_NAME(e) ((e)->de_channel_name == NULL ? (e)-> de_channel->ch_name : (e)->de_channel_name) + /** * Autorec entry diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index b7c66d68..ff792556 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -174,7 +174,7 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); if(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE) - snprintf(output, outlen, "%s-", de->de_channel->ch_name); + snprintf(output, outlen, "%s-", DVR_CH_NAME(de)); else output[0] = 0; @@ -335,7 +335,7 @@ static dvr_entry_t *_dvr_entry_create ( tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, " "scheduled for recording by \"%s\"", - lang_str_get(de->de_title, NULL), de->de_channel->ch_name, tbuf, creator); + lang_str_get(de->de_title, NULL), DVR_CH_NAME(de), tbuf, creator); dvrdb_changed(); dvr_entry_save(de); @@ -458,6 +458,7 @@ dvr_entry_remove(dvr_entry_t *de) LIST_REMOVE(de, de_channel_link); LIST_REMOVE(de, de_global_link); de->de_channel = NULL; + free(de->de_channel_name); dvrdb_changed(); @@ -486,7 +487,7 @@ dvr_db_load_one(htsmsg_t *c, int id) if((s = htsmsg_get_str(c, "channel")) == NULL) return; - if((ch = channel_find_by_name(s, 0, 0)) == NULL) + if((ch = channel_find_by_name(s, 0, 1)) == NULL) return; s = htsmsg_get_str(c, "config_name"); @@ -596,7 +597,7 @@ dvr_entry_save(dvr_entry_t *de) lock_assert(&global_lock); - htsmsg_add_str(m, "channel", de->de_channel->ch_name); + htsmsg_add_str(m, "channel", DVR_CH_NAME(de)); htsmsg_add_u32(m, "start", de->de_start); htsmsg_add_u32(m, "stop", de->de_stop); @@ -715,7 +716,7 @@ static dvr_entry_t *_dvr_entry_update htsp_dvr_entry_update(de); dvr_entry_notify(de); tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": Updated Timer", - lang_str_get(de->de_title, NULL), de->de_channel->ch_name); + lang_str_get(de->de_title, NULL), DVR_CH_NAME(de)); } return de; @@ -781,7 +782,7 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode) tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": " "End of program: %s", - lang_str_get(de->de_title, NULL), de->de_channel->ch_name, + lang_str_get(de->de_title, NULL), DVR_CH_NAME(de), dvr_entry_status(de)); dvr_entry_save(de); @@ -816,7 +817,7 @@ dvr_timer_start_recording(void *aux) de->de_rec_state = DVR_RS_PENDING; tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting", - lang_str_get(de->de_title, NULL), de->de_channel->ch_name); + lang_str_get(de->de_title, NULL), DVR_CH_NAME(de)); dvr_entry_notify(de); htsp_dvr_entry_update(de); @@ -929,8 +930,6 @@ dvr_entry_purge(dvr_entry_t *de) { if(de->de_sched_state == DVR_RECORDING) dvr_stop_recording(de, SM_CODE_SOURCE_DELETED); - - dvr_entry_remove(de); } /** @@ -941,8 +940,12 @@ dvr_destroy_by_channel(channel_t *ch) { dvr_entry_t *de; - while((de = LIST_FIRST(&ch->ch_dvrs)) != NULL) + while((de = LIST_FIRST(&ch->ch_dvrs)) != NULL) { + LIST_REMOVE(de, de_channel_link); + de->de_channel = NULL; + de->de_channel_name = strdup(ch->ch_name); dvr_entry_purge(de); + } } /** diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 39b929e7..d1db2734 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -176,7 +176,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) { - char *chname = strdup(de->de_channel->ch_name); + char *chname = strdup(DVR_CH_NAME(de)); cleanupfilename(chname,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", chname); @@ -588,7 +588,7 @@ dvr_spawn_postproc(dvr_entry_t *de, const char *dvr_postproc) memset(fmap, 0, sizeof(fmap)); fmap['f'] = de->de_filename; /* full path to recoding */ fmap['b'] = basename(fbasename); /* basename of recoding */ - fmap['c'] = de->de_channel->ch_name; /* channel name */ + fmap['c'] = DVR_CH_NAME(de); /* channel name */ fmap['C'] = de->de_creator; /* user who created this recording */ fmap['t'] = lang_str_get(de->de_title, NULL); /* program title */ fmap['d'] = lang_str_get(de->de_desc, NULL); /* program description */ diff --git a/src/htsp_server.c b/src/htsp_server.c index a487a1d1..ad7bb1a3 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -557,7 +557,8 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method) dvr_config_t *cfg; htsmsg_add_u32(out, "id", de->de_id); - htsmsg_add_u32(out, "channel", de->de_channel->ch_id); + if (de->de_channel) + htsmsg_add_u32(out, "channel", de->de_channel->ch_id); htsmsg_add_s64(out, "start", de->de_start); htsmsg_add_s64(out, "stop", de->de_stop); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index f6b33c7c..8f1be797 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1389,8 +1389,8 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, m = htsmsg_create_map(); + htsmsg_add_str(m, "channel", DVR_CH_NAME(de)); if(de->de_channel != NULL) { - htsmsg_add_str(m, "channel", de->de_channel->ch_name); if (de->de_channel->ch_icon) htsmsg_add_imageurl(m, "chicon", "imagecache/%d", de->de_channel->ch_icon); diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index faabaf8d..1601ab18 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -322,7 +322,7 @@ page_pvrinfo(http_connection_t *hc, const char *remain, void *opaque) a.tm_hour, a.tm_min, b.tm_hour, b.tm_min); htsbuf_qprintf(hq, "
\"%s\": \"%s\"

", - de->de_channel->ch_name, lang_str_get(de->de_title, NULL)); + DVR_CH_NAME(de), lang_str_get(de->de_title, NULL)); if((rstatus = val2str(de->de_sched_state, recstatustxt)) != NULL) htsbuf_qprintf(hq, "Recording status: %s
", rstatus); From 8754b14d50b5b2369f79ce1dfd7c12397e7f042f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Fri, 25 Jan 2013 21:21:52 +0100 Subject: [PATCH 333/503] put tsfix before the global headers. fixes problems where recordings could loose tracks --- src/dvr/dvr_rec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 39b929e7..94aaaa94 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -79,9 +79,9 @@ dvr_rec_subscribe(dvr_entry_t *de) flags = SUBSCRIPTION_RAW_MPEGTS; } else { streaming_queue_init(&de->de_sq, 0); - de->de_tsfix = tsfix_create(&de->de_sq.sq_st); + de->de_gh = globalheaders_create(&de->de_sq.sq_st); + st = de->de_tsfix = tsfix_create(de->de_gh); tsfix_set_start_time(de->de_tsfix, de->de_start - (60 * de->de_start_extra)); - st = de->de_gh = globalheaders_create(de->de_tsfix); flags = 0; } From c7aaa0b913b0a8f2c930a354ee1562c4d2a393e0 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 26 Jan 2013 11:56:17 +0000 Subject: [PATCH 334/503] Fix #1559 - ensure new accounts are init with default IP prefix. --- src/access.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/access.c b/src/access.c index 3e7614d5..53235465 100644 --- a/src/access.c +++ b/src/access.c @@ -451,6 +451,7 @@ access_set_prefix(access_entry_t *ae, const char *prefix) static access_entry_t * access_entry_find(const char *id, int create) { + access_ipmask_t *ai; access_entry_t *ae; char buf[20]; static int tally; @@ -478,6 +479,12 @@ access_entry_find(const char *id, int create) ae->ae_password = strdup("*"); ae->ae_comment = strdup("New entry"); TAILQ_INIT(&ae->ae_ipmasks); + ai = calloc(1, sizeof(access_ipmask_t)); + ai->ai_ipv6 = 1; + TAILQ_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); + ai = calloc(1, sizeof(access_ipmask_t)); + ai->ai_ipv6 = 0; + TAILQ_INSERT_HEAD(&ae->ae_ipmasks, ai, ai_link); TAILQ_INSERT_TAIL(&access_entries, ae, ae_link); return ae; } From e2550adb35ad3e7d91c384a899ff7c7e854b934f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 26 Jan 2013 12:16:08 +0000 Subject: [PATCH 335/503] Fix #1565 - access: ensure anon accounts are inored for HTSP default perms --- src/access.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/access.c b/src/access.c index 53235465..44f88108 100644 --- a/src/access.c +++ b/src/access.c @@ -324,6 +324,9 @@ access_get_by_addr(struct sockaddr *src) TAILQ_FOREACH(ae, &access_entries, ae_link) { + if(!ae->ae_enabled) + continue; + if(ae->ae_username[0] != '*') continue; From ce67dd97a925169fcb50f8a0da313354d82e41f5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 26 Jan 2013 00:37:18 +0000 Subject: [PATCH 336/503] dvb: cleanup SNR support whitelist --- src/dvb/dvb_adapter.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index a1ba8ae8..edd164b6 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -53,6 +53,14 @@ struct th_dvb_mux_instance_tree dvb_muxes; static void *dvb_adapter_input_dvr(void *aux); static void tda_init(th_dvb_adapter_t *tda); +/** + * Adapters that are known to have SNR support + */ +static const char* dvb_adapter_snr_whitelist[] = { + "Sony CXD2820R", + "stv090x", + NULL +}; /** * @@ -503,6 +511,7 @@ tda_add(int adapter_num) th_dvb_adapter_t *tda; struct dvb_frontend_info fe_info; DIR *dirp; + const char **str; /* Check valid adapter */ snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num); @@ -591,9 +600,14 @@ tda_add(int adapter_num) dvb_adapter_checkspeed(tda); /* Adapters known to provide valid SNR */ - if(strcasestr(fe_info.name, "Sony CXD2820R") || - strcasestr(fe_info.name, "stv090x")) - tda->tda_snr_valid = 1; + str = dvb_adapter_snr_whitelist; + while (*str) { + if (strcasestr(fe_info.name, *str)) { + tda->tda_snr_valid = 1; + break; + } + str++; + } /* Store */ tvhlog(LOG_INFO, "dvb", From 146126a610ff14fb46dce169cd60fddc753c45b2 Mon Sep 17 00:00:00 2001 From: amet Date: Tue, 29 Jan 2013 20:40:09 +0400 Subject: [PATCH 337/503] whitelist TBS 6981 card for SNR monitoring --- src/dvb/dvb_adapter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index edd164b6..ac85b454 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -59,6 +59,7 @@ static void tda_init(th_dvb_adapter_t *tda); static const char* dvb_adapter_snr_whitelist[] = { "Sony CXD2820R", "stv090x", + "TBS 6981", NULL }; From 673549141d5d51988e1ee9bdc30ed5a7cc32bf8a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 30 Jan 2013 12:15:20 +0000 Subject: [PATCH 338/503] timeshift: simplify total size locking --- src/atomic.h | 6 ++++++ src/timeshift/timeshift_filemgr.c | 17 +++++------------ src/timeshift/timeshift_writer.c | 5 ++--- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/atomic.h b/src/atomic.h index 1c3efb73..8627e64a 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -29,3 +29,9 @@ atomic_exchange(volatile int *ptr, int new) { return __sync_lock_test_and_set(ptr, new); } + +static inline uint64_t +atomic_add_u64(volatile uint64_t *ptr, uint64_t incr) +{ + return __sync_fetch_and_add(ptr, incr); +} diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 635f4769..64f724bc 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -31,6 +31,7 @@ #include "timeshift/private.h" #include "config2.h" #include "settings.h" +#include "atomic.h" static int timeshift_reaper_run; static timeshift_file_list_t timeshift_reaper_list; @@ -38,7 +39,6 @@ static pthread_t timeshift_reaper_thread; static pthread_mutex_t timeshift_reaper_lock; static pthread_cond_t timeshift_reaper_cond; -pthread_mutex_t timeshift_size_lock; size_t timeshift_total_size; /* ************************************************************************** @@ -75,10 +75,7 @@ static void* timeshift_reaper_callback ( void *p ) if (errno != ENOTEMPTY) tvhlog(LOG_ERR, "timeshift", "failed to remove %s [e=%s]", dpath, strerror(errno)); - pthread_mutex_lock(×hift_size_lock); - assert(tsf->size <= timeshift_total_size); - timeshift_total_size -= tsf->size; - pthread_mutex_unlock(×hift_size_lock); + atomic_add_u64(×hift_total_size, -tsf->size); /* Free memory */ while ((ti = TAILQ_FIRST(&tsf->iframes))) { @@ -151,9 +148,7 @@ void timeshift_filemgr_close ( timeshift_file_t *tsf ) if (r > 0) { tsf->size += r; - pthread_mutex_lock(×hift_size_lock); - timeshift_total_size += r; - pthread_mutex_unlock(×hift_size_lock); + atomic_add_u64(×hift_total_size, r); } close(tsf->fd); tsf->fd = -1; @@ -230,12 +225,11 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) } /* Check size */ - pthread_mutex_lock(×hift_size_lock); - if (!timeshift_unlimited_size && timeshift_total_size >= timeshift_max_size) { + if (!timeshift_unlimited_size && + atomic_add_u64(×hift_total_size, 0) >= timeshift_max_size) { tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); ts->full = 1; } - pthread_mutex_unlock(×hift_size_lock); /* Create new file */ tsf_tmp = NULL; @@ -332,7 +326,6 @@ void timeshift_filemgr_init ( void ) /* Size processing */ timeshift_total_size = 0; - pthread_mutex_init(×hift_size_lock, NULL); /* Start the reaper thread */ timeshift_reaper_run = 1; diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index bf944dbe..06810a86 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -20,6 +20,7 @@ #include "streaming.h" #include "timeshift.h" #include "timeshift/private.h" +#include "atomic.h" #include #include @@ -224,9 +225,7 @@ static inline ssize_t _process_msg0 if (err > 0) { tsf->last = sm->sm_time; tsf->size += err; - pthread_mutex_lock(×hift_size_lock); - timeshift_total_size += err; - pthread_mutex_unlock(×hift_size_lock); + atomic_add_u64(×hift_total_size, err); } return err; } From b83ad18a996500ffc5bcf8d3e0a5234aef47787d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 30 Jan 2013 12:23:36 +0000 Subject: [PATCH 339/503] timeshift: increase timeshift play buffer period. --- src/timeshift/private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 63b7479d..f462b91b 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -19,7 +19,7 @@ #ifndef __TVH_TIMESHIFT_PRIVATE_H__ #define __TVH_TIMESHIFT_PRIVATE_H__ -#define TIMESHIFT_PLAY_BUF 100000 // us to buffer in TX +#define TIMESHIFT_PLAY_BUF 500000 // us to buffer in TX #define TIMESHIFT_FILE_PERIOD 60 // number of secs in each buffer file /** From ea6d988409dbfde58fa57af2f4375f64d8e95303 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 30 Jan 2013 12:32:00 +0000 Subject: [PATCH 340/503] rawts: add missing (new) callback param to stop crash --- src/rawtsinput.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rawtsinput.c b/src/rawtsinput.c index 315e1651..134fcad5 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -92,6 +92,15 @@ rawts_service_quality(service_t *t) return 100; } +/** + * + */ +static int +rawts_service_is_enabled(service_t *t) +{ + return 1; +} + /** * Generate a descriptive name for the source @@ -133,6 +142,7 @@ rawts_service_add(rawts_t *rt, uint16_t sid, int pmt_pid) t->s_config_save = rawts_service_save; t->s_setsourceinfo = rawts_service_setsourceinfo; t->s_quality_index = rawts_service_quality; + t->s_is_enabled = rawts_service_is_enabled; t->s_svcname = strdup(tmp); From 0b16c754c1c4e80a15019ef386e23b1598c6c3fc Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 00:08:34 +0000 Subject: [PATCH 341/503] dvb: stop psi_section_reassemble from skipping packets --- src/psi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psi.c b/src/psi.c index 374815bf..10133b8c 100644 --- a/src/psi.c +++ b/src/psi.c @@ -89,15 +89,15 @@ psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc, int len = tsb[off++]; if(len > 0) { if(len > 188 - off) { - ps->ps_lock = 0; - return; + ps->ps_lock = 0; + return; } psi_section_reassemble0(ps, tsb + off, len, 0, crc, cb, opaque); off += len; } } - while(off < 188 && tsb[off] != 0xff) { + while(off < 188) { r = psi_section_reassemble0(ps, tsb + off, 188 - off, pusi, crc, cb, opaque); if(r < 0) { From 51fa4bb0e34083bfd96db4aadd57a78ae14fc2c1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 10:20:17 +0000 Subject: [PATCH 342/503] dvr: stop possible NULL ptr if messing about with config --- src/dvr/dvr_db.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index b7c66d68..78d5ad4b 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -849,6 +849,8 @@ dvr_entry_find_by_event(epg_broadcast_t *e) { dvr_entry_t *de; + if(!e->channel) return NULL; + LIST_FOREACH(de, &e->channel->ch_dvrs, de_channel_link) if(de->de_bcast == e) return de; return NULL; From 18c820ea17618845132407acc114a9820b53310f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 26 Jan 2013 23:00:30 +0000 Subject: [PATCH 343/503] dvb: reworked dvb mux creation to only use NIT data --- src/dvb/dvb.h | 2 +- src/dvb/dvb_multiplex.c | 16 +++- src/dvb/dvb_preconf.c | 2 +- src/dvb/dvb_tables.c | 189 ++++++++++++++++------------------------ 4 files changed, 89 insertions(+), 120 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 14e8ef68..28a9587d 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -425,7 +425,7 @@ th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda, uint16_t onid, uint16_t tsid, const char *network, const char *logprefix, int enabled, int initialscan, const char *identifier, - dvb_satconf_t *satconf); + dvb_satconf_t *satconf, int create); void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 9341988d..7f0eb0c3 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -155,7 +155,7 @@ th_dvb_mux_instance_t * dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, uint16_t onid, uint16_t tsid, const char *network, const char *source, int enabled, int initialscan, const char *identifier, - dvb_satconf_t *satconf) + dvb_satconf_t *satconf, int create) { th_dvb_mux_instance_t *tdmi, *c; char buf[200]; @@ -204,6 +204,11 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, tdmi->tdmi_network_id = onid; save = 1; } + if(network && strcmp(tdmi->tdmi_network ?: "", network)) { + free(tdmi->tdmi_network); + tdmi->tdmi_network = strdup(network); + save = 1; + } /* HACK - load old transports and remove old mux config */ if(identifier) { @@ -225,6 +230,9 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, return NULL; } + if (!create) + return NULL; + tdmi = calloc(1, sizeof(th_dvb_mux_instance_t)); if(identifier == NULL) { @@ -791,7 +799,7 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) tdmi = dvb_mux_create(tda, &dmc, onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled, initscan, - identifier, NULL); + identifier, NULL, 1); if(tdmi != NULL) { if((s = htsmsg_get_str(m, "status")) != NULL) @@ -1170,7 +1178,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, } dmc.dmc_polarisation = polarisation; - tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL); + tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL, 1); if(tdmi == NULL) return "Mux already exist"; @@ -1197,7 +1205,7 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, tdmi_src->tdmi_transport_stream_id, tdmi_src->tdmi_network, "copy operation", tdmi_src->tdmi_enabled, - 1, NULL, satconf); + 1, NULL, satconf, 1); if(tdmi_dst == NULL) return -1; // Already exist diff --git a/src/dvb/dvb_preconf.c b/src/dvb/dvb_preconf.c index 755340db..d1805242 100644 --- a/src/dvb/dvb_preconf.c +++ b/src/dvb/dvb_preconf.c @@ -98,7 +98,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net, dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); - dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL); + dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL, 1); } } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index ecc540b8..26f410e9 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -332,22 +332,19 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tsid = ptr[0] << 8 | ptr[1]; onid = ptr[5] << 8 | ptr[6]; if (tableid == 0x42) { - if(tdmi->tdmi_transport_stream_id != tsid) + if(tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) return -1; - if(!tdmi->tdmi_network_id) - dvb_mux_set_onid(tdmi, onid); } else { LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id != onid) break; - if (!tdmi) return 0; + if (!tdmi) return -1; } // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; // last_section_number = ptr[4]; - // original_network_id = ptr[5] << 8 | ptr[6]; // reserved = ptr[7]; if((ptr[2] & 1) == 0) { @@ -478,7 +475,6 @@ static int dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { - th_dvb_mux_instance_t *other; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; uint16_t service, pmt, tsid; @@ -491,21 +487,8 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } tsid = (ptr[0] << 8) | ptr[1]; - - // Make sure this TSID is not already known on another mux - // That might indicate that we have accedentally received a PAT - // from another mux - LIST_FOREACH(other, &tda->tda_muxes, tdmi_adapter_link) - if(other != tdmi && - other->tdmi_conf.dmc_satconf == tdmi->tdmi_conf.dmc_satconf && - other->tdmi_transport_stream_id == tsid && - other->tdmi_network_id == tdmi->tdmi_network_id) - return -1; - - if(tdmi->tdmi_transport_stream_id == 0xffff) - dvb_mux_set_tsid(tdmi, tsid); - else if (tdmi->tdmi_transport_stream_id != tsid) - return -1; // TSID mismatches, skip packet, may be from another mux + if (tdmi->tdmi_transport_stream_id != tsid) + return -1; ptr += 5; len -= 5; @@ -650,14 +633,11 @@ static const fe_hierarchy_t hierarchy_info_tab [8] = { */ static int dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid, uint16_t onid) + uint16_t tsid, uint16_t onid, const char *netname) { struct dvb_mux_conf dmc; int freq, symrate; - if(!tdmi->tdmi_adapter->tda_autodiscovery) - return -1; - if(len < 11) return -1; @@ -687,8 +667,10 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x07]; - dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, - "automatic mux discovery", 1, 1, NULL, NULL); + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, + "automatic mux discovery", 1, 1, NULL, NULL, + tdmi->tdmi_adapter->tda_autodiscovery); + return 0; } @@ -697,15 +679,12 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, */ static int dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid, uint16_t onid) + uint16_t tsid, uint16_t onid, const char *netname) { int freq, symrate; // uint16_t orbital_pos; struct dvb_mux_conf dmc; - if(!tdmi->tdmi_adapter->tda_autodiscovery) - return -1; - if(len < 11) return -1; @@ -772,8 +751,10 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } #endif + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, - "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf); + "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf, + tdmi->tdmi_adapter->tda_autodiscovery); return 0; } @@ -784,14 +765,11 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, */ static int dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid, uint16_t onid) + uint16_t tsid, uint16_t onid, const char *netname) { struct dvb_mux_conf dmc; int freq; - if(!tdmi->tdmi_adapter->tda_autodiscovery) - return -1; - if(len < 11) return -1; @@ -812,8 +790,9 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dmc.dmc_fe_params.u.ofdm.guard_interval=guard_interval_tab[(ptr[6] & 0x18) >> 3]; dmc.dmc_fe_params.u.ofdm.transmission_mode=transmission_mode_tab[(ptr[6] & 0x06) >> 1]; - dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, - "automatic mux discovery", 1, 1, NULL, NULL); + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, + "automatic mux discovery", 1, 1, NULL, NULL, + tdmi->tdmi_adapter->tda_autodiscovery); return 0; } @@ -866,107 +845,89 @@ static int dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { - uint8_t tag, tlen; - int ntl; - char networkname[256]; + uint8_t dtag, dlen; + uint16_t llen; + char netname[256]; uint16_t tsid, onid; uint16_t network_id = (ptr[0] << 8) | ptr[1]; + netname[0] = '\0'; - if(tdmi->tdmi_adapter->tda_nitoid) { - if(tableid != 0x41) - return -1; - - if(network_id != tdmi->tdmi_adapter->tda_nitoid) - return -1; - - } else { - if(tableid != 0x40) - return -1; - } - - if((ptr[2] & 1) == 0) { - /* current_next_indicator == next, skip this */ - return -1; - } - - ptr += 5; - len -= 5; - - ntl = ((ptr[0] & 0xf) << 8) | ptr[1]; - ptr += 2; - len -= 2; - if(ntl > len) + /* Check NID */ + if(tdmi->tdmi_adapter->tda_nitoid && + tdmi->tdmi_adapter->tda_nitoid != network_id) return -1; - while(ntl > 2) { - tag = *ptr++; - tlen = *ptr++; - len -= 2; - ntl -= 2; + /* Ignore non-current */ + if((ptr[2] & 1) == 0) + return -1; - switch(tag) { - case DVB_DESC_NETWORK_NAME: - if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen, NULL, NULL)) - return -1; + /* Network descriptors */ + llen = ((ptr[5] & 0xf) << 8) | ptr[6]; + ptr += 7; + len -= llen + 7; + if (len < 0) + return -1; - if(strcmp(tdmi->tdmi_network ?: "", networkname)) - dvb_mux_set_networkname(tdmi, networkname); - break; + while(llen > 2) { + dtag = ptr[0]; + dlen = ptr[1]; + + switch(dtag) { + case DVB_DESC_NETWORK_NAME: + if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL)) + return -1; + break; } - ptr += tlen; - len -= tlen; - ntl -= tlen; + ptr += dlen + 2; + llen -= dlen + 2; } - - if(len < 2) + if (llen) return -1; - ntl = ((ptr[0] & 0xf) << 8) | ptr[1]; + /* Transport loop */ + llen = ((ptr[0] & 0xf) << 8) | ptr[1]; ptr += 2; len -= 2; - - if(len < ntl) + if (llen > len) return -1; - while(len >= 6) { tsid = ( ptr[0] << 8) | ptr[1]; onid = ( ptr[2] << 8) | ptr[3]; - ntl = ((ptr[4] & 0xf) << 8) | ptr[5]; + llen = ((ptr[4] & 0xf) << 8) | ptr[5]; ptr += 6; - len -= 6; - if(ntl > len) - break; + len -= llen + 6; + if(len < 0) + return -1; - while(ntl > 2) { - tag = *ptr++; - tlen = *ptr++; - len -= 2; - ntl -= 2; + while(llen > 2) { + dtag = ptr[0]; + dlen = ptr[1]; - switch(tag) { - case DVB_DESC_SAT: - if(tdmi->tdmi_adapter->tda_type == FE_QPSK) - dvb_table_sat_delivery(tdmi, ptr, tlen, tsid, onid); - break; - case DVB_DESC_CABLE: - if(tdmi->tdmi_adapter->tda_type == FE_QAM) - dvb_table_cable_delivery(tdmi, ptr, tlen, tsid, onid); - break; - case DVB_DESC_TERR: - if(tdmi->tdmi_adapter->tda_type == FE_OFDM) - dvb_table_terr_delivery(tdmi, ptr, tlen, tsid, onid); - break; - case DVB_DESC_LOCAL_CHAN: - dvb_table_local_channel(tdmi, ptr, tlen, tsid, onid); - break; + switch(dtag) { + case DVB_DESC_SAT: + if(tdmi->tdmi_adapter->tda_type == FE_QPSK) + dvb_table_sat_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); + break; + case DVB_DESC_CABLE: + if(tdmi->tdmi_adapter->tda_type == FE_QAM) + dvb_table_cable_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); + break; + case DVB_DESC_TERR: + if(tdmi->tdmi_adapter->tda_type == FE_OFDM) + dvb_table_terr_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); + break; + case DVB_DESC_LOCAL_CHAN: + dvb_table_local_channel(tdmi, ptr+2, dlen, tsid, onid); + break; } - ptr += tlen; - len -= tlen; - ntl -= tlen; + llen -= dlen + 2; + ptr += dlen + 2; } + if (llen) + return -1; } return 0; } From b90b7f96b0e65401956080f5558686a94b7e2838 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 30 Jan 2013 20:40:53 +0000 Subject: [PATCH 344/503] dvb: add some debug logging to dvb table code --- src/dvb/dvb_tables.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 26f410e9..328599e3 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -42,6 +42,13 @@ #include "notify.h" #include "cwc.h" +#if TDT_TRACE +#define TRACE(_pre, _fmt, ...)\ +tvhlog(LOG_DEBUG, "tdt-"_pre, _fmt, __VA_ARGS__) +#else +#define TRACE(_pre, _fmt, ...) (void)0 +#endif + /** * @@ -321,6 +328,9 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, char provider[256]; char chname0[256], *chname; uint8_t stype; +#if TDT_TRACE + uint8_t running_status; +#endif int l; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; @@ -341,6 +351,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, break; if (!tdmi) return -1; } + TRACE("sdt", "onid %04X tsid %04X", onid, tsid); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; @@ -360,9 +371,13 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, int save = 0; service_id = ptr[0] << 8 | ptr[1]; // reserved = ptr[2]; - // running_status = (ptr[3] >> 5) & 0x7; +#if TDT_TRACE + running_status = (ptr[3] >> 5) & 0x7; +#endif free_ca_mode = (ptr[3] >> 4) & 0x1; dllen = ((ptr[3] & 0x0f) << 8) | ptr[4]; + TRACE("sdt", " sid %04X running %d free_ca %d", + service_id, running_status, free_ca_mode); len -= 5; ptr += 5; @@ -393,6 +408,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(dvb_desc_service(ptr, dlen, &stype, provider, sizeof(provider), chname0, sizeof(chname0)) == 0) { + TRACE("sdt", " stype = %d, provider = %s, name = %s", + stype, provider, chname0); chname = chname0; /* Some providers insert spaces. Clean up that (both heading and trailing) */ @@ -652,6 +669,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq * 100; + TRACE("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + @@ -695,6 +713,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]); dmc.dmc_fe_params.frequency = freq * 10; + TRACE("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); if(!freq) return -1; @@ -782,6 +801,7 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq; + TRACE("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); dmc.dmc_fe_params.u.ofdm.bandwidth = bandwidth_tab[(ptr[4] & 0xe0) >> 5]; dmc.dmc_fe_params.u.ofdm.constellation=constellation_tab[(ptr[5] & 0xc0) >> 6]; dmc.dmc_fe_params.u.ofdm.hierarchy_information=hierarchy_info_tab[(ptr[5] & 0x38) >> 3]; @@ -896,6 +916,9 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, onid = ( ptr[2] << 8) | ptr[3]; llen = ((ptr[4] & 0xf) << 8) | ptr[5]; + TRACE("nit", "netw %d/%s onid %04X tsid %04X", + network_id, netname, onid, tsid); + ptr += 6; len -= llen + 6; if(len < 0) From e00ba4c85f9d177c152568969e5e07d5e84e21a5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 10:27:40 +0000 Subject: [PATCH 345/503] dvb: allow off-air services to be inserted into the service list --- src/dvb/dvb_service.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index a91e92d5..61242bef 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -143,7 +143,7 @@ dvb_service_is_enabled(service_t *t) { th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - return tda->tda_enabled && tdmi->tdmi_enabled && t->s_enabled; + return tda->tda_enabled && tdmi->tdmi_enabled && t->s_enabled && t->s_pmt_pid; } @@ -416,12 +416,18 @@ dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) { if(t->s_dvb_service_id == sid) - return t; + break; + } + + /* Existing - updated PMT_PID if required */ + if (t) { + if (pmt_pid && pmt_pid != t->s_pmt_pid) { + t->s_pmt_pid = pmt_pid; + *save = 1; + } + return t; } - if(pmt_pid == 0) - return NULL; - if(identifier == NULL) { snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid); identifier = tmp; From 4335797e53e41a8cdec0e6c12d5c7e46f5775c27 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 10:48:15 +0000 Subject: [PATCH 346/503] Fix #1521 - dvr: some minor corrections to PR code ensure that recordings are properly loaded in the event no channel exists and that the internal channel name is preferred not the DE on. --- src/channels.c | 6 ++++++ src/dvr/dvr.h | 3 +-- src/dvr/dvr_db.c | 25 +++++++++++++++---------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/channels.c b/src/channels.c index 92b9b434..36404368 100644 --- a/src/channels.c +++ b/src/channels.c @@ -346,6 +346,7 @@ channel_save(channel_t *ch) int channel_rename(channel_t *ch, const char *newname) { + dvr_entry_t *de; service_t *t; lock_assert(&global_lock); @@ -364,6 +365,11 @@ channel_rename(channel_t *ch, const char *newname) LIST_FOREACH(t, &ch->ch_services, s_ch_link) t->s_config_save(t); + + LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link) { + dvr_entry_save(de); + dvr_entry_notify(de); + } channel_save(ch); htsp_channel_update(ch); diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 03d1d9ce..4bc56aa7 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -200,8 +200,7 @@ typedef struct dvr_entry { } dvr_entry_t; -#define DVR_CH_NAME(e) ((e)->de_channel_name == NULL ? (e)-> de_channel->ch_name : (e)->de_channel_name) - +#define DVR_CH_NAME(e) ((e)->de_channel == NULL ? (e)->de_channel_name : (e)-> de_channel->ch_name) /** * Autorec entry diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 2ffc4ae3..ee020d2e 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -240,10 +240,12 @@ dvr_entry_link(dvr_entry_t *de) gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, de->de_stop + cfg->dvr_retention_days * 86400); - } else { + } else if (de->de_channel) { de->de_sched_state = DVR_SCHEDULED; gtimer_arm_abs(&de->de_timer, dvr_timer_start_recording, de, preamble); + } else { + de->de_sched_state = DVR_NOSTATE; } htsp_dvr_entry_add(de); } @@ -473,7 +475,7 @@ static void dvr_db_load_one(htsmsg_t *c, int id) { dvr_entry_t *de; - const char *s, *creator; + const char *chname, *s, *creator; channel_t *ch; uint32_t start, stop, bcid; int d; @@ -485,11 +487,10 @@ dvr_db_load_one(htsmsg_t *c, int id) if(htsmsg_get_u32(c, "stop", &stop)) return; - if((s = htsmsg_get_str(c, "channel")) == NULL) + if((chname = htsmsg_get_str(c, "channel")) == NULL) return; - if((ch = channel_find_by_name(s, 0, 1)) == NULL) - return; - + ch = channel_find_by_name(chname, 0, 0); + s = htsmsg_get_str(c, "config_name"); cfg = dvr_config_find_by_name_default(s); @@ -504,8 +505,12 @@ dvr_db_load_one(htsmsg_t *c, int id) de_tally = MAX(id, de_tally); - de->de_channel = ch; - LIST_INSERT_HEAD(&de->de_channel->ch_dvrs, de, de_channel_link); + if (ch) { + de->de_channel = ch; + LIST_INSERT_HEAD(&de->de_channel->ch_dvrs, de, de_channel_link); + } else { + de->de_channel_name = strdup(chname); + } de->de_start = start; de->de_stop = stop; @@ -515,7 +520,7 @@ dvr_db_load_one(htsmsg_t *c, int id) de->de_pri = dvr_pri2val(htsmsg_get_str(c, "pri")); if(htsmsg_get_s32(c, "start_extra", &d)) - if (ch->ch_dvr_extra_time_pre) + if (ch && ch->ch_dvr_extra_time_pre) de->de_start_extra = ch->ch_dvr_extra_time_pre; else de->de_start_extra = cfg->dvr_extra_time_pre; @@ -523,7 +528,7 @@ dvr_db_load_one(htsmsg_t *c, int id) de->de_start_extra = d; if(htsmsg_get_s32(c, "stop_extra", &d)) - if (ch->ch_dvr_extra_time_post) + if (ch && ch->ch_dvr_extra_time_post) de->de_stop_extra = ch->ch_dvr_extra_time_post; else de->de_stop_extra = cfg->dvr_extra_time_post; From 8f5d8fe9bf06515addea68bc940591eeb396100d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 11:14:59 +0000 Subject: [PATCH 347/503] dvr: ensure subsystems are notified when dvr starts (and filename is set) --- src/dvr/dvr_rec.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index bf6a02e2..adcb4302 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -32,6 +32,7 @@ #include "service.h" #include "plumbing/tsfix.h" #include "plumbing/globalheaders.h" +#include "htsp_server.h" #include "muxer.h" @@ -465,11 +466,15 @@ dvr_thread(void *aux) } if(!started) { - pthread_mutex_lock(&global_lock); - dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0); - if(dvr_rec_start(de, sm->sm_data) == 0) - started = 1; - pthread_mutex_unlock(&global_lock); + pthread_mutex_lock(&global_lock); + dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0); + if(dvr_rec_start(de, sm->sm_data) == 0) { + started = 1; + dvr_entry_notify(de); + htsp_dvr_entry_update(de); + dvr_entry_save(de); + } + pthread_mutex_unlock(&global_lock); } break; From 43ae54c062edfbe0bd9dbfc07471484d151ef649 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 11:27:00 +0000 Subject: [PATCH 348/503] Fix #1569 - dvr: simplify filename cleanup --- src/dvr/dvr_rec.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index adcb4302..0893562d 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -126,12 +126,11 @@ cleanupfilename(char *s, int dvr_flags) { int i, len = strlen(s); for(i = 0; i < len; i++) { - if(s[i] == '/' || s[i] == ':' || s[i] == '\\' || s[i] == '<' || - s[i] == '>' || s[i] == '|' || s[i] == '*' || s[i] == '?') - s[i] = '-'; - - if((dvr_flags & DVR_WHITESPACE_IN_TITLE) && s[i] == ' ') + if((dvr_flags & DVR_WHITESPACE_IN_TITLE) && (s[i] == ' ' || s[i] == '\t')) s[i] = '-'; + + if((s[i] < 32) || (s[i] > 122) || (strchr("/:\\<>|*?'\"", s[i]) != NULL)) + s[i] = '-'; } } From 432009acc6d7bb1c1ff2b20fa7ad848a2bee2cb9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 11:52:18 +0000 Subject: [PATCH 349/503] timeshift: fix atomic add problems on i386 --- src/timeshift.c | 2 +- src/timeshift.h | 6 ++---- src/timeshift/timeshift_filemgr.c | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/timeshift.c b/src/timeshift.c index 2ecc39b4..de3766e9 100644 --- a/src/timeshift.c +++ b/src/timeshift.c @@ -41,7 +41,7 @@ char *timeshift_path; int timeshift_unlimited_period; uint32_t timeshift_max_period; int timeshift_unlimited_size; -size_t timeshift_max_size; +uint64_t timeshift_max_size; /* * Intialise global file manager diff --git a/src/timeshift.h b/src/timeshift.h index 3c6fc0ab..db173625 100644 --- a/src/timeshift.h +++ b/src/timeshift.h @@ -25,10 +25,8 @@ extern char *timeshift_path; extern int timeshift_unlimited_period; extern uint32_t timeshift_max_period; extern int timeshift_unlimited_size; -extern size_t timeshift_max_size; - -extern size_t timeshift_total_size; -extern pthread_mutex_t timeshift_size_lock; +extern uint64_t timeshift_max_size; +extern uint64_t timeshift_total_size; typedef struct timeshift_status { diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 64f724bc..185d80a5 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -39,7 +39,7 @@ static pthread_t timeshift_reaper_thread; static pthread_mutex_t timeshift_reaper_lock; static pthread_cond_t timeshift_reaper_cond; -size_t timeshift_total_size; +uint64_t timeshift_total_size; /* ************************************************************************** * File reaper thread From 7a52ff434c8287d0f1a68d709cb41d0c4678c737 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 11:53:15 +0000 Subject: [PATCH 350/503] support: re-instate getmuxlist script to ensure pbuilder script works --- configure | 14 ++++---------- support/getmuxlist | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) create mode 100755 support/getmuxlist diff --git a/configure b/configure index e9bc7e4e..1b618cea 100755 --- a/configure +++ b/configure @@ -175,16 +175,10 @@ fi # if enabled linuxdvb && enabled dvbscan; then printf "${TAB}" "fetching dvb-scan files ..." - if [ -d ${ROOTDIR}/data/dvb-scan/.git ]; then - (cd ${ROOTDIR}/data/dvb-scan; git pull) &> /dev/null - else - rm -rf ${ROOTDIR}/data/dvb-scan &> /dev/null - URL=git://linuxtv.org/dtv-scan-tables.git - git clone $URL ${ROOTDIR}/data/dvb-scan &> /dev/null - if [ $? -ne 0 ]; then - echo "fail" - die "Failed to fetch dvb-scan data (use --disable-dvbscan)" - fi + ${ROOTDIR}/support/getmuxlist + if [ $? -ne 0 ]; then + echo "fail" + die "Failed to fetch dvb-scan data (use --disable-dvbscan)" fi echo "ok" fi diff --git a/support/getmuxlist b/support/getmuxlist new file mode 100755 index 00000000..f115f871 --- /dev/null +++ b/support/getmuxlist @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Fetch DVB scan files +# + +# Arguments +DIR=$1 +[ -z "$DIR" ] && DIR=$(dirname $0)/../data/dvb-scan + +# Update +if [ -d ${DIR}/.git ]; then + (cd ${DIR}; git pull) &> /dev/null + +# Fetch +else + rm -rf ${DIR} &> /dev/null + URL=git://linuxtv.org/dtv-scan-tables.git + git clone $URL ${DIR} &> /dev/null +fi From ef97ed35a1d0cec86a530008420c47388226f039 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 12:20:33 +0000 Subject: [PATCH 351/503] debian: add git to dep list. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index cc924927..ebb346e6 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: tvheadend Section: video Priority: extra Maintainer: Andreas Öman -Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev +Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev, git Standards-Version: 3.7.3 Package: tvheadend From af095a3e51c9e051f9bf2614bc74a21643725675 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 12:34:42 +0000 Subject: [PATCH 352/503] debian: looks like git pseudo package didn't exist in lucid --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index ebb346e6..7c0de44a 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: tvheadend Section: video Priority: extra Maintainer: Andreas Öman -Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev, git +Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev, git-core Standards-Version: 3.7.3 Package: tvheadend From 2e25766a10d5ce91e31d8fcea5a1409f846f8626 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 Jan 2013 23:18:21 +0000 Subject: [PATCH 353/503] dvb: forgot to pass netname to sat mux_create --- src/dvb/dvb_tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 328599e3..302c7b53 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -771,7 +771,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, #endif - dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf, tdmi->tdmi_adapter->tda_autodiscovery); From 2b3de552b94c42eea0bee48242d1521e45ba2978 Mon Sep 17 00:00:00 2001 From: Piotras Date: Thu, 31 Jan 2013 23:38:48 +0000 Subject: [PATCH 354/503] SNR support for all TBS tuners Drivers fot TBS tuner support SNR this change work for all models ;) --- src/dvb/dvb_adapter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index ac85b454..41d7fbbe 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -59,7 +59,7 @@ static void tda_init(th_dvb_adapter_t *tda); static const char* dvb_adapter_snr_whitelist[] = { "Sony CXD2820R", "stv090x", - "TBS 6981", + "TurboSight", NULL }; From dc160b9e48ab09bb6cb8a719be6d9a495760a61e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Feb 2013 20:53:36 +0000 Subject: [PATCH 355/503] dvb: re-instate some of the form scanning code. Mux TSID and ONID can now be updated from other tables if, and only if, they values are not yet set. Values from the NIT will ALWAYS override existing values, as they're hopefully more reliable. --- src/dvb/dvb.h | 4 ++-- src/dvb/dvb_multiplex.c | 16 +++++++++++++--- src/dvb/dvb_tables.c | 18 +++++++++++------- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 28a9587d..50ce547a 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -429,9 +429,9 @@ th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda, void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); -void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid); +void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force); -void dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid); +void dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force); void dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled); diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 7f0eb0c3..7fd5e2c2 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -169,12 +169,14 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, } if(tdmi != NULL) { + /* Update stuff ... */ int save = 0; char buf2[1024]; buf2[0] = 0; - if(tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { + if(tdmi->tdmi_adapter->tda_autodiscovery && + tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { #if DVB_API_VERSION >= 5 snprintf(buf2, sizeof(buf2), " ("); if (tdmi->tdmi_conf.dmc_fe_modulation != dmc->dmc_fe_modulation) @@ -861,10 +863,14 @@ dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) * */ void -dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid) +dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force) { htsmsg_t *m; + if (!force) + if (tdmi->tdmi_transport_stream_id != 0xFFFF || tsid == 0xFFFF) + return; + tdmi->tdmi_transport_stream_id = tsid; dvb_mux_save(tdmi); @@ -879,10 +885,14 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid) * */ void -dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid) +dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force) { htsmsg_t *m; + if (force) + if (tdmi->tdmi_network_id != 0 || onid == 0) + return; + tdmi->tdmi_network_id = onid; dvb_mux_save(tdmi); diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 302c7b53..f3ddb560 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -342,6 +342,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tsid = ptr[0] << 8 | ptr[1]; onid = ptr[5] << 8 | ptr[6]; if (tableid == 0x42) { + dvb_mux_set_tsid(tdmi, tsid, 0); + dvb_mux_set_onid(tdmi, onid, 0); if(tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) return -1; } else { @@ -504,6 +506,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } tsid = (ptr[0] << 8) | ptr[1]; + dvb_mux_set_tsid(tdmi, tsid, 0); if (tdmi->tdmi_transport_stream_id != tsid) return -1; @@ -844,11 +847,11 @@ dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, t = dvb_service_find(tdmi, sid, 0, NULL); if(t != NULL) { - if(t->s_channel_number != chan) { - t->s_channel_number = chan; - t->s_config_save(t); - service_refresh_channel(t); - } + if(t->s_channel_number != chan) { + t->s_channel_number = chan; + t->s_config_save(t); + service_refresh_channel(t); + } } } ptr += 4; @@ -999,8 +1002,9 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, /* Search all muxes on adapter */ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) - if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid); - break; + if(tdmi->tdmi_transport_stream_id == tsid && + tdmi->tdmi_network_id == onid); + break; if(tdmi == NULL) continue; From 03e9f1179d67bd2b4ccd7a0f3fd9c5fc039c0d5c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 25 Jan 2013 10:47:20 +0000 Subject: [PATCH 356/503] dvb: close dvr device on each re-tune --- src/dvb/dvb.h | 2 ++ src/dvb/dvb_adapter.c | 30 ++++++++++++++++++------------ src/dvb/dvb_fe.c | 1 + 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 50ce547a..468b9271 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -344,6 +344,8 @@ void dvb_adapter_start (th_dvb_adapter_t *tda); void dvb_adapter_stop (th_dvb_adapter_t *tda); +void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); + void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index ac85b454..680c96a6 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -713,6 +713,22 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } } +void +dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) +{ + /* Stop DVR thread */ + if (tda->tda_dvr_pipe.rd != -1) { + tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); + int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); + assert(!err); + pthread_join(tda->tda_dvr_thread, NULL); + close(tda->tda_dvr_pipe.rd); + close(tda->tda_dvr_pipe.wr); + tda->tda_dvr_pipe.rd = -1; + tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); + } +} + void dvb_adapter_stop ( th_dvb_adapter_t *tda ) { @@ -729,18 +745,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) tda->tda_fe_fd = -1; } - /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { - tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); - assert(!err); - pthread_join(tda->tda_dvr_thread, NULL); - close(tda->tda_dvr_pipe.rd); - close(tda->tda_dvr_pipe.wr); - tda->tda_dvr_pipe.rd = -1; - tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); - } - + dvb_adapter_stop_dvr(tda); + dvb_adapter_notify(tda); } diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index a4e58089..0f27b898 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -282,6 +282,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) } dvb_table_flush_all(tdmi); + dvb_adapter_stop_dvr(tda); assert(tdmi->tdmi_scan_queue == NULL); From 665ca55b4eccdf79b8c965b023f763de142269ad Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Feb 2013 21:00:34 +0000 Subject: [PATCH 357/503] dvr: don't run postproc script unless there is actually a file to process --- src/dvr/dvr_rec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 0893562d..1471ecc6 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -628,6 +628,6 @@ dvr_thread_epilog(dvr_entry_t *de) de->de_mux = NULL; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); - if(cfg->dvr_postproc) + if(cfg->dvr_postproc && de->de_filename) dvr_spawn_postproc(de,cfg->dvr_postproc); } From ae008ff96a64a54eb8a47155092ad83c9da4d3ec Mon Sep 17 00:00:00 2001 From: Vuolter Date: Sat, 26 Jan 2013 17:55:42 +0100 Subject: [PATCH 358/503] docs: updated the README and add markdown --- README | 30 ------------------------------ README.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 30 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 6edba018..00000000 --- a/README +++ /dev/null @@ -1,30 +0,0 @@ - Tvheadend TV streaming server - ============================= - - (c) 2006 - 2012 Andreas Öman, et al. - - -How to build for Linux -====================== - -First you need to configure: - -$ ./configure - -If any dependencies are missing the configure script will complain or attempt -to disable optional features. - -$ make - -Build the binary, after build the binary resides in 'build.linux/'. -Thus, to start it, just type: - -$ ./build.linux/tvheadend - -Settings are stored in $HOME/.hts/tvheadend - -Further information -=================== - -For more information about building, including generating packages please -visit https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Building diff --git a/README.md b/README.md new file mode 100644 index 00000000..8899c76a --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +Tvheadend (TV streaming server) +==================================== +(c) 2006 - 2013 Andreas Öman, et al. + +How to build for Linux +---------------------- + +First you need to configure: + + $ ./configure + +If any dependencies are missing the configure script will complain or attempt +to disable optional features. + +Build the binary: + + $ make + +After build, the binary resides in `build.linux/`. + +Thus, to start it, just type: + + $ ./build.linux/tvheadend + +Settings are stored in `$HOME/.hts/tvheadend`. + +Further information +------------------- + +For more information about building, including generating packages please visit: +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Building +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Packaging +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Git From df9eeef371b2432978b7a01c3d5be29c46ed380c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Feb 2013 21:15:23 +0000 Subject: [PATCH 359/503] docs: reinstate original README --- README | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 00000000..8899c76a --- /dev/null +++ b/README @@ -0,0 +1,33 @@ +Tvheadend (TV streaming server) +==================================== +(c) 2006 - 2013 Andreas Öman, et al. + +How to build for Linux +---------------------- + +First you need to configure: + + $ ./configure + +If any dependencies are missing the configure script will complain or attempt +to disable optional features. + +Build the binary: + + $ make + +After build, the binary resides in `build.linux/`. + +Thus, to start it, just type: + + $ ./build.linux/tvheadend + +Settings are stored in `$HOME/.hts/tvheadend`. + +Further information +------------------- + +For more information about building, including generating packages please visit: +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Building +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Packaging +> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Git From 7a7835dc0a11ab0541076896211c57825765eecf Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 11:47:38 +0000 Subject: [PATCH 360/503] dvb: add adapter bandwidth to status page. --- src/dvb/dvb.h | 2 ++ src/dvb/dvb_adapter.c | 2 ++ src/dvb/dvb_fe.c | 6 +++++- src/webui/static/app/status.js | 12 +++++++++--- src/webui/static/app/tvadapters.js | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 468b9271..aaccd946 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -265,6 +265,8 @@ typedef struct th_dvb_adapter { int tda_rawmode; + int tda_bytes; + // Full mux streaming, protected via the delivery mutex streaming_pad_t tda_streaming_pad; diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 570c0a77..07172ea8 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -47,6 +47,7 @@ #include "service.h" #include "epggrab.h" #include "diseqc.h" +#include "atomic.h" struct th_dvb_adapter_queue dvb_adapters; struct th_dvb_mux_instance_tree dvb_muxes; @@ -1063,6 +1064,7 @@ dvb_adapter_input_dvr(void *aux) } } r += c; + atomic_add(&tda->tda_bytes, c); /* not enough data */ if (r < 188) continue; diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 0f27b898..779e79b3 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -41,6 +41,7 @@ #include "dvr/dvr.h" #include "service.h" #include "streaming.h" +#include "atomic.h" #include "epggrab.h" @@ -90,7 +91,7 @@ dvb_fe_monitor(void *aux) { th_dvb_adapter_t *tda = aux; fe_status_t fe_status; - int status, v, vv, i, fec, q; + int status, v, vv, i, fec, q, bw; th_dvb_mux_instance_t *tdmi = tda->tda_mux_current; char buf[50]; signal_status_t sigstat; @@ -218,6 +219,8 @@ dvb_fe_monitor(void *aux) } } + bw = atomic_exchange(&tda->tda_bytes, 0); + if(notify) { htsmsg_t *m = htsmsg_create_map(); htsmsg_add_str(m, "id", tdmi->tdmi_identifier); @@ -238,6 +241,7 @@ dvb_fe_monitor(void *aux) htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); htsmsg_add_dbl(m, "uncavg", tdmi->tdmi_unc_avg); + htsmsg_add_u32(m, "bw", bw); notify_by_msg("tvAdapter", m); } diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index 3ecf1f17..5aacae41 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -148,6 +148,10 @@ tvheadend.status_adapters = function() { colored : true }); + function renderBw(value) { + return parseInt(value / 125); + } + var cm = new Ext.grid.ColumnModel([{ width : 50, header : "Name", @@ -160,6 +164,11 @@ tvheadend.status_adapters = function() { width : 100, header : "Currently tuned to", dataIndex : 'currentMux' + },{ + width : 100, + header : "Bandwidth (kb/s)", + dataIndex : 'bw', + renderer: renderBw },{ width : 50, header : "Bit error rate", @@ -198,9 +207,6 @@ tvheadend.status_adapters = function() { return panel; } - - - tvheadend.status = function() { var panel = new Ext.Panel({ diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index dc14c198..3661fabd 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -7,7 +7,7 @@ tvheadend.tvAdapterStore = new Ext.data.JsonStore({ fields : [ 'identifier', 'type', 'name', 'path', 'devicename', 'hostconnection', 'currentMux', 'services', 'muxes', 'initialMuxes', 'satConf', 'deliverySystem', 'freqMin', 'freqMax', 'freqStep', - 'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg'], + 'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg', 'bw'], url : 'tv/adapter' }); From bec277d699284592882b4ed30aa02e6c07db996f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 11:51:13 +0000 Subject: [PATCH 361/503] Revert "dvb: close dvr device on each re-tune" This reverts commit 03e9f1179d67bd2b4ccd7a0f3fd9c5fc039c0d5c. --- src/dvb/dvb.h | 2 -- src/dvb/dvb_adapter.c | 30 ++++++++++++------------------ src/dvb/dvb_fe.c | 1 - 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index aaccd946..b065467f 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -346,8 +346,6 @@ void dvb_adapter_start (th_dvb_adapter_t *tda); void dvb_adapter_stop (th_dvb_adapter_t *tda); -void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); - void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 07172ea8..e4b30c51 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -714,22 +714,6 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } } -void -dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) -{ - /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { - tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); - assert(!err); - pthread_join(tda->tda_dvr_thread, NULL); - close(tda->tda_dvr_pipe.rd); - close(tda->tda_dvr_pipe.wr); - tda->tda_dvr_pipe.rd = -1; - tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); - } -} - void dvb_adapter_stop ( th_dvb_adapter_t *tda ) { @@ -746,8 +730,18 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) tda->tda_fe_fd = -1; } - dvb_adapter_stop_dvr(tda); - + /* Stop DVR thread */ + if (tda->tda_dvr_pipe.rd != -1) { + tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); + int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); + assert(!err); + pthread_join(tda->tda_dvr_thread, NULL); + close(tda->tda_dvr_pipe.rd); + close(tda->tda_dvr_pipe.wr); + tda->tda_dvr_pipe.rd = -1; + tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); + } + dvb_adapter_notify(tda); } diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 779e79b3..73a8cd0a 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -286,7 +286,6 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) } dvb_table_flush_all(tdmi); - dvb_adapter_stop_dvr(tda); assert(tdmi->tdmi_scan_queue == NULL); From 6dbec8e2e42851e217d52cb9017a8c4b8a8ba87f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 12:19:38 +0000 Subject: [PATCH 362/503] cwc: remove duplicate lock causing deadlock on cwc entry delete --- src/cwc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index e5c2046c..58c1e08b 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -2199,11 +2199,9 @@ cwc_service_start(service_t *t) static void cwc_destroy(cwc_t *cwc) { - pthread_mutex_lock(&cwc_mutex); TAILQ_REMOVE(&cwcs, cwc, cwc_link); cwc->cwc_running = 0; pthread_cond_signal(&cwc->cwc_cond); - pthread_mutex_unlock(&cwc_mutex); } From b3c6fc94599e1719ab3e8fb9fa043b77b1ffeb82 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 12:52:20 +0000 Subject: [PATCH 363/503] tableeditor: ensure all reload operations are properly processed --- src/webui/static/app/tableeditor.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/webui/static/app/tableeditor.js b/src/webui/static/app/tableeditor.js index dc04d1af..c36498d8 100644 --- a/src/webui/static/app/tableeditor.js +++ b/src/webui/static/app/tableeditor.js @@ -13,13 +13,13 @@ tvheadend.tableEditor = function(title, dtable, cm, rec, plugins, store, op : "get" } }); - - tvheadend.comet.on(dtable, function(m){ - if (m.reload) - store.reload(); - }); } + tvheadend.comet.on(dtable, function(m){ + if (m.reload) + store.reload(); + }); + function addRecord() { Ext.Ajax.request({ url : "tablemgr", From b7c5d271918d370f2687f330a3042c5d716de602 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 19:27:48 +0000 Subject: [PATCH 364/503] timeshift: improve rdwr locking to try and reduce stutters. --- src/timeshift/timeshift_reader.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 1750a137..d6b11941 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -346,7 +346,9 @@ static int _timeshift_read if (r == sizeof(size_t) || *cur_off > (*cur_file)->size) { close(*fd); *fd = -1; + pthread_mutex_lock(&ts->rdwr_mutex); *cur_file = timeshift_filemgr_next(*cur_file, NULL, 0); + pthread_mutex_unlock(&ts->rdwr_mutex); *cur_off = 0; // reset *wait = 0; @@ -631,9 +633,6 @@ void *timeshift_reader ( void *p ) continue; } - /* File processing lock */ - pthread_mutex_lock(&ts->rdwr_mutex); - /* Calculate delivery time */ deliver = (now - play_time) + TIMESHIFT_PLAY_BUF; deliver = (deliver * cur_speed) / 100; @@ -655,8 +654,10 @@ void *timeshift_reader ( void *p ) tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to %"PRId64" from %"PRId64, ts->id, req_time, last_time); /* Find */ + pthread_mutex_lock(&ts->rdwr_mutex); end = _timeshift_skip(ts, req_time, last_time, cur_file, &tsf, &tsi); + pthread_mutex_unlock(&ts->rdwr_mutex); if (tsi) tvhlog(LOG_DEBUG, "timeshift", "ts %d skip found pkt @ %"PRId64, ts->id, tsi->time); @@ -676,7 +677,6 @@ void *timeshift_reader ( void *p ) /* Find packet */ if (_timeshift_read(ts, &cur_file, &cur_off, &fd, &sm, &wait) == -1) { - pthread_mutex_unlock(&ts->rdwr_mutex); pthread_mutex_unlock(&ts->state_mutex); break; } @@ -776,10 +776,12 @@ void *timeshift_reader ( void *p ) /* Flush unwanted */ } else if (ts->ondemand && cur_file) { + pthread_mutex_lock(&ts->rdwr_mutex); + timeshift_writer_flush(ts); timeshift_filemgr_flush(ts, cur_file); + pthread_mutex_unlock(&ts->rdwr_mutex); } - pthread_mutex_unlock(&ts->rdwr_mutex); pthread_mutex_unlock(&ts->state_mutex); } From bbc215f4767912f36a29952e4df0ab445c71e2d2 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 19:28:52 +0000 Subject: [PATCH 365/503] timeshift: increase play out buffer to reduce stutter due to disk IO. --- src/timeshift/private.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timeshift/private.h b/src/timeshift/private.h index f462b91b..0a7f5b86 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -19,8 +19,8 @@ #ifndef __TVH_TIMESHIFT_PRIVATE_H__ #define __TVH_TIMESHIFT_PRIVATE_H__ -#define TIMESHIFT_PLAY_BUF 500000 // us to buffer in TX -#define TIMESHIFT_FILE_PERIOD 60 // number of secs in each buffer file +#define TIMESHIFT_PLAY_BUF 2000000 // us to buffer in TX +#define TIMESHIFT_FILE_PERIOD 60 // number of secs in each buffer file /** * Indexes of import data in the stream From a0e5b07e624492852402f2997f7c84e61b34fb98 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 2 Feb 2013 20:10:35 +0000 Subject: [PATCH 366/503] docs: update documentation about icon caching. --- docs/html/config_misc.html | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/docs/html/config_misc.html b/docs/html/config_misc.html index 5f11cf7b..c5709839 100644 --- a/docs/html/config_misc.html +++ b/docs/html/config_misc.html @@ -19,20 +19,32 @@ Select the path to use for DVB scan configuration files. Typically dvb-apps stores these in /usr/share/dvb/. Leave blank to use TVH's internal file set. + -
Cache channel icons: -
- Enable the caching of channel icons. This will cause TVH to download channel - icons locally, and then when requested via HTSP clients the URL for the icon - (or logo) will be retrieved from the TVH web server rather than fetched each - time by the HTSP client. - This REQUIRES the TVH server IP address field to also be populated +

+ Icon caching - this will cache any channel icons or other images (such as + EPG metadata). These will then be served from the local webserver, this + can be useful for multi-client systems and generally to reduce hits on + upstream providers. +

-
TVH Server IP Address: +
+ +
Enabled
- Enter the IP address of the TVH server used for HTSP clients. This permits the - above cache channel icons to work (TVH Clients are directed to the IP address - entered here to retrieve channel icons from) + Select whether or not to enable caching. Note: even with this disabled + you can still specify local (file://) icons and these will be served by + the built-in webserver. + +
Re-fetch period (hours) +
+ How frequently the upstream provider is checked for changes. + +
Re-try period (hours) +
+ How frequently it will re-try fetching an image that has failed to be + fetched. +
From a582fe7c7b4bd1df5fe160e7c8d941e4d15e39cb Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Feb 2013 13:22:21 +0000 Subject: [PATCH 367/503] Correct some mistkaes picked up by static analysis. Thanks to seo for pointing these out. --- src/cwc.c | 3 ++- src/dvb/dvb_adapter.c | 1 + src/main.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cwc.c b/src/cwc.c index 58c1e08b..3c0b5ae9 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1857,7 +1857,8 @@ cwc_emm_cryptoworks(cwc_t *cwc, uint8_t *data, int len) cwc_send_msg(cwc, composed, elen + 12, 0, 1); free(composed); free(tmp); - } + } else if (tmp) + free(tmp); cwc->cwc_cryptoworks_emm.shared_emm = NULL; cwc->cwc_cryptoworks_emm.shared_len = 0; } diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index e4b30c51..861c1117 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -520,6 +520,7 @@ tda_add(int adapter_num) dirp = opendir(path); if (!dirp) return; + closedir(dirp); /* Check each frontend */ // Note: this algo will fail if there are really exotic variations diff --git a/src/main.c b/src/main.c index 40261213..61cebd72 100644 --- a/src/main.c +++ b/src/main.c @@ -283,6 +283,7 @@ show_usage } tok = strtok(NULL, "\n"); } + free(desc); } } printf("\n"); From cedd06468dbcdf3e53a448788a077c437b4c835d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Feb 2013 13:50:46 +0000 Subject: [PATCH 368/503] dvb: ensure that we only create radio/tv services from SDT --- src/dvb/dvb_tables.c | 13 +++++++------ src/service.c | 38 ++++++++++++++++++++++++-------------- src/service.h | 4 ++++ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index f3ddb560..186af745 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -387,12 +387,6 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(dllen > len) break; - if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) { - len -= dllen; - ptr += dllen; - continue; - } - stype = 0; chname = NULL; *crid = 0; @@ -436,7 +430,14 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } len -= dlen; ptr += dlen; dllen -= dlen; } + + if (!servicetype_is_tv(stype) && + !servicetype_is_radio(stype)) + continue; + if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) + continue; + if(t->s_servicetype != stype || t->s_scrambled != free_ca_mode) { t->s_servicetype = stype; diff --git a/src/service.c b/src/service.c index a22eec77..2bf84aa2 100644 --- a/src/service.c +++ b/src/service.c @@ -777,30 +777,40 @@ service_servicetype_txt(service_t *t) * */ int -service_is_tv(service_t *t) +servicetype_is_tv(int servicetype) { return - t->s_servicetype == ST_SDTV || - t->s_servicetype == ST_HDTV || - t->s_servicetype == ST_EX_HDTV || - t->s_servicetype == ST_EX_SDTV || - t->s_servicetype == ST_EP_HDTV || - t->s_servicetype == ST_ET_HDTV || - t->s_servicetype == ST_DN_SDTV || - t->s_servicetype == ST_DN_HDTV || - t->s_servicetype == ST_SK_SDTV || - t->s_servicetype == ST_NE_SDTV || - t->s_servicetype == ST_AC_SDTV || - t->s_servicetype == ST_AC_HDTV; + servicetype == ST_SDTV || + servicetype == ST_HDTV || + servicetype == ST_EX_HDTV || + servicetype == ST_EX_SDTV || + servicetype == ST_EP_HDTV || + servicetype == ST_ET_HDTV || + servicetype == ST_DN_SDTV || + servicetype == ST_DN_HDTV || + servicetype == ST_SK_SDTV || + servicetype == ST_NE_SDTV || + servicetype == ST_AC_SDTV || + servicetype == ST_AC_HDTV; +} +int +service_is_tv(service_t *t) +{ + return servicetype_is_tv(t->s_servicetype); } /** * */ int +servicetype_is_radio(int servicetype) +{ + return servicetype == ST_RADIO; +} +int service_is_radio(service_t *t) { - return t->s_servicetype == ST_RADIO; + return servicetype_is_radio(t->s_servicetype); } /** diff --git a/src/service.h b/src/service.h index 05321cde..d2f8b03b 100644 --- a/src/service.h +++ b/src/service.h @@ -551,6 +551,10 @@ int service_is_tv(service_t *t); int service_is_radio(service_t *t); +int servicetype_is_tv(int st); + +int servicetype_is_radio(int st); + void service_destroy(service_t *t); void service_remove_subscriber(service_t *t, struct th_subscription *s, From ce672631dc3158c8fadf1c4449e1b83f33efbe6c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Feb 2013 19:22:48 +0000 Subject: [PATCH 369/503] dvr: remove bad LIST_REMOVE() call if entry has no channel --- src/dvr/dvr_db.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index ee020d2e..d9cfe026 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -457,7 +457,8 @@ dvr_entry_remove(dvr_entry_t *de) gtimer_disarm(&de->de_timer); - LIST_REMOVE(de, de_channel_link); + if (de->de_channel) + LIST_REMOVE(de, de_channel_link); LIST_REMOVE(de, de_global_link); de->de_channel = NULL; free(de->de_channel_name); From e50408ee0ba522053a21639188e747ff566b119b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Feb 2013 20:05:36 +0000 Subject: [PATCH 370/503] Fix #1588 - epg: forgot to check string bounds for epggrab channel name --- src/epggrab/channel.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/epggrab/channel.c b/src/epggrab/channel.c index db73b288..3fe0381c 100644 --- a/src/epggrab/channel.c +++ b/src/epggrab/channel.c @@ -185,7 +185,7 @@ epggrab_channel_t *epggrab_channel_find htsmsg_t *epggrab_channel_list ( void ) { - char name[100]; + char name[500]; epggrab_module_t *mod; epggrab_channel_t *ec; htsmsg_t *e, *m; @@ -198,9 +198,10 @@ htsmsg_t *epggrab_channel_list ( void ) htsmsg_add_str(e, "id", ec->id); if (ec->name) htsmsg_add_str(e, "name", ec->name); - sprintf(name, "%s|%s", mod->id, ec->id); + snprintf(name, sizeof(name), "%s|%s", mod->id, ec->id); htsmsg_add_str(e, "mod-id", name); - sprintf(name, "%s: %s (%s)", mod->name, ec->name, ec->id); + snprintf(name, sizeof(name), "%s: %s (%s)", + mod->name, ec->name, ec->id); htsmsg_add_str(e, "mod-name", name); htsmsg_add_msg(m, NULL, e); } From 5dd58269210a8c6e105510e2bcb65bafd04ea64f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Feb 2013 20:10:21 +0000 Subject: [PATCH 371/503] Fix #1589 - only remove "unsafe" characters if configured to do so. --- src/dvr/dvr_rec.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 1471ecc6..c48eafff 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -126,10 +126,17 @@ cleanupfilename(char *s, int dvr_flags) { int i, len = strlen(s); for(i = 0; i < len; i++) { - if((dvr_flags & DVR_WHITESPACE_IN_TITLE) && (s[i] == ' ' || s[i] == '\t')) + + if(s[i] == '/') + s[i] = '-'; + + else if((dvr_flags & DVR_WHITESPACE_IN_TITLE) && + (s[i] == ' ' || s[i] == '\t')) s[i] = '-'; - if((s[i] < 32) || (s[i] > 122) || (strchr("/:\\<>|*?'\"", s[i]) != NULL)) + else if((dvr_flags & DVR_CLEAN_TITLE) && + ((s[i] < 32) || (s[i] > 122) || + (strchr("/:\\<>|*?'\"", s[i]) != NULL))) s[i] = '-'; } } From 8b617c893ef87ba638882fb0ddc99bbc24469c33 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 11:04:34 +0000 Subject: [PATCH 372/503] logging: add option to log to separate file, useful for debug. This can be simpler than using log to syslog OR log to console for reporting problems. --- src/main.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 61cebd72..c7e23255 100644 --- a/src/main.c +++ b/src/main.c @@ -149,6 +149,8 @@ static int log_decorate; static LIST_HEAD(, gtimer) gtimers; static int log_debug_to_syslog; static int log_debug_to_console; +static int log_debug_to_path; +static char* log_path; static void handle_sigpipe(int x) @@ -347,6 +349,8 @@ main(int argc, char **argv) log_decorate = isatty(2); log_debug_to_syslog = 0; log_debug_to_console = 0; + log_debug_to_path = 0; + log_path = NULL; tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; @@ -408,6 +412,7 @@ main(int argc, char **argv) { 'd', "debug", "Enable all debug", OPT_BOOL, &opt_debug }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, + { 'l', "log", "Log to file", OPT_STR, &log_path }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, #if ENABLE_LINUXDVB { 'R', "dvbraw", "Use rawts file to create virtual adapter", @@ -459,7 +464,9 @@ main(int argc, char **argv) /* Additional cmdline processing */ log_debug_to_console = opt_debug; log_debug_to_syslog = opt_syslog; + log_debug_to_path = opt_debug; tvheadend_webui_debug = opt_debug || opt_uidebug; + tvhlog(LOG_INFO, "START", "initialising"); #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; @@ -707,6 +714,7 @@ tvhlogv(int notify, int severity, const char *subsys, const char *fmt, int l; struct tm tm; time_t now; + static int log_path_fail = 0; l = snprintf(buf, sizeof(buf), "%s: ", subsys); @@ -751,7 +759,24 @@ tvhlogv(int notify, int severity, const char *subsys, const char *fmt, } else { sgroff = "\033[0m"; } - fprintf(stderr, "%s%s [%s]:%s%s\n", sgr, t, leveltxt, buf, sgroff); + fprintf(stderr, "%s%s [%7s]:%s%s\n", sgr, t, leveltxt, buf, sgroff); + } + + /** + * Write to file + */ + if (log_path && (log_debug_to_path || severity < LOG_DEBUG)) { + const char *leveltxt = logtxtmeta[severity][0]; + FILE *fp = fopen(log_path, "a"); + if (fp) { + log_path_fail = 0; + fprintf(fp, "%s [%7s]:%s\n", t, leveltxt, buf); + fclose(fp); + } else { + if (!log_path_fail) + syslog(LOG_WARNING, "failed to write log file %s", log_path); + log_path_fail = 1; + } } } From 1726565e20df2e2369586741672709d42d0976c1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 11:05:35 +0000 Subject: [PATCH 373/503] access: add new option to override all ACL This can be quite useful for debug/testing, especially when using configuration provided by a user (saves deleting ACL files). --- src/access.c | 16 ++++++++++++++-- src/access.h | 2 +- src/main.c | 5 ++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/access.c b/src/access.c index 44f88108..25ee2948 100644 --- a/src/access.c +++ b/src/access.c @@ -43,6 +43,8 @@ struct access_ticket_queue access_tickets; const char *superuser_username; const char *superuser_password; +static int access_noacl; + /** * */ @@ -229,6 +231,9 @@ access_verify(const char *username, const char *password, uint32_t bits = 0; access_entry_t *ae; + if (access_noacl) + return 0; + if(username != NULL && superuser_username != NULL && password != NULL && superuser_password != NULL && !strcmp(username, superuser_username) && @@ -274,6 +279,9 @@ access_get_hashed(const char *username, const uint8_t digest[20], uint32_t r = 0; int match = 0; + if(access_noacl) + return 0xffffffff; + if(superuser_username != NULL && superuser_password != NULL) { SHA1_Init(&shactx); @@ -696,7 +704,7 @@ static const dtable_class_t access_dtc = { * */ void -access_init(int createdefault) +access_init(int createdefault, int noacl) { dtable_t *dt; htsmsg_t *r, *m; @@ -709,6 +717,10 @@ access_init(int createdefault) struct timeval tv; } randseed; + access_noacl = noacl; + if (noacl) + tvhlog(LOG_WARNING, "access", "Access control checking disabled"); + randseed.pid = getpid(); gettimeofday(&randseed.tv, NULL); RAND_seed(&randseed, sizeof(randseed)); @@ -742,7 +754,7 @@ access_init(int createdefault) dtable_record_store(dt, ae->ae_id, r); htsmsg_destroy(r); - tvhlog(LOG_WARNING, "accesscontrol", + tvhlog(LOG_WARNING, "access", "Created default wide open access controle entry"); } diff --git a/src/access.h b/src/access.h index 2ecc9873..2edb0ab9 100644 --- a/src/access.h +++ b/src/access.h @@ -110,6 +110,6 @@ uint32_t access_get_by_addr(struct sockaddr *src); /** * */ -void access_init(int createdefault); +void access_init(int createdefault, int noacl); #endif /* ACCESS_H_ */ diff --git a/src/main.c b/src/main.c index c7e23255..be27a602 100644 --- a/src/main.c +++ b/src/main.c @@ -365,6 +365,7 @@ main(int argc, char **argv) opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, + opt_noacl = 0, opt_ipv6 = 0; const char *opt_config = NULL, *opt_user = NULL, @@ -414,6 +415,8 @@ main(int argc, char **argv) { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, { 'l', "log", "Log to file", OPT_STR, &log_path }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, + { 0, "noacl", "Disable all access control checks", + OPT_BOOL, &opt_noacl }, #if ENABLE_LINUXDVB { 'R', "dvbraw", "Use rawts file to create virtual adapter", OPT_STR, &opt_dvb_raw }, @@ -598,7 +601,7 @@ main(int argc, char **argv) subscription_init(); - access_init(opt_firstrun); + access_init(opt_firstrun, opt_noacl); #if ENABLE_LINUXDVB muxes_init(); From 5c1359b66366fd03ae8cebcc9bfb6491884e69d8 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 12:06:46 +0000 Subject: [PATCH 374/503] dvb: add a bit more debug to NIT processing --- src/dvb/dvb_tables.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 186af745..1a8fee98 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -673,7 +673,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq * 100; - TRACE("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); + TRACE("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + @@ -717,7 +717,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]); dmc.dmc_fe_params.frequency = freq * 10; - TRACE("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); + TRACE("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); if(!freq) return -1; @@ -805,7 +805,7 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq; - TRACE("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); + TRACE("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); dmc.dmc_fe_params.u.ofdm.bandwidth = bandwidth_tab[(ptr[4] & 0xe0) >> 5]; dmc.dmc_fe_params.u.ofdm.constellation=constellation_tab[(ptr[5] & 0xc0) >> 6]; dmc.dmc_fe_params.u.ofdm.hierarchy_information=hierarchy_info_tab[(ptr[5] & 0x38) >> 3]; @@ -906,6 +906,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, ptr += dlen + 2; llen -= dlen + 2; } + TRACE("nit", "network %d/%s", network_id, netname); if (llen) return -1; @@ -920,8 +921,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, onid = ( ptr[2] << 8) | ptr[3]; llen = ((ptr[4] & 0xf) << 8) | ptr[5]; - TRACE("nit", "netw %d/%s onid %04X tsid %04X", - network_id, netname, onid, tsid); + TRACE("nit", " onid %04X tsid %04X", onid, tsid); ptr += 6; len -= llen + 6; From ac36914b986dfdab80d1243344f1b9a5b97a6247 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 14:11:07 +0000 Subject: [PATCH 375/503] Fix #1596 - some version of git don't like describe in subdir --- support/version | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/version b/support/version index 7d5c40ea..68d4fc85 100755 --- a/support/version +++ b/support/version @@ -8,11 +8,11 @@ FILE=$1 # Calculate version if [ -d ".git" ]; then - VER=$(cd $(dirname $0); git describe --dirty --match "v*" 2> /dev/null) + VER=$(cd $(dirname $0)/..; git describe --dirty --match "v*" 2> /dev/null) if [ $? -ne 0 ]; then # Git describe failed, maybe "--dirty" option is not available # Adding "-unknown" postfix to mark this situation - VER=$(cd $(dirname $0); git describe --match "v*" 2> /dev/null)-unknown + VER=$(cd $(dirname $0)/..; git describe --match "v*" 2> /dev/null)-unknown fi VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") else From fea99ba6497090c17b7fe407ec5a64133f769208 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 14:12:16 +0000 Subject: [PATCH 376/503] Fix #1595 - fix typo in IPV6 check for debian init script --- debian/tvheadend.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/tvheadend.init b/debian/tvheadend.init index 1fe83698..d0fb7dd7 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -33,7 +33,7 @@ ARGS="-f" [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" -[ "$TVH_IPV6" == "1" ] && ARGS="$ARGS -6" +[ "$TVH_IPV6" = "1" ] && ARGS="$ARGS -6" [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" From b359144b7cf975ceeaab724de00a57633dc522f0 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 15:19:47 +0000 Subject: [PATCH 377/503] Fix #1591 - timeshift: fix mutex deadlock affecting on-demand mode. Note: there is still an issue I've realised in that writer_flush() could potentially result in a single packet being written out of order as two threads have the potential to write. This isn't fatal and is probably rare enough to ignore at this stage. --- src/timeshift/timeshift_reader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index d6b11941..2e5d1d2f 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -777,7 +777,6 @@ void *timeshift_reader ( void *p ) /* Flush unwanted */ } else if (ts->ondemand && cur_file) { pthread_mutex_lock(&ts->rdwr_mutex); - timeshift_writer_flush(ts); timeshift_filemgr_flush(ts, cur_file); pthread_mutex_unlock(&ts->rdwr_mutex); } From 53986293940c326fdc4d1ef748c115b2f494835d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 6 Feb 2013 03:10:31 -0500 Subject: [PATCH 378/503] Add support for adding ATSC muxes manually --- src/dvb/dvb_multiplex.c | 2 +- src/webui/static/app/dvb.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 7fd5e2c2..986765ae 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1169,7 +1169,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, break; case FE_ATSC: - dmc.dmc_fe_params.frequency = freq; + dmc.dmc_fe_params.frequency = freq * 1000; if(!val2str(constellation, qamtab)) return "Invalid VSB constellation"; diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index eeca7090..4d423d3f 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -792,6 +792,36 @@ tvheadend.addMuxManually = function(adapterData, satConfStore) { var items = []; switch (adapterData.deliverySystem) { + case 'ATSC': + items.push(new Ext.form.NumberField({ + fieldLabel : 'Frequency (kHz)', + name : 'frequency', + allowNegative : false, + allowBlank : false, + minValue : adapterData.freqMin, + maxValue : adapterData.freqMax + })); + + items.push(new Ext.form.ComboBox({ + fieldLabel : 'Modulation', + name : 'constellation', + hiddenName : 'constellationID', + editable : false, + allowBlank : false, + displayField : 'title', + valueField : 'id', + mode : 'remote', + triggerAction : 'all', + store : new Ext.data.JsonStore({ + root : 'entries', + fields : [ 'title', 'id' ], + url : 'dvb/feopts/constellations/' + adId + }) + })); + + break; + + case 'DVB-T': items.push(new Ext.form.NumberField({ From 0fd2aaf66fa4a630d5ad3a5311869f389a8caec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Wed, 6 Feb 2013 03:22:06 -0500 Subject: [PATCH 379/503] ATSC: In VCT, if tsid is 0, assume it's current mux --- src/dvb/dvb_tables.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 1a8fee98..2dbe5474 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -967,6 +967,7 @@ static int atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { + th_dvb_mux_instance_t *tdmi0 = tdmi; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; service_t *t; int numch; @@ -1000,15 +1001,18 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tsid = (ptr[22] << 8) | ptr[23]; onid = (ptr[24] << 8) | ptr[25]; - - /* Search all muxes on adapter */ - LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) - if(tdmi->tdmi_transport_stream_id == tsid && - tdmi->tdmi_network_id == onid); - break; - - if(tdmi == NULL) - continue; + + if(tsid == 0) { + tdmi = tdmi0; + } else { + /* Search all muxes on adapter */ + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { + if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid) + break; + } + if(tdmi == NULL) + continue; + } service_id = (ptr[24] << 8) | ptr[25]; if((t = dvb_service_find(tdmi, service_id, 0, NULL)) == NULL) From 53a9d6a4baa3eb9ee7415e46ee6b8612d962a343 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 6 Feb 2013 16:17:51 +0000 Subject: [PATCH 380/503] Fix #1574 - imagecache: add option to ignore all invalid SSL certificates --- src/imagecache.c | 37 ++++++++++++++++++++++++++-------- src/imagecache.h | 3 +++ src/webui/extjs.c | 3 +++ src/webui/static/app/config.js | 10 +++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/imagecache.c b/src/imagecache.c index 53198945..44bd6506 100644 --- a/src/imagecache.c +++ b/src/imagecache.c @@ -76,6 +76,7 @@ static void _imagecache_save ( imagecache_image_t *img ); uint32_t imagecache_enabled; uint32_t imagecache_ok_period; uint32_t imagecache_fail_period; +uint32_t imagecache_ignore_sslcert; static pthread_cond_t _imagecache_cond; static TAILQ_HEAD(, imagecache_image) _imagecache_queue; @@ -106,11 +107,12 @@ void imagecache_init ( void ) uint32_t id; /* Init vars */ - _imagecache_id = 0; + _imagecache_id = 0; #if ENABLE_IMAGECACHE - imagecache_enabled = 0; - imagecache_ok_period = 24 * 7; // weekly - imagecache_fail_period = 24; // daily + imagecache_enabled = 0; + imagecache_ok_period = 24 * 7; // weekly + imagecache_fail_period = 24; // daily + imagecache_ignore_sslcert = 0; #endif /* Create threads */ @@ -126,6 +128,7 @@ void imagecache_init ( void ) htsmsg_get_u32(m, "enabled", &imagecache_enabled); htsmsg_get_u32(m, "ok_period", &imagecache_ok_period); htsmsg_get_u32(m, "fail_period", &imagecache_fail_period); + htsmsg_get_u32(m, "ignore_sslcert", &imagecache_ignore_sslcert); htsmsg_destroy(m); } #endif @@ -173,9 +176,10 @@ void imagecache_init ( void ) void imagecache_save ( void ) { htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_u32(m, "enabled", imagecache_enabled); - htsmsg_add_u32(m, "ok_period", imagecache_ok_period); - htsmsg_add_u32(m, "fail_period", imagecache_fail_period); + htsmsg_add_u32(m, "enabled", imagecache_enabled); + htsmsg_add_u32(m, "ok_period", imagecache_ok_period); + htsmsg_add_u32(m, "fail_period", imagecache_fail_period); + htsmsg_add_u32(m, "ignore_sslcert", imagecache_ignore_sslcert); hts_settings_save(m, "imagecache/config"); } @@ -213,6 +217,17 @@ int imagecache_set_fail_period ( uint32_t p ) imagecache_fail_period = p; return 1; } + +/* + * Set ignore SSL cert + */ +int imagecache_set_ignore_sslcert ( uint32_t p ) +{ + if (p == imagecache_ignore_sslcert) + return 0; + imagecache_ignore_sslcert = p; + return 1; +} #endif /* @@ -406,7 +421,8 @@ static int _imagecache_fetch ( imagecache_image_t *img ) if (!(fp = fopen(tmp, "wb"))) return 1; - /* Fetch file */ + /* Build command */ + pthread_mutex_lock(&imagecache_mutex); tvhlog(LOG_DEBUG, "imagecache", "fetch %s", img->url); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, img->url); @@ -415,6 +431,11 @@ static int _imagecache_fetch ( imagecache_image_t *img ) curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + if (imagecache_ignore_sslcert) + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + pthread_mutex_unlock(&imagecache_mutex); + + /* Fetch */ res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(fp); diff --git a/src/imagecache.h b/src/imagecache.h index 5776f46f..c96b3186 100644 --- a/src/imagecache.h +++ b/src/imagecache.h @@ -24,6 +24,7 @@ extern uint32_t imagecache_enabled; extern uint32_t imagecache_ok_period; extern uint32_t imagecache_fail_period; +extern uint32_t imagecache_ignore_sslcert; extern pthread_mutex_t imagecache_mutex; @@ -37,6 +38,8 @@ int imagecache_set_ok_period ( uint32_t e ) __attribute__((warn_unused_result)); int imagecache_set_fail_period ( uint32_t e ) __attribute__((warn_unused_result)); +int imagecache_set_ignore_sslcert ( uint32_t e ) + __attribute__((warn_unused_result)); // Note: will return 0 if invalid (must serve original URL) uint32_t imagecache_get_id ( const char *url ); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 8f1be797..c11ec87a 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1982,6 +1982,7 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(m, "imagecache_enabled", imagecache_enabled); htsmsg_add_u32(m, "imagecache_ok_period", imagecache_ok_period); htsmsg_add_u32(m, "imagecache_fail_period", imagecache_fail_period); + htsmsg_add_u32(m, "imagecache_ignore_sslcert", imagecache_ignore_sslcert); pthread_mutex_unlock(&imagecache_mutex); #endif @@ -2011,6 +2012,8 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) save |= imagecache_set_ok_period(atoi(str)); if ((str = http_arg_get(&hc->hc_req_args, "imagecache_fail_period"))) save |= imagecache_set_fail_period(atoi(str)); + str = http_arg_get(&hc->hc_req_args, "imagecache_ignore_sslcert"); + save |= imagecache_set_ignore_sslcert(!!str); if (save) imagecache_save(); pthread_mutex_unlock(&imagecache_mutex); diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index b71a4a5d..183b8eef 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -39,7 +39,7 @@ tvheadend.miscconf = function() { root : 'config' }, [ 'muxconfpath', 'language', 'imagecache_enabled', 'imagecache_ok_period', - 'imagecache_fail_period']); + 'imagecache_fail_period', 'imagecache_ignore_sslcert']); /* **************************************************************** * Form Fields @@ -93,12 +93,18 @@ tvheadend.miscconf = function() { fieldLabel: 'Re-try period (hours)', }); + var imagecacheIgnoreSSLCert = new Ext.form.Checkbox({ + name: 'imagecache_ignore_sslcert', + fieldLabel: 'Ignore invalid SSL certificate' + }); + var imagecachePanel = new Ext.form.FieldSet({ title: 'Image Caching', width: 700, autoHeight: true, collapsible: true, - items : [ imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod ] + items : [ imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod, + imagecacheIgnoreSSLCert ] }); if (tvheadend.capabilities.indexOf('imagecache') == -1) imagecachePanel.hide(); From efd2c28b890faee7e8ec8eecbaa5dcc018e1b13e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Feb 2013 15:44:05 +0000 Subject: [PATCH 381/503] support: fix configure args storage. --- support/configure.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/support/configure.inc b/support/configure.inc index 6b2dcece..ecb85da4 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -9,6 +9,8 @@ # Defaults # ########################################################################### +CONFIGURE_ARGS="$*" + # System setup [ -z "$PLATFORM" ] && PLATFORM=linux [ -z "$CPU" ] && CPU=generic @@ -460,11 +462,12 @@ function write_config CONFIG_MK=${ROOTDIR}/.config.mk cat > ${CONFIG_MK} < Date: Sat, 9 Feb 2013 12:48:57 +0000 Subject: [PATCH 382/503] main: fix possible NULL ptr on startup --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index be27a602..a9a50651 100644 --- a/src/main.c +++ b/src/main.c @@ -514,7 +514,7 @@ main(int argc, char **argv) gid_t gid; uid_t uid; struct group *grp = getgrnam(opt_group ?: "video"); - struct passwd *pw = getpwnam(opt_user) ?: NULL; + struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; FILE *pidfile = fopen(opt_pidpath, "w+"); if(grp != NULL) { From 79c134f9c401679b01d46b8857148c5457267224 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 9 Feb 2013 12:51:14 +0000 Subject: [PATCH 383/503] init: added delay option to init --- debian/tvheadend.default | 4 ++++ debian/tvheadend.init | 1 + debian/tvheadend.upstart | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/debian/tvheadend.default b/debian/tvheadend.default index 4617c07b..29b6dbef 100644 --- a/debian/tvheadend.default +++ b/debian/tvheadend.default @@ -47,6 +47,10 @@ TVH_HTSP_PORT="" # if set to 1 will output debug to syslog TVH_DEBUG=0 +# TVH_DELAY +# if set startup will be delayed N seconds to allow hardware init +TVH_DELAY="" + # TVH_ARGS # add any other arguments TVH_ARGS="" diff --git a/debian/tvheadend.init b/debian/tvheadend.init index d0fb7dd7..b4aea9cb 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -85,6 +85,7 @@ do_stop() case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + [ ! -z "$TVH_DELAY" ] && sleep $TVH_DELAY do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; diff --git a/debian/tvheadend.upstart b/debian/tvheadend.upstart index 9fe9baf7..a5d70404 100644 --- a/debian/tvheadend.upstart +++ b/debian/tvheadend.upstart @@ -22,11 +22,13 @@ script [ -z "$TVH_GROUP" ] || ARGS="$ARGS -g $TVH_GROUP" [ -z "$TVH_CONF_DIR" ] || ARGS="$ARGS -c $TVH_CONF_DIR" [ -z "$TVH_ADAPTERS" ] || ARGS="$ARGS -a $TVH_ADAPTERS" - [ "$TVH_IPV6" == "1" ] && ARGS="$ARGS -6" + [ "$TVH_IPV6" = "1" ] && ARGS="$ARGS -6" [ -z "$TVH_HTTP_PORT" ] || ARGS="$ARGS --http_port $TVH_HTTP_PORT" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" + [ ! -z "$TVH_DELAY" ] && sleep $TVH_DELAY + exec tvheadend $ARGS $TVH_ARGS end script From eaec34220b9c7960b80e7bb9481521f04985b1d9 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Sun, 10 Feb 2013 02:39:51 -0700 Subject: [PATCH 384/503] fix compile errors with gcc 4.1.x and 4.2.x - tested with gcc 4.1.2, 4.2.1, 4.4.6, 4.6.3 and 4.7.2 - disable gcc ignore pragma for "-Warray-bounds" if gcc version < 4.3 - when I took out the pragma tvheadend compiled fine in all the above compilers, but I assume some version of gcc is issuing a false-positive. - "-Warray-bounds" was added in gcc 4.3: http://gcc.gnu.org/gcc-4.3/changes.html - fixes typo in cmdline help for -C - clarify usage of -a in help description --- src/capmt.c | 6 ++++++ src/main.c | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index 56229604..f7c3f894 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -75,7 +75,13 @@ #define CW_DUMP(buf, len, format, ...) \ printf(format, __VA_ARGS__); int j; for (j = 0; j < len; ++j) printf("%02X ", buf[j]); printf("\n"); +#ifdef __GNUC__ +#include +#if __GNUC_PREREQ(4, 3) #pragma GCC diagnostic ignored "-Warray-bounds" +#endif +#endif + #define MAX_CA 4 #define MAX_INDEX 64 #define KEY_SIZE 8 diff --git a/src/main.c b/src/main.c index a9a50651..88c4bd55 100644 --- a/src/main.c +++ b/src/main.c @@ -388,14 +388,14 @@ main(int argc, char **argv) { 'u', "user", "Run as user", OPT_STR, &opt_user }, { 'g', "group", "Run as group", OPT_STR, &opt_group }, { 'p', "pid", "Alternate pid path", OPT_STR, &opt_pidpath }, - { 'C', "firstrun", "If no useraccount exist then create one with\n" + { 'C', "firstrun", "If no user account exists then create one with\n" "no username and no password. Use with care as\n" "it will allow world-wide administrative access\n" "to your Tvheadend installation until you edit\n" "the access-control from within the Tvheadend UI", OPT_BOOL, &opt_firstrun }, #if ENABLE_LINUXDVB - { 'a', "adapters", "Use only specified DVB adapters", + { 'a', "adapters", "Only use specified DVB adapters (comma separated)", OPT_STR, &opt_dvb_adapters }, #endif { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, @@ -474,13 +474,16 @@ main(int argc, char **argv) if (!opt_dvb_adapters) { adapter_mask = ~0; } else { - char *p, *r, *e; + char *p, *e; + char *r = NULL; + char *dvb_adapters = strdup(opt_dvb_adapters); adapter_mask = 0x0; - p = strtok_r((char*)opt_dvb_adapters, ",", &r); + p = strtok_r(dvb_adapters, ",", &r); while (p) { int a = strtol(p, &e, 10); if (*e != 0 || a < 0 || a > 31) { tvhlog(LOG_ERR, "START", "Invalid adapter number '%s'", p); + free(dvb_adapters); return 1; } adapter_mask |= (1 << a); @@ -488,8 +491,10 @@ main(int argc, char **argv) } if (!adapter_mask) { tvhlog(LOG_ERR, "START", "No adapters specified!"); + free(dvb_adapters); return 1; } + free(dvb_adapters); } #endif if (tvheadend_webroot) { From 5c4c611e59d05cc9d813a99a3d4079e449b2ac65 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 9 Feb 2013 23:22:11 +0000 Subject: [PATCH 385/503] timeshift: minor addition to atomics (check max size correctly). --- src/atomic.h | 12 ++++++++++++ src/timeshift/timeshift_filemgr.c | 2 +- src/timeshift/timeshift_reader.c | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/atomic.h b/src/atomic.h index 8627e64a..f4b8c991 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -35,3 +35,15 @@ atomic_add_u64(volatile uint64_t *ptr, uint64_t incr) { return __sync_fetch_and_add(ptr, incr); } + +static inline uint64_t +atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) +{ + return __sync_add_and_fetch(ptr, incr); +} + +static inline uint64_t +atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) +{ + return __sync_add_and_fetch(ptr, incr); +} diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 185d80a5..a0d1603a 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -226,7 +226,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Check size */ if (!timeshift_unlimited_size && - atomic_add_u64(×hift_total_size, 0) >= timeshift_max_size) { + atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); ts->full = 1; } diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 2e5d1d2f..70bd9d0a 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -356,7 +356,6 @@ static int _timeshift_read } else { streaming_message_t *ssm = _timeshift_find_sstart(*cur_file, (*sm)->sm_time); if (ssm && ssm->sm_data != ts->smt_start) { - printf("SENDING NEW SMT_START MESSAGE\n"); streaming_target_deliver2(ts->output, streaming_msg_clone(ssm)); if (ts->smt_start) streaming_start_unref(ts->smt_start); From b043cf3729a90efd6d1b5ec92c6f7893098ecd82 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 7 Feb 2013 14:26:23 +0000 Subject: [PATCH 386/503] tvhtime: start adding time processing support to TVH --- Makefile | 3 +- src/dvb/dvb_tables.c | 54 +++++++++++ src/tvhtime.c | 160 +++++++++++++++++++++++++++++++++ src/tvhtime.h | 34 +++++++ src/webui/extjs.c | 16 ++++ src/webui/static/app/config.js | 32 ++++++- 6 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 src/tvhtime.c create mode 100644 src/tvhtime.h 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 ] }); From 8f49909e9e5245782bbb14d6468f1c7684229f76 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 9 Feb 2013 23:18:28 +0000 Subject: [PATCH 387/503] Fix #1608 - atomic: add workaround for missing intrinsic atomic ops. --- configure | 5 +++++ src/atomic.h | 30 ++++++++++++++++++++++++------ src/main.c | 2 ++ src/tvheadend.h | 1 + 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 1b618cea..3d3ec68f 100755 --- a/configure +++ b/configure @@ -54,6 +54,11 @@ check_cc_option sse2 check_cc_snippet getloadavg '#include void test() { getloadavg(NULL,0); }' +check_cc_snippet atomic64 '#include +uint64_t test(uint64_t *ptr){ +return __sync_fetch_and_add(ptr, 1); +}' + # # Python # diff --git a/src/atomic.h b/src/atomic.h index f4b8c991..db68ca7e 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -33,13 +33,31 @@ atomic_exchange(volatile int *ptr, int new) static inline uint64_t atomic_add_u64(volatile uint64_t *ptr, uint64_t incr) { +#if ENABLE_ATOMIC64 return __sync_fetch_and_add(ptr, incr); -} - -static inline uint64_t -atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) -{ - return __sync_add_and_fetch(ptr, incr); +#else + uint64_t ret; + pthread_mutex_lock(&atomic_lock); + ret = *ptr; + *ptr += incr; + pthread_mutex_unlock(&atomic_lock); + return ret; +#endif +} + +static inline uint64_t +atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) +{ +#if ENABLE_ATOMIC64 + return __sync_add_and_fetch(ptr, incr); +#else + uint64_t ret; + pthread_mutex_lock(&atomic_lock); + *ptr += incr; + ret = *ptr; + pthread_mutex_unlock(&atomic_lock); + return ret; +#endif } static inline uint64_t diff --git a/src/main.c b/src/main.c index a9a50651..60c4cc1c 100644 --- a/src/main.c +++ b/src/main.c @@ -139,6 +139,7 @@ time_t dispatch_clock; pthread_mutex_t global_lock; pthread_mutex_t ffmpeg_lock; pthread_mutex_t fork_lock; +pthread_mutex_t atomic_lock; /* * Locals @@ -574,6 +575,7 @@ main(int argc, char **argv) pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); + pthread_mutex_init(&atomic_lock, NULL); pthread_mutex_lock(&global_lock); time(&dispatch_clock); diff --git a/src/tvheadend.h b/src/tvheadend.h index 6b0f3ab5..d2f2b4e5 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -63,6 +63,7 @@ static inline htsmsg_t *tvheadend_capabilities_list(int check) extern pthread_mutex_t global_lock; extern pthread_mutex_t ffmpeg_lock; extern pthread_mutex_t fork_lock; +extern pthread_mutex_t atomic_lock; extern int tvheadend_webui_port; extern int tvheadend_webui_debug; From 920a0a802cd7f85be41b78af81f652e4e9298478 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 11 Feb 2013 17:11:42 +0000 Subject: [PATCH 388/503] dvb: some minor corrections to nit scanning. --- src/dvb/dvb_multiplex.c | 5 ++++- src/dvb/dvb_tables.c | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 986765ae..a5ac0d4d 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -206,7 +206,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, tdmi->tdmi_network_id = onid; save = 1; } - if(network && strcmp(tdmi->tdmi_network ?: "", network)) { + if(network && *network && strcmp(tdmi->tdmi_network ?: "", network)) { free(tdmi->tdmi_network); tdmi->tdmi_network = strdup(network); save = 1; @@ -848,6 +848,9 @@ dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) { htsmsg_t *m; + if (!networkname || !*networkname) + return; + free(tdmi->tdmi_network); tdmi->tdmi_network = strdup(networkname); dvb_mux_save(tdmi); diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 18d6d7ad..db59f811 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -878,6 +878,11 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint16_t network_id = (ptr[0] << 8) | ptr[1]; netname[0] = '\0'; + TRACE("nit", "tableid 0x%02x", tableid); +#if TDT_TRACE + hexdump("nit", ptr, len); +#endif + /* Check NID */ if(tdmi->tdmi_adapter->tda_nitoid && tdmi->tdmi_adapter->tda_nitoid != network_id) @@ -898,10 +903,14 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dtag = ptr[0]; dlen = ptr[1]; + TRACE("nit", "dtag %02X dlen %d", dtag, dlen); + switch(dtag) { case DVB_DESC_NETWORK_NAME: if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL)) return -1; + if(tableid == 0x40 && (!tdmi->tdmi_network || *tdmi->tdmi_network == '\0')) + dvb_mux_set_networkname(tdmi, netname); break; } @@ -934,6 +943,8 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dtag = ptr[0]; dlen = ptr[1]; + TRACE("nit", " dtag %02X dlen %d", dtag, dlen); + switch(dtag) { case DVB_DESC_SAT: if(tdmi->tdmi_adapter->tda_type == FE_QPSK) @@ -1133,14 +1144,7 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) { /* Network Information Table */ - int table; - - if(tdmi->tdmi_adapter->tda_nitoid) { - table = 0x41; - } else { - table = 0x40; - } - tdt_add(tdmi, table, 0xff, dvb_nit_callback, NULL, "nit", + tdt_add(tdmi, 0, 0, dvb_nit_callback, NULL, "nit", TDT_QUICKREQ | TDT_CRC, 0x10); /* Service Descriptor Table and Bouqeut Allocation Table */ From 8b8876d768a693d3a6a8dbd0939268605e509407 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 10:31:59 +0000 Subject: [PATCH 389/503] Fix #1605 - timeshift: mark file space as free at earliest opportunity. Previously the space was only marked as free when the file was physically removed (which happens async) which resulted in the buffer immediately being seen as full. --- src/timeshift/timeshift_filemgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index a0d1603a..19bc6b43 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -75,7 +75,6 @@ static void* timeshift_reaper_callback ( void *p ) if (errno != ENOTEMPTY) tvhlog(LOG_ERR, "timeshift", "failed to remove %s [e=%s]", dpath, strerror(errno)); - atomic_add_u64(×hift_total_size, -tsf->size); /* Free memory */ while ((ti = TAILQ_FIRST(&tsf->iframes))) { @@ -163,6 +162,7 @@ void timeshift_filemgr_remove if (tsf->fd != -1) close(tsf->fd); TAILQ_REMOVE(&ts->files, tsf, link); + atomic_add_u64(×hift_total_size, -tsf->size); timeshift_reaper_remove(tsf); } From ec8a0dfd1ef6eafddc926a0b6b8a72505d667160 Mon Sep 17 00:00:00 2001 From: Vuolter Date: Sun, 10 Feb 2013 00:59:08 +0100 Subject: [PATCH 390/503] Cosmetics: epg genre --- src/epg.c | 254 +++++++++++++++++++++++++++--------------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/src/epg.c b/src/epg.c index 6984fa0e..697dae1f 100644 --- a/src/epg.c +++ b/src/epg.c @@ -1885,153 +1885,153 @@ epg_broadcast_t *epg_broadcast_deserialize static const char *_epg_genre_names[16][16] = { { "" }, { - "Movie/Drama", - "detective/thriller", - "adventure/western/war", - "science fiction/fantasy/horror", - "comedy", - "soap/melodrama/folkloric", - "romance", - "serious/classical/religious/historical movie/drama", - "adult movie/drama", - "adult movie/drama", - "adult movie/drama", - "adult movie/drama", - "adult movie/drama", - "adult movie/drama", + "Movie / Drama", + "Detective / Thriller", + "Adventure / Western / War", + "Science fiction / Fantasy / Horror", + "Comedy", + "Soap / Melodrama / Folkloric", + "Romance", + "Serious / Classical / Religious / Historical movie / Drama", + "Adult movie / Drama", + "Adult movie / Drama", + "Adult movie / Drama", + "Adult movie / Drama", + "Adult movie / Drama", + "Adult movie / Drama", }, { - "News/Current affairs", - "news/weather report", - "news magazine", - "documentary", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", - "discussion/interview/debate", + "News / Current affairs", + "News / Weather report", + "News magazine", + "Documentary", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", + "Discussion / Interview / Debate", }, { - "Show/Game show", - "game show/quiz/contest", - "variety show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", - "talk show", + "Show / Game show", + "Game show / Quiz / Contest", + "Variety show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", + "Talk show", }, { "Sports", - "special events (Olympic Games, World Cup, etc.)", - "sports magazines", - "football/soccer", - "tennis/squash", - "team sports (excluding football)", - "athletics", - "motor sport", - "water sport", + "Special events (Olympic Games, World Cup, etc.)", + "Sports magazines", + "Football / Soccer", + "Tennis / Squash", + "Team sports (excluding football)", + "Athletics", + "Motor sport", + "Water sport", }, { - "Children's/Youth programmes", - "pre-school children's programmes", - "entertainment programmes for 6 to14", - "entertainment programmes for 10 to 16", - "informational/educational/school programmes", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", - "cartoons/puppets", + "Children's / Youth programmes", + "Pre-school children's programmes", + "Entertainment programmes for 6 to 14", + "Entertainment programmes for 10 to 16", + "Informational / Educational / School programmes", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", + "Cartoons / Puppets", }, { - "Music/Ballet/Dance", - "rock/pop", - "serious music/classical music", - "folk/traditional music", - "jazz", - "musical/opera", - "musical/opera", - "musical/opera", - "musical/opera", - "musical/opera", - "musical/opera", - "musical/opera", - "musical/opera", + "Music / Ballet / Dance", + "Rock / Pop", + "Serious music / Classical music", + "Folk / Traditional music", + "Jazz", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", + "Musical / Opera", }, { - "Arts/Culture (without music)", - "performing arts", - "fine arts", - "religion", - "popular culture/traditional arts", - "literature", - "film/cinema", - "experimental film/video", - "broadcasting/press", + "Arts / Culture (without music)", + "Performing arts", + "Fine arts", + "Religion", + "Popular culture / Traditional arts", + "Literature", + "Film / Cinema", + "Experimental film / Video", + "Broadcasting / Press", }, { - "Social/Political issues/Economics", - "magazines/reports/documentary", - "economics/social advisory", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", - "remarkable people", + "Social / Political issues / Economics", + "Magazines / Reports / Documentary", + "Economics / Social advisory", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", + "Remarkable people", }, { - "Education/Science/Factual topics", - "nature/animals/environment", - "technology/natural sciences", - "medicine/physiology/psychology", - "foreign countries/expeditions", - "social/spiritual sciences", - "further education", - "languages", - "languages", - "languages", - "languages", - "languages", - "languages", - "languages", + "Education / Science / Factual topics", + "Nature / Animals / Environment", + "Technology / Natural sciences", + "Medicine / Physiology / Psychology", + "Foreign countries / Expeditions", + "Social / Spiritual sciences", + "Further education", + "Languages", + "Languages", + "Languages", + "Languages", + "Languages", + "Languages", + "Languages", }, { "Leisure hobbies", - "tourism/travel", - "handicraft", - "motoring", - "fitness and health", - "cooking", - "advertisement/shopping", - "gardening", - "gardening", - "gardening", - "gardening", - "gardening", - "gardening", - "gardening", + "Tourism / Travel", + "Handicraft", + "Motoring", + "Fitness and health", + "Cooking", + "Advertisement / Shopping", + "Gardening", + "Gardening", + "Gardening", + "Gardening", + "Gardening", + "Gardening", + "Gardening", } }; From 6cbf00e9f5935cfa1c0607ff513ab9330c242f57 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 11:24:04 +0000 Subject: [PATCH 391/503] atomic: fix previous merge mistake --- src/atomic.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/atomic.h b/src/atomic.h index db68ca7e..e9ef2648 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -59,9 +59,3 @@ atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) return ret; #endif } - -static inline uint64_t -atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) -{ - return __sync_add_and_fetch(ptr, incr); -} From 3a5c84af103baf1d4969088bbae44723b754616d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 11:29:00 +0000 Subject: [PATCH 392/503] comsetics: some updates to --help output. Changes are based on suggestions from Vuolter --- src/main.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index c5c8b8e5..33db93b7 100644 --- a/src/main.c +++ b/src/main.c @@ -257,8 +257,7 @@ show_usage { int i; char buf[256]; - printf("Usage :- %s [options]\n\n", argv0); - printf("Options\n"); + printf("Usage: %s [OPTIONS]\n", argv0); for (i = 0; i < num; i++) { /* Section */ @@ -271,14 +270,14 @@ show_usage char sopt[4]; char *desc, *tok; if (opts[i].sopt) - snprintf(sopt, sizeof(sopt), "-%c/", opts[i].sopt); + snprintf(sopt, sizeof(sopt), "-%c,", opts[i].sopt); else - sopt[0] = 0; - snprintf(buf, sizeof(buf), " %s--%s", sopt, opts[i].lopt); + strcpy(sopt, " "); + snprintf(buf, sizeof(buf), " %s --%s", sopt, opts[i].lopt); desc = strdup(opts[i].desc); tok = strtok(desc, "\n"); while (tok) { - printf("%s\t\t%s\n", buf, tok); + printf("%-30s%s\n", buf, tok); tok = buf; while (*tok) { *tok = ' '; @@ -290,8 +289,8 @@ show_usage } } printf("\n"); - printf("For more information read the man page or visit\n"); - printf(" http://www.lonelycoder.com/hts/\n"); + printf("For more information please visit the Tvheadend website:\n"); + printf(" http://www.lonelycoder.com/tvheadend/\n"); printf("\n"); exit(0); } From 7088c9c0299c88adf9549236d5f835b9712d5740 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 12:34:22 +0000 Subject: [PATCH 393/503] webui: break configuration into multiple super tabs This is done to limit the total number of tabs on one row which is leading to problems on smaller displays. I also think its more logical. --- src/webui/static/app/tvheadend.js | 130 +++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 37 deletions(-) diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index cfaab734..1db51311 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -1,5 +1,9 @@ tvheadend.accessupdate = null; tvheadend.capabilties = null; +tvheadend.conf_chepg = null; +tvheadend.conf_dvbin = null; +tvheadend.conf_tsdvr = null; +tvheadend.conf_csa = null; /** * Displays a help popup window @@ -245,54 +249,106 @@ function accessUpdate(o) { if (!tvheadend.capabilities) return; - if (o.dvr == true && tvheadend.dvrpanel == null) { - tvheadend.dvrpanel = new tvheadend.dvr; - tvheadend.rootTabPanel.add(tvheadend.dvrpanel); - } + if (o.dvr == true && tvheadend.dvrpanel == null) { + tvheadend.dvrpanel = new tvheadend.dvr; + tvheadend.rootTabPanel.add(tvheadend.dvrpanel); + } - if (o.admin == true && tvheadend.confpanel == null) { - tvheadend.confpanel = new Ext.TabPanel({ - activeTab : 0, - autoScroll : true, - title : 'Configuration', - iconCls : 'wrench', - items : [ new tvheadend.miscconf, new tvheadend.chconf, - new tvheadend.epggrab, new tvheadend.cteditor, - new tvheadend.dvrsettings, - new tvheadend.iptv, new tvheadend.acleditor ] - }); - tvheadend.rootTabPanel.add(tvheadend.confpanel); + if (o.admin == true && tvheadend.confpanel == null) { + var tabs1 = [ + new tvheadend.miscconf, + new tvheadend.acleditor + ] + var tabs2; + + /* DVB inputs */ + tabs2 = []; if (tvheadend.capabilities.indexOf('linuxdvb') != -1 || tvheadend.capabilities.indexOf('v4l') != -1) { - tvheadend.confpanel.add(new tvheadend.tvadapters); - } - if (tvheadend.capabilities.indexOf('cwc') != -1) { - tvheadend.confpanel.add(new tvheadend.cwceditor); - tvheadend.confpanel.add(new tvheadend.capmteditor); + tabs2.push(new tvheadend.tvadapters); } + tabs2.push(new tvheadend.iptv); + tvheadend.conf_dvbin = new Ext.TabPanel({ + activeTab: 0, + autoScroll: true, + title: 'DVB Inputs', + iconCls: 'hardware', + items : tabs2 + }); + tabs1.push(tvheadend.conf_dvbin); + + /* Channel / EPG */ + tvheadend.conf_chepg = new Ext.TabPanel({ + activeTab: 0, + autoScroll: true, + title : 'Channel / EPG', + iconCls : 'television', + items : [ + new tvheadend.chconf, + new tvheadend.cteditor, + new tvheadend.epggrab + ] + }); + tabs1.push(tvheadend.conf_chepg); + + /* DVR / Timeshift */ + tabs2 = [ new tvheadend.dvrsettings ]; if (tvheadend.capabilities.indexOf('timeshift') != -1) { - tvheadend.confpanel.add(new tvheadend.timeshift); + tabs2.push(new tvheadend.timeshift) } + tvheadend.conf_tsdvr = new Ext.TabPanel({ + activeTab: 0, + autoScroll: true, + title: 'Recording', + iconCls: 'drive', + items : tabs2 + }); + tabs1.push(tvheadend.conf_tsdvr); + + /* CSA */ + if (tvheadend.capabilities.indexOf('cwc') != -1) { + tvheadend.conf_csa = new Ext.TabPanel({ + activeTab: 0, + autoScroll: true, + title: 'CSA', + iconCls: 'key', + items: [ + new tvheadend.cwceditor, + new tvheadend.capmteditor + ] + }); + tabs1.push(tvheadend.conf_csa); + } + + tvheadend.confpanel = new Ext.TabPanel({ + activeTab : 0, + autoScroll : true, + title : 'Configuration', + iconCls : 'wrench', + items : tabs1 + }); + + tvheadend.rootTabPanel.add(tvheadend.confpanel); tvheadend.confpanel.doLayout(); } - if (o.admin == true && tvheadend.statuspanel == null) { - tvheadend.statuspanel = new tvheadend.status; - tvheadend.rootTabPanel.add(tvheadend.statuspanel); - } + if (o.admin == true && tvheadend.statuspanel == null) { + tvheadend.statuspanel = new tvheadend.status; + tvheadend.rootTabPanel.add(tvheadend.statuspanel); + } - if (tvheadend.aboutPanel == null) { - tvheadend.aboutPanel = new Ext.Panel({ - border : false, - layout : 'fit', - title : 'About', - iconCls : 'info', - autoLoad : 'about.html' - }); - tvheadend.rootTabPanel.add(tvheadend.aboutPanel); - } + if (tvheadend.aboutPanel == null) { + tvheadend.aboutPanel = new Ext.Panel({ + border : false, + layout : 'fit', + title : 'About', + iconCls : 'info', + autoLoad : 'about.html' + }); + tvheadend.rootTabPanel.add(tvheadend.aboutPanel); + } - tvheadend.rootTabPanel.doLayout(); + tvheadend.rootTabPanel.doLayout(); } /** From a1efa161f199d6cb72dd11e65baef29b34ac4ddc Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 13:41:26 +0000 Subject: [PATCH 394/503] bump version number for next dev cycle --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 8899c76a..56a138ad 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Tvheadend (TV streaming server) +Tvheadend (TV streaming server) v3.5 ==================================== (c) 2006 - 2013 Andreas Öman, et al. From 205dda1b05565ad77d2ed1eaff25366922e76a78 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 14:14:45 +0000 Subject: [PATCH 395/503] support: update getmuxlist to work with launchpad. --- support/getmuxlist | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/support/getmuxlist b/support/getmuxlist index f115f871..eedce862 100755 --- a/support/getmuxlist +++ b/support/getmuxlist @@ -12,8 +12,9 @@ if [ -d ${DIR}/.git ]; then (cd ${DIR}; git pull) &> /dev/null # Fetch -else - rm -rf ${DIR} &> /dev/null +elif [ ! -d ${DIR} ]; then URL=git://linuxtv.org/dtv-scan-tables.git git clone $URL ${DIR} &> /dev/null fi + +# Note: will not update existing set (if not .git) From c33880290202d7181b51da968cd6247328225c92 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 15 Feb 2013 16:39:27 +0000 Subject: [PATCH 396/503] Fix #1605 - timeshift: correct mistake in file refcounting. --- src/timeshift/private.h | 6 ++++-- src/timeshift/timeshift_filemgr.c | 35 ++++++++++++++++++++++++------- src/timeshift/timeshift_reader.c | 19 +++++++++++++---- src/timeshift/timeshift_writer.c | 1 + 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/timeshift/private.h b/src/timeshift/private.h index 0a7f5b86..435eb4f8 100644 --- a/src/timeshift/private.h +++ b/src/timeshift/private.h @@ -137,10 +137,12 @@ void timeshift_filemgr_init ( void ); void timeshift_filemgr_term ( void ); int timeshift_filemgr_makedirs ( int ts_index, char *buf, size_t len ); -timeshift_file_t *timeshift_filemgr_last - ( timeshift_t *ts ); timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ); +timeshift_file_t *timeshift_filemgr_oldest + ( timeshift_t *ts ); +timeshift_file_t *timeshift_filemgr_newest + ( timeshift_t *ts ); timeshift_file_t *timeshift_filemgr_prev ( timeshift_file_t *ts, int *end, int keep ); timeshift_file_t *timeshift_filemgr_next diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 19bc6b43..cf6906e2 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -161,6 +161,7 @@ void timeshift_filemgr_remove { if (tsf->fd != -1) close(tsf->fd); + tvhlog(LOG_DEBUG, "timeshift", "ts %d remove %s\n", ts->id, tsf->path); TAILQ_REMOVE(&ts->files, tsf, link); atomic_add_u64(×hift_total_size, -tsf->size); timeshift_reaper_remove(tsf); @@ -192,7 +193,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Return last file */ if (!create) - return TAILQ_LAST(&ts->files, timeshift_file_list); + return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) @@ -216,9 +217,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); } else { -#ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); -#endif ts->full = 1; } } @@ -227,8 +226,16 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Check size */ if (!timeshift_unlimited_size && atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { - tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); - ts->full = 1; + + /* Remove the last file (if we can) */ + if (!tsf_hd->refcount) { + timeshift_filemgr_remove(ts, tsf_hd, 0); + + /* Full */ + } else { + tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); + ts->full = 1; + } } /* Create new file */ @@ -273,6 +280,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) tsf_tl = tsf_tmp; } + tsf_tl->refcount++; return tsf_tl; } @@ -303,11 +311,24 @@ timeshift_file_t *timeshift_filemgr_prev /* * Get the oldest file */ -timeshift_file_t *timeshift_filemgr_last ( timeshift_t *ts ) +timeshift_file_t *timeshift_filemgr_oldest ( timeshift_t *ts ) { - return TAILQ_FIRST(&ts->files); + timeshift_file_t *tsf = TAILQ_FIRST(&ts->files); + if (tsf) + tsf->refcount++; + return tsf; } +/* + * Get the newest file + */ +timeshift_file_t *timeshift_filemgr_newest ( timeshift_t *ts ) +{ + timeshift_file_t *tsf = TAILQ_LAST(&ts->files, timeshift_file_list); + if (tsf) + tsf->refcount++; + return tsf; +} /* ************************************************************************** * Setup / Teardown diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 70bd9d0a..e2d4437e 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -192,10 +192,11 @@ static timeshift_index_iframe_t *_timeshift_first_frame { int end; timeshift_index_iframe_t *tsi = NULL; - timeshift_file_t *tsf = timeshift_filemgr_last(ts); + timeshift_file_t *tsf = timeshift_filemgr_oldest(ts); while (tsf && !tsi) { - if (!(tsi = TAILQ_FIRST(&tsf->iframes))) + if (!(tsi = TAILQ_FIRST(&tsf->iframes))) { tsf = timeshift_filemgr_next(tsf, &end, 0); + } } if (tsf) tsf->refcount--; @@ -209,8 +210,9 @@ static timeshift_index_iframe_t *_timeshift_last_frame timeshift_index_iframe_t *tsi = NULL; timeshift_file_t *tsf = timeshift_filemgr_get(ts, 0); while (tsf && !tsi) { - if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) + if (!(tsi = TAILQ_LAST(&tsf->iframes, timeshift_index_iframe_list))) { tsf = timeshift_filemgr_prev(tsf, &end, 0); + } } if (tsf) tsf->refcount--; @@ -227,6 +229,10 @@ static int _timeshift_skip int64_t sec = req_time / (1000000 * TIMESHIFT_FILE_PERIOD); int back = (req_time < cur_time) ? 1 : 0; int end = 0; + + /* Hold local ref */ + if (cur_file) + cur_file->refcount++; /* Coarse search */ if (!tsi) { @@ -272,7 +278,7 @@ static int _timeshift_skip /* Find start/end of buffer */ if (end) { if (back) { - tsf = timeshift_filemgr_last(ts); + tsf = timeshift_filemgr_oldest(ts); tsi = NULL; while (tsf && !tsi) { if (!(tsi = TAILQ_FIRST(&tsf->iframes))) @@ -290,6 +296,9 @@ static int _timeshift_skip } } + if (cur_file) + cur_file->refcount--; + /* Done */ *new_file = tsf; *iframe = tsi; @@ -667,6 +676,8 @@ void *timeshift_reader ( void *p ) } /* Position */ + if (cur_file) + cur_file->refcount--; cur_file = tsf; if (tsi) cur_off = tsi->pos; diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index 06810a86..0e2f50ae 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -271,6 +271,7 @@ static void _process_msg tsf->bad = 1; ts->full = 1; ///< Stop any more writing } + tsf->refcount--; } pthread_mutex_unlock(&ts->rdwr_mutex); break; From 6aeeb4a4b60748469becef5dddae3955eb2d380f Mon Sep 17 00:00:00 2001 From: Stefan Saraev Date: Sat, 16 Feb 2013 16:06:36 +0200 Subject: [PATCH 397/503] capmt: fix crash while trying to open iptv channel with capmt enabled --- src/capmt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/capmt.c b/src/capmt.c index f7c3f894..ac507bd5 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -1018,6 +1018,10 @@ capmt_service_start(service_t *t) if (!capmt->capmt_enabled) continue; + + if (!(t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_adapter)) + continue; + tvhlog(LOG_INFO, "capmt", "Starting capmt server for service \"%s\" on tuner %d", t->s_svcname, From dace3778559f705254afde27dcd492db34ac8ab2 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 16 Feb 2013 19:31:24 +0000 Subject: [PATCH 398/503] Issue #1622 - timeshift: remove post double list removal --- src/timeshift/timeshift_filemgr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index cf6906e2..5ce488e0 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -161,7 +161,7 @@ void timeshift_filemgr_remove { if (tsf->fd != -1) close(tsf->fd); - tvhlog(LOG_DEBUG, "timeshift", "ts %d remove %s\n", ts->id, tsf->path); + tvhlog(LOG_DEBUG, "timeshift", "ts %d remove %s", ts->id, tsf->path); TAILQ_REMOVE(&ts->files, tsf, link); atomic_add_u64(×hift_total_size, -tsf->size); timeshift_reaper_remove(tsf); @@ -216,6 +216,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); + tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; @@ -228,12 +229,12 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { /* Remove the last file (if we can) */ - if (!tsf_hd->refcount) { + if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { - tvhlog(LOG_DEBUG, "timshift", "ts %d buffer full", ts->id); + tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } From 84c3cbafebd6920067e1ef8a43093a80efd6d9c3 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Sat, 16 Feb 2013 15:01:03 -0600 Subject: [PATCH 399/503] Add missing command line options to the man page. --- man/tvheadend.1 | 64 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/man/tvheadend.1 b/man/tvheadend.1 index 47fb3c5a..c36e36b1 100644 --- a/man/tvheadend.1 +++ b/man/tvheadend.1 @@ -13,21 +13,69 @@ Media player. .SH OPTIONS All arguments are optional. .TP -\fB\-f -Fork and become a background process (deamon). Default no. +\fB\-A\fR, \fB\-\-abort\fR +Immediately abort on startup (for debug). .TP -\fB\-u \fR\fIusername\fR -Run as user \fIusername\fR. Only applicable if daemonizing. Default is to -use the uid of 1 (daemon on most systems). +\fB\-a\fR, \fB\-\-adapters\fR +Only use specified DVB adapters (comma separated). .TP -\fB\-g \fR\fIgroupname\fR -Run as group \fR\fIgroupname\fR. Only applicable if daemonizing. Default is to use the 'video' group. If the 'video' group does not exist, gid 1 (daemon) will be used. +\fB\-c\fR, \fB\-\-config\fR +Specify an alternate config path; the default is \fI${HOME}/.hts\fR .TP -\fB\-C +\fB\-C\fR, \fB\-\-firstrun\fR If no useraccount exist then create one with no username and no password. Use with care as it will allow world-wide administrative access to your Tvheadend installation until you edit the access-control from within the Tvheadend UI. +.TP +\fB\-d\fR, \fB\-\-debug\fR +Enable all debug. +.TP +\fB\-f +Fork and become a background process (deamon). Default no. +.TP +\fB\-g\fR \fIgroupname\fR, \fB\-\-group \fR\fIgroupname\fR +Run as group \fR\fIgroupname\fR. Only applicable if daemonizing. Default is to use the 'video' group. If the 'video' group does not exist, gid 1 (daemon) will be used. +.TP +\fB\-\-http_port +Specify alternative http port (default 9881). +.TP +\fB\-\-http_root +Specify alternative http webroot. +.TP +\fB\-\-htsp_port +Specify alternative htsp port (default 9882). +.TP +\fB\-\-htsp_port2 +Specify extra htsp port. +.TP +\fB\-j\fR, \fB\-\-join\fR +Subscribe to a service permanently. +.TP +\fB\-l\fR, \fB\-\-log\fR +Log to file. +.TP +\fB\-r\fR, \fB\-\-rawts\fR +Use rawts file to generate virtual services. +.TP +\fB\-R\fR, \fB\-\-dvbraw\fR +Use rawts file to create virtual adapter. +.TP +\fB\-s\fR, \fB\-\-syslog\fR +Enable debug to syslog. +.TP +\fB\-\-uidebug +Enable web UI debug. +.TP +\fB\-u\fR \fIusername\fR, \fB\-\-user\fR \fIusername\fR +Run as user \fIusername\fR. Only applicable if daemonizing. Default is to +use the uid of 1 (daemon on most systems). +.TP +\fB\-6\fR, \fB\-\-ipv6\fR +Listen on IPv6. +.TP +\fB\-v +Show version information. .SH "LOGGING" All activity inside tvheadend is logged to syslog using log facility \fBLOG_DAEMON\fR. From a3fdc6f1200aef4f161607bb9f62e07d5d85b9e6 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Sat, 16 Feb 2013 16:01:34 -0600 Subject: [PATCH 400/503] Allow binding address to be specified via --bindaddr flag. --- man/tvheadend.1 | 4 ++++ src/htsp_server.c | 6 +++--- src/htsp_server.h | 2 +- src/http.c | 4 ++-- src/http.h | 2 +- src/main.c | 6 ++++-- src/tcp.c | 12 +++++++++--- src/tcp.h | 2 +- 8 files changed, 25 insertions(+), 13 deletions(-) diff --git a/man/tvheadend.1 b/man/tvheadend.1 index 47fb3c5a..7b1e03f3 100644 --- a/man/tvheadend.1 +++ b/man/tvheadend.1 @@ -13,6 +13,10 @@ Media player. .SH OPTIONS All arguments are optional. .TP +\fB\-b\fR \fIaddress\fR, \fB\-\-bindaddr\fR \fIaddress\fR +Specify an interface IP address on which incoming HTTP and HTSP connections +will be accepted. By default, connections are accepted on all interfaces. +.TP \fB\-f Fork and become a background process (deamon). Default no. .TP diff --git a/src/htsp_server.c b/src/htsp_server.c index ad7bb1a3..7e5d8c0d 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1966,12 +1966,12 @@ htsp_serve(int fd, void *opaque, struct sockaddr_storage *source, * Fire up HTSP server */ void -htsp_init(void) +htsp_init(const char *bindaddr) { extern int tvheadend_htsp_port_extra; - htsp_server = tcp_server_create(tvheadend_htsp_port, htsp_serve, NULL); + htsp_server = tcp_server_create(bindaddr, tvheadend_htsp_port, htsp_serve, NULL); if(tvheadend_htsp_port_extra) - htsp_server_2 = tcp_server_create(tvheadend_htsp_port_extra, htsp_serve, NULL); + htsp_server_2 = tcp_server_create(bindaddr, tvheadend_htsp_port_extra, htsp_serve, NULL); } /* ************************************************************************** diff --git a/src/htsp_server.h b/src/htsp_server.h index a82cca12..77002cac 100644 --- a/src/htsp_server.h +++ b/src/htsp_server.h @@ -22,7 +22,7 @@ #include "epg.h" #include "dvr/dvr.h" -void htsp_init(void); +void htsp_init(const char *bindaddr); void htsp_channel_update_current(channel_t *ch); diff --git a/src/http.c b/src/http.c index c668a02f..98562d74 100644 --- a/src/http.c +++ b/src/http.c @@ -816,7 +816,7 @@ http_serve(int fd, void *opaque, struct sockaddr_storage *peer, * Fire up HTTP server */ void -http_server_init(void) +http_server_init(const char *bindaddr) { - http_server = tcp_server_create(tvheadend_webui_port, http_serve, NULL); + http_server = tcp_server_create(bindaddr, tvheadend_webui_port, http_serve, NULL); } diff --git a/src/http.h b/src/http.h index 9e3d061c..08878b46 100644 --- a/src/http.h +++ b/src/http.h @@ -133,7 +133,7 @@ http_path_t *http_path_add(const char *path, void *opaque, -void http_server_init(void); +void http_server_init(const char *bindaddr); int http_access_verify(http_connection_t *hc, int mask); diff --git a/src/main.c b/src/main.c index 33db93b7..e163e029 100644 --- a/src/main.c +++ b/src/main.c @@ -376,6 +376,7 @@ main(int argc, char **argv) *opt_dvb_raw = NULL, #endif *opt_rawts = NULL, + *opt_bindaddr = NULL, *opt_subscribe = NULL; cmdline_opt_t cmdline_opts[] = { { 0, NULL, "Generic Options", OPT_BOOL, NULL }, @@ -383,6 +384,7 @@ main(int argc, char **argv) { 'v', "version", "Show version infomation", OPT_BOOL, &opt_version }, { 0, NULL, "Service Configuration", OPT_BOOL, NULL }, + { 'b', "bindaddr", "Specify bind address", OPT_STR, &opt_bindaddr}, { 'c', "config", "Alternate config path", OPT_STR, &opt_config }, { 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork }, { 'u', "user", "Run as user", OPT_STR, &opt_user }, @@ -625,7 +627,7 @@ main(int argc, char **argv) #endif tcp_server_init(opt_ipv6); - http_server_init(); + http_server_init(opt_bindaddr); webui_init(); serviceprobe_init(); @@ -643,7 +645,7 @@ main(int argc, char **argv) dvr_init(); - htsp_init(); + htsp_init(opt_bindaddr); if(opt_rawts != NULL) rawts_init(opt_rawts); diff --git a/src/tcp.c b/src/tcp.c index 5ccf1bac..0a9dbd7a 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -499,7 +499,7 @@ tcp_server_loop(void *aux) * */ void * -tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) +tcp_server_create(const char *bindaddr, int port, tcp_server_callback_t *start, void *opaque) { int fd, x; struct epoll_event e; @@ -515,14 +515,19 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; + if (bindaddr != NULL) + hints.ai_flags |= AI_NUMERICHOST; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - x = getaddrinfo(NULL, portBuf, &hints, &res); + x = getaddrinfo(bindaddr, portBuf, &hints, &res); free(portBuf); - if(x != 0) + if(x != 0) { + fprintf(stderr, "getaddrinfo: %s: %s", bindaddr != NULL ? bindaddr : "*", + x == EAI_SYSTEM ? strerror(errno) : gai_strerror(x)); return NULL; + } ressave = res; while(res) @@ -553,6 +558,7 @@ tcp_server_create(int port, tcp_server_callback_t *start, void *opaque) if(x != 0) { + fprintf(stderr, "bind: %s: %s", bindaddr != NULL ? bindaddr : "*", strerror(errno)); close(fd); return NULL; } diff --git a/src/tcp.h b/src/tcp.h index 0c061e43..98649f0b 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -32,7 +32,7 @@ typedef void (tcp_server_callback_t)(int fd, void *opaque, struct sockaddr_storage *peer, struct sockaddr_storage *self); -void *tcp_server_create(int port, tcp_server_callback_t *start, void *opaque); +void *tcp_server_create(const char *bindaddr, int port, tcp_server_callback_t *start, void *opaque); int tcp_read(int fd, void *buf, size_t len); From 06f83663c1f8203c2b22be1f6df1dc58a9524cc1 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 20 Feb 2013 11:18:30 +0000 Subject: [PATCH 401/503] timeshift: fix NULL ptr issue --- src/timeshift/timeshift_filemgr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 5ce488e0..56a4c2cd 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -281,7 +281,8 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) tsf_tl = tsf_tmp; } - tsf_tl->refcount++; + if (tsf_tl) + tsf_tl->refcount++; return tsf_tl; } From 92d0450ccaecf4dd031a3b3bf3e9d8bcbcaeb14d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 20 Feb 2013 11:55:13 +0000 Subject: [PATCH 402/503] imagecache: fix problem with imagecaching in the UI display --- src/imagecache.h | 1 + src/webui/extjs.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/imagecache.h b/src/imagecache.h index c96b3186..225d717e 100644 --- a/src/imagecache.h +++ b/src/imagecache.h @@ -52,6 +52,7 @@ int imagecache_open ( uint32_t id ); uint32_t _id = imagecache_get_id(_url);\ if (_id) {\ snprintf(_tmp, sizeof(_tmp), _fmt, _id);\ + htsmsg_add_str(_msg, _fld, _tmp);\ } else {\ htsmsg_add_str(_msg, _fld, _url);\ }\ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 45ecfed1..55c30a28 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -423,7 +423,7 @@ build_record_channel ( channel_t *ch ) htsmsg_add_u32(c, "chid", ch->ch_id); if(ch->ch_icon != NULL) - htsmsg_add_str(c, "ch_icon", ch->ch_icon); + htsmsg_add_imageurl(c, "ch_icon", "imagecache/%d", ch->ch_icon); buf[0] = 0; LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) { From b77c68e6e8a453aeefa679b58fe8313044ee5876 Mon Sep 17 00:00:00 2001 From: xhaggi Date: Wed, 20 Feb 2013 14:24:11 +0100 Subject: [PATCH 403/503] cwc: fixed problem with changed CA pids by provider and prefered CA pid --- src/cwc.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index 3c0b5ae9..5de7b839 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1,6 +1,6 @@ /* * tvheadend, CWC interface - * Copyright (C) 2007 Andreas Öman + * Copyright (C) 2007 Andreas Öman * * 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 @@ -1636,19 +1636,11 @@ cwc_table_input(struct th_descrambler *td, struct service *t, } if (ct->cs_okchannel == -3 && t->s_prefcapid != 0) { - if (t->s_prefcapid == st->es_pid) { ep = calloc(1, sizeof(ecm_pid_t)); ep->ep_pid = t->s_prefcapid; LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); tvhlog(LOG_DEBUG, "cwc", "Insert only one new ECM channel %d for service id %d", t->s_prefcapid, sid); - } else { - // check if prefcapid wrong - struct elementary_stream *prefca = service_stream_find(t, t->s_prefcapid); - - if (!prefca || prefca->es_type != SCT_CA) { - t->s_prefcapid = 0; - } - } + ct->cs_okchannel = -4; } if (ct->cs_okchannel == -1 || (ct->cs_okchannel == -3 && t->s_prefcapid == 0)) { From 667e1c2bad610fdfd443d73b0b945c7c848bfa5c Mon Sep 17 00:00:00 2001 From: xhaggi Date: Thu, 21 Feb 2013 01:12:43 +0100 Subject: [PATCH 404/503] cwc: reset prefcapid if service can't be descrambled --- src/cwc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cwc.c b/src/cwc.c index 5de7b839..6a0538ad 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -835,12 +835,17 @@ forbid: "Req delay: %"PRId64" ms)", t->s_svcname, seq, delay); ct->cs_keystate = CS_FORBIDDEN; + + /* reset prefcapid if descrambling fails */ + t->s_prefcapid = 0; + service_request_save(t, 0); + return; } else { ct->cs_okchannel = es->es_channel; - tvhlog(LOG_DEBUG, "cwc", "es->es_nok %d t->tht_prefcapid %d", es->es_nok, t->s_prefcapid); + tvhlog(LOG_DEBUG, "cwc", "es->es_nok %d, t->tht_prefcapid %d", es->es_nok, t->s_prefcapid); if(es->es_nok == 1 || t->s_prefcapid == 0) { t->s_prefcapid = ct->cs_okchannel; service_request_save(t, 0); From 14a470efd1da1f9a5c7fd1ae0df8718d4da49ef8 Mon Sep 17 00:00:00 2001 From: xhaggi Date: Thu, 21 Feb 2013 12:48:34 +0100 Subject: [PATCH 405/503] cwc: cleanups and better debug logging --- src/cwc.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index 6a0538ad..ebafd4ff 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -774,15 +774,10 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg, char chaninfo[32]; int i; int64_t delay = (getmonoclock() - es->es_time) / 1000LL; // in ms - - if(es->es_channel != -1) { - snprintf(chaninfo, sizeof(chaninfo), " (channel %d)", es->es_channel); - } else { - chaninfo[0] = 0; - } - es->es_pending = 0; + snprintf(chaninfo, sizeof(chaninfo), " (PID %d)", es->es_channel); + if(len < 19) { /* ERROR */ @@ -845,9 +840,9 @@ forbid: } else { ct->cs_okchannel = es->es_channel; - tvhlog(LOG_DEBUG, "cwc", "es->es_nok %d, t->tht_prefcapid %d", es->es_nok, t->s_prefcapid); if(es->es_nok == 1 || t->s_prefcapid == 0) { t->s_prefcapid = ct->cs_okchannel; + tvhlog(LOG_DEBUG, "cwc", "Saving prefered PID %d", t->s_prefcapid); service_request_save(t, 0); } es->es_nok = 0; @@ -897,7 +892,7 @@ forbid: for(i = 0; i < 256; i++) free(ep->ep_sections[i]); LIST_REMOVE(ep, ep_link); - tvhlog(LOG_WARNING, "cwc", "Delete ECMpid %d", ep->ep_pid); + tvhlog(LOG_WARNING, "cwc", "Delete ECM (PID %d) for service \"%s\"", ep->ep_pid, t->s_svcname); free(ep); ep = epn; } @@ -1637,14 +1632,14 @@ cwc_table_input(struct th_descrambler *td, struct service *t, if (ct->cs_okchannel == -2) { t->s_prefcapid = 0; ct->cs_okchannel = -1; - tvhlog(LOG_DEBUG, "cwc", "Insert after unexpected reply"); + tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected reply for service \"%s\"", t->s_svcname); } if (ct->cs_okchannel == -3 && t->s_prefcapid != 0) { ep = calloc(1, sizeof(ecm_pid_t)); ep->ep_pid = t->s_prefcapid; LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); - tvhlog(LOG_DEBUG, "cwc", "Insert only one new ECM channel %d for service id %d", t->s_prefcapid, sid); + tvhlog(LOG_DEBUG, "cwc", "Insert prefered ECM (PID %d) for service \"%s\"", t->s_prefcapid, t->s_svcname); ct->cs_okchannel = -4; } @@ -1652,7 +1647,7 @@ cwc_table_input(struct th_descrambler *td, struct service *t, ep = calloc(1, sizeof(ecm_pid_t)); ep->ep_pid = st->es_pid; LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); - tvhlog(LOG_DEBUG, "cwc", "Insert new ECM channel %d", st->es_pid); + tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", st->es_pid, t->s_svcname); } else { return; @@ -1677,19 +1672,15 @@ cwc_table_input(struct th_descrambler *td, struct service *t, /* ECM */ if(cwc->cwc_caid >> 8 == 6) { - channel = data[6] << 8 | data[7]; - snprintf(chaninfo, sizeof(chaninfo), " (channel %d)", channel); ep->ep_last_section = data[5]; section = data[4]; } else { - channel = -1; - chaninfo[0] = 0; ep->ep_last_section = 0; section = 0; } channel = st->es_pid; - snprintf(chaninfo, sizeof(chaninfo), " (channel %d)", channel); + snprintf(chaninfo, sizeof(chaninfo), " (PID %d)", channel); if(ep->ep_sections[section] == NULL) ep->ep_sections[section] = calloc(1, sizeof(ecm_section_t)); @@ -1717,16 +1708,15 @@ cwc_table_input(struct th_descrambler *td, struct service *t, if(ct->cs_okchannel >= 0 && channel != -1 && ct->cs_okchannel != channel) { - tvhlog(LOG_DEBUG, "cwc", "Filtering ECM channel %d", channel); + tvhlog(LOG_DEBUG, "cwc", "Filtering ECM (PID %d)", channel); return; } es->es_seq = cwc_send_msg(cwc, data, len, sid, 1); tvhlog(LOG_DEBUG, "cwc", - "Sending ECM%s section=%d/%d, for service %s (seqno: %d) PID %d", - chaninfo, section, ep->ep_last_section, t->s_svcname, es->es_seq, - st->es_pid); + "Sending ECM%s section=%d/%d, for service \"%s\" (seqno: %d)", + chaninfo, section, ep->ep_last_section, t->s_svcname, es->es_seq); es->es_time = getmonoclock(); break; From 0fab063340974a35cd68ea5ff9759acce66a291c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 21 Feb 2013 11:58:48 +0000 Subject: [PATCH 406/503] build: update build system to fix some odd use cases --- Makefile | 41 ++++++++++++++++++++++------------------- configure | 2 +- support/configure.inc | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index cd79dad8..1498467e 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ # Configuration # -include ${CURDIR}/.config.mk -PROG = ${BUILDDIR}/tvheadend +include $(dir $(lastword $(MAKEFILE_LIST))).config.mk +PROG := $(BUILDDIR)/tvheadend # # Common compiler flags @@ -31,9 +31,12 @@ CFLAGS += -Wall -Werror -Wwrite-strings -Wno-deprecated-declarations CFLAGS += -Wmissing-prototypes -fms-extensions CFLAGS += -g -funsigned-char -O2 CFLAGS += -D_FILE_OFFSET_BITS=64 -CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} +CFLAGS += -I${BUILDDIR} -I${ROOTDIR}/src -I${ROOTDIR} LDFLAGS += -lrt -ldl -lpthread -lm +vpath %.c $(ROOTDIR) +vpath %.h $(ROOTDIR) + # # Other config # @@ -45,7 +48,7 @@ BUNDLE_FLAGS = ${BUNDLE_FLAGS-yes} # Binaries/Scripts # -MKBUNDLE = $(PYTHON) $(CURDIR)/support/mkbundle +MKBUNDLE = $(PYTHON) $(ROOTDIR)/support/mkbundle # # Debug/Output @@ -54,7 +57,7 @@ MKBUNDLE = $(PYTHON) $(CURDIR)/support/mkbundle ifndef V ECHO = printf "%-16s%s\n" $(1) $(2) BRIEF = CC MKBUNDLE CXX -MSG = $(subst $(CURDIR)/,,$@) +MSG = $(subst $(BUILDDIR)/,,$@) $(foreach VAR,$(BRIEF), \ $(eval $(VAR) = @$$(call ECHO,$(VAR),$$(MSG)); $($(VAR)))) endif @@ -62,10 +65,10 @@ endif # # Core # -SRCS = src/main.c \ +SRCS = src/version.c \ + src/main.c \ src/utils.c \ src/wrappers.c \ - src/version.c \ src/access.c \ src/dtable.c \ src/tcp.c \ @@ -208,7 +211,7 @@ SRCS-${CONFIG_BUNDLE} += bundle.c BUNDLES-yes += docs/html docs/docresources src/webui/static BUNDLES-yes += data/conf BUNDLES-${CONFIG_DVBSCAN} += data/dvb-scan -BUNDLES = $(BUNDLES-yes) +BUNDLES = $(BUNDLES-yes:%=$(ROOTDIR)/%) # # Add-on modules @@ -237,13 +240,13 @@ all: ${PROG} # Check configure output is valid check_config: - @test $(CURDIR)/.config.mk -nt $(CURDIR)/configure\ + @test $(ROOTDIR)/.config.mk -nt $(ROOTDIR)/configure\ || echo "./configure output is old, please re-run" - @test $(CURDIR)/.config.mk -nt $(CURDIR)/configure + @test $(ROOTDIR)/.config.mk -nt $(ROOTDIR)/configure # Recreate configuration reconfigure: - $(CURDIR)/configure $(CONFIGURE_ARGS) + $(ROOTDIR)/configure $(CONFIGURE_ARGS) # Binary ${PROG}: check_config $(OBJS) $(ALLDEPS) @@ -252,7 +255,7 @@ ${PROG}: check_config $(OBJS) $(ALLDEPS) # Object ${BUILDDIR}/%.o: %.c @mkdir -p $(dir $@) - $(CC) -MD -MP $(CFLAGS) -c -o $@ $(CURDIR)/$< + $(CC) -MD -MP $(CFLAGS) -c -o $@ $< # Add-on ${BUILDDIR}/%.so: ${SRCS_EXTRA} @@ -265,24 +268,24 @@ clean: find . -name "*~" | xargs rm -f distclean: clean - rm -rf ${CURDIR}/build.* - rm -f ${CURDIR}/.config.mk + rm -rf ${ROOTDIR}/build.* + rm -f ${ROOTDIR}/.config.mk -# Create buildversion.h -src/version.c: FORCE - @$(CURDIR)/support/version $@ > /dev/null +# Create version +$(ROOTDIR)/src/version.c: FORCE + @$(ROOTDIR)/support/version $@ > /dev/null FORCE: # Include dependency files if they exist. -include $(DEPS) # Include OS specific targets -include support/${OSENV}.mk +include ${ROOTDIR}/support/${OSENV}.mk # Bundle files $(BUILDDIR)/bundle.o: $(BUILDDIR)/bundle.c @mkdir -p $(dir $@) - $(CC) -I${CURDIR}/src -c -o $@ $< + $(CC) -I${ROOTDIR}/src -c -o $@ $< $(BUILDDIR)/bundle.c: @mkdir -p $(dir $@) diff --git a/configure b/configure index 3d3ec68f..542ec423 100755 --- a/configure +++ b/configure @@ -9,7 +9,7 @@ # Setup # ########################################################################### -ROOTDIR=$(dirname $0) +ROOTDIR=$(cd $(dirname $0); pwd) # # Options diff --git a/support/configure.inc b/support/configure.inc index ecb85da4..f5737f1f 100644 --- a/support/configure.inc +++ b/support/configure.inc @@ -464,7 +464,7 @@ function write_config # Automatically generated by configure - DO NOT EDIT! CONFIGURE_ARGS = ${CONFIGURE_ARGS} ROOTDIR ?= ${ROOTDIR} -BUILDDIR ?= ${ROOTDIR}/build.${PLATFORM} +BUILDDIR ?= ${BUILDDIR} OSENV ?= ${OSENV} ARCH ?= ${ARCH} CPU ?= ${CPU} From f44baad54c467353bf293f1f622e43c8e1ad9807 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 21 Feb 2013 14:33:32 +0000 Subject: [PATCH 407/503] ui: fix previous mod that broken channel configuration editing I made the change so that /channels returned the imagecache icon while I was testing some other UI code. Unfortunately that breaks channel editing. So I've added a second (possibly confusingly named) field. --- src/webui/extjs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 55c30a28..4d365f81 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -422,8 +422,10 @@ build_record_channel ( channel_t *ch ) htsmsg_add_str(c, "name", ch->ch_name); htsmsg_add_u32(c, "chid", ch->ch_id); - if(ch->ch_icon != NULL) - htsmsg_add_imageurl(c, "ch_icon", "imagecache/%d", ch->ch_icon); + if(ch->ch_icon != NULL) { + htsmsg_add_imageurl(c, "chicon", "imagecache/%d", ch->ch_icon); + htsmsg_add_str(c, "ch_icon", ch->ch_icon); + } buf[0] = 0; LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) { From 6b94edbaa280f6ddc5b87882e80bf93d00e4ef0d Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 21 Feb 2013 16:22:21 +0000 Subject: [PATCH 408/503] dvb: fix bug causing failures in service discovery using SDT --- src/dvb/dvb_tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index db59f811..126b78f8 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -351,7 +351,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } else { LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) if(tdmi->tdmi_transport_stream_id == tsid && - tdmi->tdmi_network_id != onid) + tdmi->tdmi_network_id == onid) break; if (!tdmi) return -1; } From 231e7b5fabeba074d9b98a0542b8bcb643195c2d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 21 Feb 2013 21:33:02 +0000 Subject: [PATCH 409/503] build: fix mistakes in previous build system updates --- Makefile | 5 +++-- support/posix.mk | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 1498467e..ac9a3e89 100644 --- a/Makefile +++ b/Makefile @@ -211,7 +211,7 @@ SRCS-${CONFIG_BUNDLE} += bundle.c BUNDLES-yes += docs/html docs/docresources src/webui/static BUNDLES-yes += data/conf BUNDLES-${CONFIG_DVBSCAN} += data/dvb-scan -BUNDLES = $(BUNDLES-yes:%=$(ROOTDIR)/%) +BUNDLES = $(BUNDLES-yes) # # Add-on modules @@ -272,6 +272,7 @@ distclean: clean rm -f ${ROOTDIR}/.config.mk # Create version +$(BUILDDIR)/src/version.o: $(ROOTDIR)/src/version.c $(ROOTDIR)/src/version.c: FORCE @$(ROOTDIR)/support/version $@ > /dev/null FORCE: @@ -289,4 +290,4 @@ $(BUILDDIR)/bundle.o: $(BUILDDIR)/bundle.c $(BUILDDIR)/bundle.c: @mkdir -p $(dir $@) - $(MKBUNDLE) -o $@ -d ${BUILDDIR}/bundle.d $(BUNDLE_FLAGS) $(BUNDLES) + $(MKBUNDLE) -o $@ -d ${BUILDDIR}/bundle.d $(BUNDLE_FLAGS) $(BUNDLES:%=$(ROOTDIR)/%) diff --git a/support/posix.mk b/support/posix.mk index f77677d7..03e772da 100644 --- a/support/posix.mk +++ b/support/posix.mk @@ -1,5 +1,5 @@ -MAN = man/tvheadend.1 -ICON = support/gnome/tvheadend.svg +MAN = $(ROOTDIR)/man/tvheadend.1 +ICON = $(ROOTDIR)/support/gnome/tvheadend.svg INSTICON= ${DESTDIR}$(prefix)/share/icons/hicolor/scalable/apps @@ -10,11 +10,12 @@ install: ${PROG} ${MAN} for bundle in ${BUNDLES}; do \ mkdir -p ${DESTDIR}${datadir}/tvheadend/$$bundle ;\ - cp -r $$bundle/* ${DESTDIR}${datadir}/tvheadend/$$bundle ;\ + cp -r $(ROOTDIR)/$$bundle/* ${DESTDIR}${datadir}/tvheadend/$$bundle ;\ done find ${DESTDIR}${datadir}/tvheadend -name .git -exec rm -rf {} \; &>/dev/null || /bin/true uninstall: - rm -f ${DESTDIR}${bindir)/tvheadend - rm -f ${DESTDIR}${mandir)/tvheadend.1 + rm -f ${DESTDIR}${bindir}/tvheadend + rm -f ${DESTDIR}${mandir}/tvheadend.1 + rm -rf ${DESTDIR}${datadir}/tvheadend From 7764b5618ee98ef2d8953ff780f2001206172571 Mon Sep 17 00:00:00 2001 From: "Andrew C. Martin" Date: Thu, 21 Feb 2013 20:49:29 -0700 Subject: [PATCH 410/503] documentation updates - fix typos/misspellings in documentation - fix broken link on how to build Tvheadend - standardize on most commonly used capitalization of "Tvheadend" - update copyright year to 2013 - break up the handful of run-on lines --- docs/html/config_access.html | 4 +-- docs/html/config_channels.html | 4 +-- docs/html/config_dvb.html | 32 ++++++++++----------- docs/html/config_epggrab.html | 51 ++++++++++++++++++++++++--------- docs/html/config_timeshift.html | 2 +- docs/html/epg.html | 2 +- docs/html/features.html | 2 +- docs/html/install.html | 4 +-- docs/html/overview.html | 2 +- docs/html/sysreq.html | 13 +++++---- 10 files changed, 70 insertions(+), 46 deletions(-) diff --git a/docs/html/config_access.html b/docs/html/config_access.html index 0eddf860..ef6c5fcb 100644 --- a/docs/html/config_access.html +++ b/docs/html/config_access.html @@ -6,7 +6,7 @@ wide open.

-When Tvheadend verifies access is scan thru all the enabled access control entries. +When Tvheadend verifies access is scan through all the enabled access control entries. The permission flags are combined for all matching access entries. An access entry is said to match if the username / password matches and the IP source address of the requesting peer is within the prefix. @@ -42,7 +42,7 @@ The columns have the following functions:

Username
Name of user, if no username is needed for match it should contain a - single asterix (*). + single asterisk (*).
Password
diff --git a/docs/html/config_channels.html b/docs/html/config_channels.html index 022dbe59..5062c39e 100644 --- a/docs/html/config_channels.html +++ b/docs/html/config_channels.html @@ -3,7 +3,7 @@
- The channels are listed / edited in a grid. + The channels are listed / edited in a grid.
  • To edit a cell, double-click on it. After a cell is changed it @@ -37,7 +37,7 @@ player.
    EPG Grab Source -
    Name of the Internet based EPG provider (typically XMLTV) channel +
    Name of the Internet-based EPG provider (typically XMLTV) channel that should be used to update this channels EPG info. By default Tvheadend tries to match the name itself, but sometimes it might not match correctly in which case you can do the mapping diff --git a/docs/html/config_dvb.html b/docs/html/config_dvb.html index a772da80..1f713325 100644 --- a/docs/html/config_dvb.html +++ b/docs/html/config_dvb.html @@ -53,7 +53,7 @@ This will attempt to close all available device handles. This can be necessary to allow some devices to go into low power states.
    - However this option has been known to cause problems with some multi tuner + However, this option has been known to cause problems with some multi-tuner DVB cards. If you have signal problems, try disabling this option.
    Note: this option has no effect if idle scanning is enabled. @@ -110,8 +110,8 @@
    Turn off LNB when idle
    This option can be enabled to disable the power to the LNB when the adapter - is not in use. This can reduce power consumption, however for poorly shieled - multi tuner setups you may some inteference when the LNB is re-enabled. + is not in use. This can reduce power consumption, however for poorly shielded + multi-tuner setups you may have some interference when the LNB is re-enabled. @@ -143,25 +143,25 @@ temporary broken
    Network -
    Network name as given in the DVB stream. Can not be changed +
    Network name as given in the DVB stream. Cannot be changed
    Frequency -
    Center frequency for the mux. Can not be changed +
    Center frequency for the mux. Cannot be changed
    Modulation -
    Information about the modulation used on the mux. Can not be changed +
    Information about the modulation used on the mux. Cannot be changed
    Polarisation -
    Information about the polarisation used on the mux. Can not be changed +
    Information about the polarisation used on the mux. Cannot be changed
    Satellite config (DVB-S only) -
    The satellite configuration in use on this mux. Can not be changed. +
    The satellite configuration in use on this mux. Cannot be changed.
    Frontend status -
    The status of the frontend signal last time the mux was tuned. Can not be changed +
    The status of the frontend signal last time the mux was tuned. Cannot be changed
    Mux id -
    Unique ID for this mux in the dvb network. Can not be changed +
    Unique ID for this mux in the dvb network. Cannot be changed
    Quality
    Tvheadend's estimated quality for the mux. @@ -180,7 +180,7 @@ 'Save changes' button. In order to change a Checkbox cell you only have to click once in it. -
  • Service can not be deleted since they are directly inherited / +
  • Service cannot be deleted since they are directly inherited / discovered from a mux they will reappear in just a few seconds should one delete them.
@@ -194,7 +194,7 @@ temporary broken
Service name -
Service name as given in the DVB stream. Can not be changed +
Service name as given in the DVB stream. Cannot be changed
Play
Open the VLC plugin window to play this service. @@ -210,13 +210,13 @@ charset to use when none is specified by the broadcaster.
EPG -
Uncheck this if EPG data should not be retreived for this service. +
Uncheck this if EPG data should not be retrieved for this service.
Type -
Type of service. Can not be changed +
Type of service. Cannot be changed
Provider -
Provider as given in the DVB stream. Can not be changed +
Provider as given in the DVB stream. Cannot be changed
Network
Network name for the mux this service resides on @@ -259,7 +259,7 @@
Port number to select for this configuration (numbering begins at 0).
In DiSEqC 1.0/2.0 mode, ports 0-3 are valid.
In DiSEqC 1.1/2.1 mode, ports 0-63 are valid. -
Use numbers 0-3 for LNBs behind first input on the uncommited switch, +
Use numbers 0-3 for LNBs behind first input on the uncommitted switch, then 4-7 and so on to support up to 64 ports using DiSEqC 1.1/2.1.
LNB type diff --git a/docs/html/config_epggrab.html b/docs/html/config_epggrab.html index 222e4ce6..2eda58ef 100644 --- a/docs/html/config_epggrab.html +++ b/docs/html/config_epggrab.html @@ -1,28 +1,51 @@

- This tab is used to configure EPG grabbing capabilities. TVheadend supports - a variety of different EPG grabbing mechanisms. These fall into 3 broad - categories, within which there are a variety of specific grabber - implementations. + This tab is used to configure the Electronic Program Guide (EPG) grabbing + capabilities. Tvheadend supports a variety of different EPG grabbing + mechanisms. These fall into 3 broad categories, within which there are a + variety of specific grabber implementations.

Grabber Types

    -
  • Over-the-Air (OTA) - These grabbers receive EPG data directly from the DVB network. This is often the easiest way to get up and running and does provide timely updates should scheduling change. However the information isn't always as rich as some of the other grabbers. -
  • Internal - These are grabbers which can be internally initiated from within TVheadend using a very simple scheduler. These are typically Internet based services. This can be a quick way to get richer EPG data where you don't have decent OTA support. -
  • External - These provide the option to run grabber scripts externally and to send data into TVheadend via Unix domain sockets. It provides the ability to run more complex configurations using things like cronjob's, script chains, etc. +
  • Over-the-Air (OTA) - These grabbers receive EPG data directly from the + DVB network. This is often the easiest way to get up and running and does + provide timely updates should scheduling change. However, the information + isn't always as rich as some of the other grabbers. +
  • Internal - These are grabbers which can be internally initiated from + within Tvheadend using a very simple scheduler. These are typically + Internet-based services. This can be a quick way to get richer EPG data + where you don't have decent OTA support. +
  • External - These provide the option to run grabber scripts externally and + to send data into Tvheadend via Unix domain sockets. It provides the ability + to run more complex configurations using things like cronjob's, script + chains, etc.

Grabber Modules

    -
  • EIT - This is a DVB standards compatible EIT grabber. Typically it will - retrieve now/next information, though on some networks there may be more +
  • EIT - This is a DVB standards compatible EIT grabber. Typically it + will retrieve now/next information, though on some networks there may be more extensive data published. -
  • Freesat/view - This is an extended version of EIT that is used by the Free-to-air DVB providers in the UK. It includes additional information such as series links and episode identifiers. -
  • OpenTV - This is a proprietary OTA EPG grabber. Its known to be used on the SKY networks, but others may use it. You need two configuration files to define settings for your particular network, if you don't see yours listed please visit IRC #hts for help. -
  • XMLTV - This is am Internet based suite of scripts, for more information about XMLTV please visit http://www.xmltv.org. To make use of the internal XMLTV grabber you typically require the xmltv-utils package to be installed. If you install new grabbers you will need to restart TVheadend to pick these up as they're loaded at startup. If you see no XMLTV grabbers listed then most probably XMLTV is not properly installed and in the PATH. -
  • PyEPG - This is another Internet based scraper. It currently only supports the Atlas UK system (for which you need a key), but it does provide a very rich EPG data set. For more information see http://github.com/adamsutton/PyEPG.
  • +
  • Freesat/view - This is an extended version of EIT that is used by the + Free-to-air DVB providers in the UK. It includes additional information such + as series links and episode identifiers. +
  • OpenTV - This is a proprietary OTA EPG grabber. It's known to be used on + the SKY networks, but others may use it. You need two configuration files to + define settings for your particular network, if you don't see yours listed + please visit IRC #hts for help. +
  • XMLTV - This is am Internet-based suite of scripts, for more information + about XMLTV please visit http://www.xmltv.org. + To make use of the internal XMLTV grabber you typically require the xmltv-utils + package to be installed. If you install new grabbers you will need to + restart Tvheadend to pick these up as they're loaded at startup. If you see + no XMLTV grabbers listed then most probably XMLTV is not properly installed + and in the PATH. +
  • PyEPG - This is another Internet-based scraper. It currently only + supports the Atlas UK system (for which you need a key), but it does provide + a very rich EPG data set. For more information see + http://github.com/adamsutton/PyEPG.

Configuration options

@@ -46,7 +69,7 @@
Select which internal grabber to use.
Grab interval -
Time period between grabs. Value and unit are indepdently set. +
Time period between grabs. Value and unit are independently set.

Over-the-air Grabbers

diff --git a/docs/html/config_timeshift.html b/docs/html/config_timeshift.html index bf1e6052..c664a986 100644 --- a/docs/html/config_timeshift.html +++ b/docs/html/config_timeshift.html @@ -14,7 +14,7 @@ you cannot rewind the buffer (it always begins on the currently playing frame). - Without this option there will be a permanent, cicular, buffer upto + Without this option there will be a permanent, circular, buffer up to the limits defined below.
Storage Path: diff --git a/docs/html/epg.html b/docs/html/epg.html index c11bc718..13118c8f 100644 --- a/docs/html/epg.html +++ b/docs/html/epg.html @@ -62,7 +62,7 @@ sorted based on start time. [Record series] button that will record all entries in the series.

For events without any series link information, a [Autorec] button will be - provided to create a pseudo series link using hte Autorec feature. + provided to create a pseudo series link using the Autorec feature.

diff --git a/docs/html/features.html b/docs/html/features.html index b1760939..7e7dc8ff 100644 --- a/docs/html/features.html +++ b/docs/html/features.html @@ -16,7 +16,7 @@

Multicasted IPTV.
Both raw transport streams in UDP and transport stream in RTP in UDP - is supported. The precense of RTP is autodetected. + is supported. The presence of RTP is autodetected.
Analog TV
diff --git a/docs/html/install.html b/docs/html/install.html index a6fc34af..569ee74b 100644 --- a/docs/html/install.html +++ b/docs/html/install.html @@ -38,7 +38,7 @@ Parts of this documentation is also available in the Tvheadend man page. access to all features / settings in the web user interface. If this is the first time you setup Tvheadend you are most encouraged to enter the web user interface, selected the 'Configuration' + 'Access Control' - tab and make reasonable changes. Futher help / documentation can be obtained + tab and make reasonable changes. Further help / documentation can be obtained inside the web interface.
Settings storage @@ -47,7 +47,7 @@ Parts of this documentation is also available in the Tvheadend man page.
Logging
- All activity inside tvheadend is logged to syslog using log facility. + All activity inside Tvheadend is logged to syslog using log facility. Also, if logged in to the web interface you will receive the same log in the bottom tab (System log). diff --git a/docs/html/overview.html b/docs/html/overview.html index 437dab8c..735f95c9 100644 --- a/docs/html/overview.html +++ b/docs/html/overview.html @@ -4,7 +4,7 @@

HTS Tvheadend 3.2

-© 2006 - 2012, Andreas Öman, et al.

+© 2006 - 2013, Andreas Öman, et al.

diff --git a/docs/html/sysreq.html b/docs/html/sysreq.html index 0f908e07..bf365f79 100644 --- a/docs/html/sysreq.html +++ b/docs/html/sysreq.html @@ -1,16 +1,17 @@

-If you want to build tvheadend from source, please visit -this page. -Please notice that wiki development site only reflects the work +If you want to build Tvheadend from source, please visit +this page. +Please note: that wiki development site only reflects the work in HEAD (-current). It should not deviate much should you want to build a released version from source, but you never know.

-Tvheadend is part of the HTS project hosted at -http://hts.lonelycoder.com/. +Tvheadend is part of the HTS project and is hosted at +https://www.lonelycoder.com/redmine/projects/tvheadend.

-It functions primarily as a TV backend for the Showtime Media player but +It functions primarily as a TV backend for the Showtime Media player, but can be used standalone for other purposes.

From 229a8d75d850a1dfd8694959e5e1114c5366f294 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 22 Feb 2013 11:11:10 +0000 Subject: [PATCH 411/503] timeshift: fix bug that can cause failure when seeking hits start of file this resulted in a failed read that terminated the timeshift buffer. --- src/timeshift/timeshift_reader.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index e2d4437e..1e9e5483 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -322,12 +322,10 @@ static int _timeshift_read #endif *fd = open((*cur_file)->path, O_RDONLY); } - if (*cur_off) { #ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d seek to %lu", ts->id, *cur_off); + tvhlog(LOG_DEBUG, "timeshift", "ts %d seek to %lu", ts->id, *cur_off); #endif - lseek(*fd, *cur_off, SEEK_SET); - } + lseek(*fd, *cur_off, SEEK_SET); /* Read msg */ ssize_t r = _read_msg(*fd, sm); From 7578a19a3e49a1f4358ab01edc0d863cad238542 Mon Sep 17 00:00:00 2001 From: xhaggi Date: Thu, 21 Feb 2013 20:49:03 +0100 Subject: [PATCH 412/503] cwc: improvements for ECM handling and code cleanups --- src/cwc.c | 78 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/src/cwc.c b/src/cwc.c index ebafd4ff..837b4cfe 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -149,7 +149,16 @@ typedef struct cwc_service { LIST_ENTRY(cwc_service) cs_link; - int cs_okchannel; + int cs_channel; + + /** + * ECM Status + */ + enum { + ECM_INIT, + ECM_VALID, + ECM_RESET + } ecm_state; /** * Status of the key(s) in cs_keys @@ -829,23 +838,23 @@ forbid: "Can not descramble service \"%s\", access denied (seqno: %d " "Req delay: %"PRId64" ms)", t->s_svcname, seq, delay); - ct->cs_keystate = CS_FORBIDDEN; - /* reset prefcapid if descrambling fails */ - t->s_prefcapid = 0; - service_request_save(t, 0); + ct->cs_keystate = CS_FORBIDDEN; + ct->ecm_state = ECM_RESET; return; } else { - ct->cs_okchannel = es->es_channel; - if(es->es_nok == 1 || t->s_prefcapid == 0) { - t->s_prefcapid = ct->cs_okchannel; + es->es_nok = 0; + ct->cs_channel = es->es_channel; + ct->ecm_state = ECM_VALID; + + if(t->s_prefcapid == 0 || t->s_prefcapid != ct->cs_channel) { + t->s_prefcapid = ct->cs_channel; tvhlog(LOG_DEBUG, "cwc", "Saving prefered PID %d", t->s_prefcapid); service_request_save(t, 0); } - es->es_nok = 0; tvhlog(LOG_DEBUG, "cwc", "Received ECM reply%s for service \"%s\" " @@ -884,7 +893,7 @@ forbid: ep = LIST_FIRST(&ct->cs_pids); while(ep != NULL) { - if (ct->cs_okchannel == ep->ep_pid) { + if (ct->cs_channel == ep->ep_pid) { ep = LIST_NEXT(ep, ep_link); } else { @@ -935,8 +944,7 @@ cwc_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len) } tvhlog(LOG_WARNING, "cwc", "Got unexpected ECM reply (seqno: %d)", seq); LIST_FOREACH(ct, &cwc->cwc_services, cs_link) { - tvhlog(LOG_DEBUG, "cwc", "After got unexpected (ct->cs_okchannel: %d)", ct->cs_okchannel); - if (ct->cs_okchannel == -3) ct->cs_okchannel = -2; + ct->ecm_state = ECM_RESET; } break; } @@ -1629,31 +1637,40 @@ cwc_table_input(struct th_descrambler *td, struct service *t, } if(ep == NULL) { - if (ct->cs_okchannel == -2) { + if (ct->ecm_state == ECM_RESET) { + ct->ecm_state = ECM_INIT; + ct->cs_channel = -1; t->s_prefcapid = 0; - ct->cs_okchannel = -1; - tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected reply for service \"%s\"", t->s_svcname); + tvhlog(LOG_DEBUG, "cwc", "Reset after unexpected or no reply for service \"%s\"", t->s_svcname); } - if (ct->cs_okchannel == -3 && t->s_prefcapid != 0) { + if (ct->ecm_state == ECM_INIT) { + // Validate prefered ECM PID + if(t->s_prefcapid != 0) { + struct elementary_stream *prefca = service_stream_find(t, t->s_prefcapid); + if (!prefca || prefca->es_type != SCT_CA) { + tvhlog(LOG_DEBUG, "cwc", "Invalid prefered ECM (PID %d) found for service \"%s\"", t->s_prefcapid, t->s_svcname); + t->s_prefcapid = 0; + } + } + + if(t->s_prefcapid == st->es_pid) { ep = calloc(1, sizeof(ecm_pid_t)); ep->ep_pid = t->s_prefcapid; LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); tvhlog(LOG_DEBUG, "cwc", "Insert prefered ECM (PID %d) for service \"%s\"", t->s_prefcapid, t->s_svcname); - ct->cs_okchannel = -4; - } - - if (ct->cs_okchannel == -1 || (ct->cs_okchannel == -3 && t->s_prefcapid == 0)) { - ep = calloc(1, sizeof(ecm_pid_t)); - ep->ep_pid = st->es_pid; - LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); - tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", st->es_pid, t->s_svcname); - } - else { - return; + } + else if(t->s_prefcapid == 0) { + ep = calloc(1, sizeof(ecm_pid_t)); + ep->ep_pid = st->es_pid; + LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link); + tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", st->es_pid, t->s_svcname); + } } } + if(ep == NULL) + return; LIST_FOREACH(c, &st->es_caids, link) { if(cwc->cwc_caid == c->caid) @@ -1706,8 +1723,8 @@ cwc_table_input(struct th_descrambler *td, struct service *t, memcpy(es->es_ecm, data, len); es->es_ecmsize = len; - if(ct->cs_okchannel >= 0 && channel != -1 && - ct->cs_okchannel != channel) { + if(ct->cs_channel >= 0 && channel != -1 && + ct->cs_channel != channel) { tvhlog(LOG_DEBUG, "cwc", "Filtering ECM (PID %d)", channel); return; } @@ -2163,7 +2180,8 @@ cwc_service_start(service_t *t) #endif ct->cs_cwc = cwc; ct->cs_service = t; - ct->cs_okchannel = -3; + ct->cs_channel = -1; + ct->ecm_state = ECM_INIT; td = &ct->cs_head; td->td_stop = cwc_service_destroy; From 4724e01d13dddb745fc248eb8b0e29ab7cbc81a1 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Thu, 28 Feb 2013 12:38:23 +0000 Subject: [PATCH 413/503] Remove unused files iptv_output.[ch] and tsmux.[ch] and related code --- src/iptv_output.c | 211 ------- src/iptv_output.h | 24 - src/psi.c | 39 -- src/psi.h | 3 - src/tsdemux.c | 3 +- src/tsmux.c | 1403 --------------------------------------------- src/tsmux.h | 75 --- src/tvheadend.h | 1 - 8 files changed, 1 insertion(+), 1758 deletions(-) delete mode 100644 src/iptv_output.c delete mode 100644 src/iptv_output.h delete mode 100644 src/tsmux.c delete mode 100644 src/tsmux.h diff --git a/src/iptv_output.c b/src/iptv_output.c deleted file mode 100644 index 9fab8ac4..00000000 --- a/src/iptv_output.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Output functions for fixed multicast streaming - * Copyright (C) 2007 Andreas Öman - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tvhead.h" -#include "iptv_output.h" -#include "dispatch.h" -#include "channels.h" -#include "subscriptions.h" -#include "tsmux.h" -#include "rtp.h" - -#define MULTICAST_PKT_SIZ (188 * 7) - -typedef struct output_multicast { - ts_muxer_t *om_muxer; - int om_fd; - struct sockaddr_in om_dst; - - int om_corruption; - - int om_inter_drop_rate; - int om_inter_drop_cnt; - - int om_seq; - enum { - OM_RAWUDP, - OM_RTP, - } om_encapsulation; - -} output_multicast_t; - -/** - * Output MPEG TS - */ -static void -iptv_output_ts(void *opaque, th_subscription_t *s, uint8_t *pkt, - int blocks, int64_t pcr) -{ - output_multicast_t *om = opaque; - - om->om_seq++; - - if(om->om_inter_drop_rate && - ++om->om_inter_drop_cnt == om->om_inter_drop_rate) { - om->om_inter_drop_cnt = 0; - return; - } - - switch(om->om_encapsulation) { - case OM_RTP: - rtp_sendmsg(pkt, blocks, pcr, om->om_fd, - (struct sockaddr *)&om->om_dst, sizeof(struct sockaddr_in), - om->om_seq); - break; - - case OM_RAWUDP: - sendto(om->om_fd, pkt, blocks * 188, 0, - (struct sockaddr *)&om->om_dst, sizeof(struct sockaddr_in)); - break; - } -} - - -/** - * Called when a subscription gets/loses access to a transport - */ -static void -iptv_subscription_callback(struct th_subscription *s, - subscription_event_t event, void *opaque) -{ - output_multicast_t *om = opaque; - - switch(event) { - case TRANSPORT_AVAILABLE: - assert(om->om_muxer == NULL); - om->om_muxer = ts_muxer_init(s, iptv_output_ts, om, TS_SEEK, - om->om_corruption); - ts_muxer_play(om->om_muxer, 0); - break; - - case TRANSPORT_UNAVAILABLE: - ts_muxer_deinit(om->om_muxer, s); - om->om_muxer = NULL; - break; - } -} - - - -/** - * Setup IPTV (TS over UDP) output - */ -static void -output_multicast_load(struct config_head *head) -{ - const char *name, *s, *b; - channel_t *ch; - output_multicast_t *om; - int ttl = 32; - struct sockaddr_in sin; - char title[100]; - char title2[100]; - - if((name = config_get_str_sub(head, "channel", NULL)) == NULL) - return; - - ch = channel_find_by_name(name, 1); - - om = calloc(1, sizeof(output_multicast_t)); - - om->om_fd = tvh_socket(AF_INET, SOCK_DGRAM, 0); - - if((b = config_get_str_sub(head, "interface-address", NULL)) != NULL) { - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = inet_addr(b); - - if(bind(om->om_fd, (struct sockaddr *)&sin, sizeof(sin))==-1) { - fprintf(stderr, "cannot bind to %s\n", b); - goto err; - } - } - - if((s = config_get_str_sub(head, "group-address", NULL)) == NULL) { - fprintf(stderr, "no group address configured\n"); - goto err; - } - om->om_dst.sin_addr.s_addr = inet_addr(s); - - if((s = config_get_str_sub(head, "port", NULL)) == NULL) { - fprintf(stderr, "no port configured\n"); - goto err; - } - om->om_dst.sin_port = htons(atoi(s)); - - if((s = config_get_str_sub(head, "ttl", NULL)) != NULL) - ttl = atoi(s); - - - if((s = config_get_str_sub(head, "corruption-interval", NULL)) != NULL) - om->om_corruption = atoi(s); - - if((s = config_get_str_sub(head, "inter-drop-rate", NULL)) != NULL) - om->om_inter_drop_rate = atoi(s); - - if((s = config_get_str_sub(head, "encapsulation", NULL)) != NULL) { - if(!strcasecmp(s, "rtp")) - om->om_encapsulation = OM_RTP; - } - - setsockopt(om->om_fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(int)); - - snprintf(title, sizeof(title), "%s:%d", inet_ntoa(om->om_dst.sin_addr), - ntohs(om->om_dst.sin_port)); - - syslog(LOG_INFO, "Static multicast output: \"%s\" to %s, source %s ", - ch->ch_name, title, inet_ntoa(sin.sin_addr)); - - snprintf(title2, sizeof(title2), "IPTV-OUT: %s", title); - - subscription_create(ch, 900, title2, iptv_subscription_callback, om, 0); - return; - - err: - close(om->om_fd); - free(om); -} - - - -void -output_multicast_setup(void) -{ - config_entry_t *ce; - - TAILQ_FOREACH(ce, &config_list, ce_link) { - if(ce->ce_type == CFG_SUB && !strcasecmp("multicast-output", ce->ce_key)) { - output_multicast_load(&ce->ce_sub); - } - } -} diff --git a/src/iptv_output.h b/src/iptv_output.h deleted file mode 100644 index 91895327..00000000 --- a/src/iptv_output.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Output functions for fixed multicast streaming - * Copyright (C) 2007 Andreas Öman - * - * 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 IPTV_OUTPUT_H_ -#define IPTV_OUTPUT_H_ - -void output_multicast_setup(void); - -#endif /* IPTV_OUTPUT_H_ */ diff --git a/src/psi.c b/src/psi.c index 10133b8c..b191037b 100644 --- a/src/psi.c +++ b/src/psi.c @@ -110,44 +110,6 @@ psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc, } -/** - * PAT parser, from ISO 13818-1 - */ -int -psi_parse_pat(service_t *t, uint8_t *ptr, int len, - pid_section_callback_t *pmt_callback) -{ - uint16_t prognum; - uint16_t pid; - elementary_stream_t *st; - - lock_assert(&t->s_stream_mutex); - - if(len < 5) - return -1; - - ptr += 5; - len -= 5; - - while(len >= 4) { - - prognum = ptr[0] << 8 | ptr[1]; - pid = (ptr[2] & 0x1f) << 8 | ptr[3]; - - if(prognum != 0) { - if(service_stream_find(t, pid) == NULL) { - st = service_stream_create(t, pid, SCT_PMT); - st->es_section_docrc = 1; - st->es_got_section = pmt_callback; - } - } - ptr += 4; - len -= 4; - } - return 0; -} - - /** * Append CRC */ @@ -954,7 +916,6 @@ static struct strtab streamtypetab[] = { { "DVBSUB", SCT_DVBSUB }, { "CA", SCT_CA }, { "PMT", SCT_PMT }, - { "PAT", SCT_PAT }, { "AAC", SCT_AAC }, { "MPEGTS", SCT_MPEGTS }, { "TEXTSUB", SCT_TEXTSUB }, diff --git a/src/psi.h b/src/psi.h index a3b099cf..34c9736b 100644 --- a/src/psi.h +++ b/src/psi.h @@ -37,9 +37,6 @@ typedef struct psi_section { void psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc, section_handler_t *cb, void *opaque); -int psi_parse_pat(struct service *t, uint8_t *ptr, int len, - pid_section_callback_t *pmt_callback); - int psi_parse_pmt(struct service *t, const uint8_t *ptr, int len, int chksvcid, int delete); diff --git a/src/tsdemux.c b/src/tsdemux.c index 7a2ecd3f..69b92374 100644 --- a/src/tsdemux.c +++ b/src/tsdemux.c @@ -103,7 +103,6 @@ ts_recv_packet0(service_t *t, elementary_stream_t *st, const uint8_t *tsb) switch(st->es_type) { case SCT_CA: - case SCT_PAT: case SCT_PMT: if(st->es_section == NULL) st->es_section = calloc(1, sizeof(struct psi_section)); @@ -224,7 +223,7 @@ ts_recv_packet1(service_t *t, const uint8_t *tsb, int64_t *pcrp) if((tsb[3] & 0xc0) || (t->s_scrambled_seen && st->es_type != SCT_CA && - st->es_type != SCT_PAT && st->es_type != SCT_PMT)) { + st->es_type != SCT_PMT)) { /** * Lock for descrambling, but only if packet was not in error diff --git a/src/tsmux.c b/src/tsmux.c deleted file mode 100644 index 6dd5c3ca..00000000 --- a/src/tsmux.c +++ /dev/null @@ -1,1403 +0,0 @@ -/* - * tvheadend, MPEG Transport stream muxer - * Copyright (C) 2008 - 2009 Andreas Öman - * - * 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 . - */ - -#include - -#include "tvhead.h" -#include "streaming.h" -#include "tsmux.h" - - -#define PID_PMT 1000 -#define PID_ES_BASE 2000 - -static const AVRational mpeg_tc = {1, 90000}; -static const AVRational mpeg_tc_27M = {1, 27000000}; - -LIST_HEAD(tsmuxer_es_list, tsmuxer_es); -TAILQ_HEAD(tsmuxer_pkt_queue, tsmuxer_pkt); - -/** - * - */ -typedef struct tsmuxer_pkt { - TAILQ_ENTRY(tsmuxer_pkt) tsp_link; - int64_t tsp_deadline; - int64_t tsp_dts; - int64_t tsp_pcr; - int tsp_contentsize; - - uint8_t tsp_payload[188]; - -} tsmuxer_pkt_t; - -/** - * - */ -typedef struct tsmuxer_fifo { - struct tsmuxer_pkt_queue tsf_queue; - int tsf_contentsize; - int tsf_len; - -} tsmuxer_fifo_t; - - - -/** - * TS Elementary stream - */ -typedef struct tsmuxer_es { - LIST_ENTRY(tsmuxer_es) te_link; - - int te_type; - - int te_input_index; - - char te_lang[4]; - - int te_pid; - int te_cc; - - int te_startcode; // Startcode to use for PES packetization - - struct streaming_message_queue te_smq; - int te_lookahead_depth; // bytes in te_smq - int te_lookahead_packets; // packets in te_smq - - int64_t te_mux_offset; - - int te_vbv_delay; - - tsmuxer_fifo_t te_delivery_fifo; - -} tsmuxer_es_t; - - - - -/** - * TS Multiplexer - */ -typedef struct tsmuxer { - - int tsm_run; - - struct tsmuxer_es_list tsm_eslist; - - int64_t tsm_pcr_start; - int64_t tsm_pcr_ref; - int64_t tsm_pcr_last; - - tsmuxer_es_t *tsm_pcr_stream; - - streaming_queue_t tsm_input; - - streaming_target_t *tsm_output; - - pthread_t tsm_thread; - -} tsmuxer_t; - - -/** - * - */ -static void -tmf_enq(tsmuxer_fifo_t *tsf, tsmuxer_pkt_t *tsp) -{ - /* record real content size */ - tsf->tsf_contentsize += tsp->tsp_contentsize; - - /* Enqueue packet */ - TAILQ_INSERT_TAIL(&tsf->tsf_queue, tsp, tsp_link); - tsf->tsf_len++; -} - -#if 0 -/** - * - */ -static void -tsf_remove(tsmuxer_fifo_t *tsf, tsmuxer_pkt_t *tsp) -{ - tsf->tsf_contentsize -= tsp->tsp_contentsize; - TAILQ_REMOVE(&tsf->tsf_queue, tsp, tsp_link); - tsf->tsf_len--; -} - -/** - * - */ -static tsmuxer_pkt_t * -tsf_deq(tsmuxer_fifo_t *tsf) -{ - tsmuxer_pkt_t *tm; - - tm = TAILQ_FIRST(&tsf->tsf_queue); - if(tm != NULL) - tsf_remove(tsf, tm); - return tm; -} -#endif - - -/** - * - */ -static void -tsf_init(tsmuxer_fifo_t *tsf) -{ - TAILQ_INIT(&tsf->tsf_queue); -} - - - -#if 0 -/** - * Check if we need to start delivery timer for the given stream - * - * Also, if it is the PCR stream and we're not yet runnig, figure out - * PCR and start generating packets - */ -static void -ts_check_deliver(tsmuxer_t *tsm, tsmuxer_es_t *te) -{ - int64_t now; - tsmuxer_fifo_t *tsf = &te->te_delivery_fifo; - int64_t next; - - if(dtimer_isarmed(&tms->tms_mux_timer)) - return; /* timer already running, we're fine */ - - assert(tms->tms_delivery_fifo.tmf_len != 0); - - now = getclock_hires(); - - if(ts->ts_pcr_ref == AV_NOPTS_VALUE) { - - if(ts->ts_pcr_start == AV_NOPTS_VALUE) - return; /* dont know anything yet */ - - ts->ts_pcr_ref = now - ts->ts_pcr_start + t->s_pcr_drift; - } - - f = TAILQ_FIRST(&tmf->tmf_queue); /* next packet we are going to send */ - next = f->tm_deadline + ts->ts_pcr_ref - t->s_pcr_drift; - - if(next < now + 100) - next = now + 100; - - dtimer_arm_hires(&tms->tms_mux_timer, ts_deliver, tms, next - 500); -} -#endif - - - -/** - * Generate TS packet, see comments inline - */ -static void -lookahead_dequeue(tsmuxer_t *tsm, tsmuxer_es_t *te) -{ - streaming_message_t *sm; - th_pkt_t *pkt; - tsmuxer_pkt_t *tsp; - uint8_t *tsb; - uint16_t u16; - int frrem, pad, tsrem, len, off, cc, hlen, flags; - int64_t t, tdur, toff, tlen, dts, pcr, basedelivery; - - - tlen = 0; - TAILQ_FOREACH(sm, &te->te_smq, sm_link) { - pkt = sm->sm_data; - tlen += pkt->pkt_payloadlen; - - if(pkt->pkt_pts != pkt->pkt_dts) { - tlen += 19; /* pes header with DTS and PTS */ - } else { - tlen += 14; /* pes header with PTS only */ - } - } - - if(tlen == 0) - return; - - sm = TAILQ_FIRST(&te->te_smq); - pkt = sm->sm_data; - toff = 0; - - /* XXX: assumes duration is linear, but it's probably ok */ - tdur = pkt->pkt_duration * te->te_lookahead_packets; - - if(te->te_mux_offset == AV_NOPTS_VALUE) { - if(te->te_vbv_delay == -1) - te->te_mux_offset = tdur / 2 - pkt->pkt_duration; - else - te->te_mux_offset = te->te_vbv_delay; - } - - if(tsm->tsm_pcr_start == AV_NOPTS_VALUE && tsm->tsm_pcr_stream == te) - tsm->tsm_pcr_start = pkt->pkt_dts - te->te_mux_offset; - - basedelivery = pkt->pkt_dts - te->te_mux_offset; - - while((sm = TAILQ_FIRST(&te->te_smq)) != NULL) { - - off = 0; - pkt = sm->sm_data; - - if(pkt->pkt_dts == pkt->pkt_pts) { - hlen = 8; - flags = 0x80; - } else { - hlen = 13; - flags = 0xc0; - } - - while(off < pkt->pkt_payloadlen) { - - tsp = malloc(sizeof(tsmuxer_pkt_t)); - tsp->tsp_deadline = basedelivery + tdur * toff / tlen; - - dts = (int64_t)pkt->pkt_duration * - (int64_t)off / (int64_t)pkt->pkt_payloadlen; - dts += pkt->pkt_dts; - - tsp->tsp_dts = dts; - tsb = tsp->tsp_payload; - - /* TS marker */ - *tsb++ = 0x47; - - /* Write PID and optionally payload unit start indicator */ - *tsb++ = te->te_pid >> 8 | (off ? 0 : 0x40); - *tsb++ = te->te_pid; - - cc = te->te_cc & 0xf; - te->te_cc++; - - /* Remaing bytes after 4 bytes of TS header */ - tsrem = 184; - - if(off == 0) { - /* When writing the packet header, shave of a bit of available - payload size */ - tsrem -= hlen + 6; - } - - /* Remaining length of frame */ - frrem = pkt->pkt_payloadlen - off; - - /* Compute amout of padding needed */ - pad = tsrem - frrem; - - pcr = tsp->tsp_deadline; - tsp->tsp_pcr = AV_NOPTS_VALUE; - - if(tsm->tsm_pcr_stream == te && tsm->tsm_pcr_last + 20000 < pcr) { - - tsp->tsp_pcr = pcr; - /* Insert PCR */ - - tlen += 8; /* compensate total length */ - tsrem -= 8; - pad -= 8; - if(pad < 0) - pad = 0; - - *tsb++ = 0x30 | cc; - *tsb++ = 7 + pad; - *tsb++ = 0x10; /* PCR flag */ - - t = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = t >> 25; - *tsb++ = t >> 17; - *tsb++ = t >> 9; - *tsb++ = t >> 1; - *tsb++ = (t & 1) << 7; - *tsb++ = 0; - - memset(tsb, 0xff, pad); - tsb += pad; - tsrem -= pad; - - tsm->tsm_pcr_last = pcr + 20000; - - } else if(pad > 0) { - /* Must pad TS packet */ - - *tsb++ = 0x30 | cc; - tsrem -= pad; - *tsb++ = --pad; - - memset(tsb, 0x00, pad); - tsb += pad; - } else { - *tsb++ = 0x10 | cc; - } - - - if(off == 0) { - /* Insert PES header */ - - /* Write startcode */ - *tsb++ = 0; - *tsb++ = 0; - *tsb++ = te->te_startcode >> 8; - *tsb++ = te->te_startcode; - - /* Write total frame length (without accounting for startcode and - length field itself */ - len = pkt->pkt_payloadlen + hlen; - - if(te->te_type == SCT_MPEG2VIDEO) { - /* It's okay to write len as 0 in transport streams, - but only for video frames, and i dont expect any of the - audio frames to exceed 64k - */ - len = 0; - } - - *tsb++ = len >> 8; - *tsb++ = len; - - *tsb++ = 0x80; /* MPEG2 */ - *tsb++ = flags; - *tsb++ = hlen - 3; /* length of rest of header (pts & dts) */ - - /* Write PTS */ - - if(flags == 0xc0) { - t = av_rescale_q(pkt->pkt_pts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - } - - /* Write DTS */ - - t = av_rescale_q(pkt->pkt_dts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - } - - memcpy(tsb, pkt->pkt_payload + off, tsrem); - - tsp->tsp_contentsize = tsrem; - - tmf_enq(&te->te_delivery_fifo, tsp); - - toff += tsrem; - off += tsrem; - - } - - te->te_lookahead_depth -= pkt->pkt_payloadlen; - te->te_lookahead_packets--; - pkt_ref_dec(pkt); - TAILQ_REMOVE(&te->te_smq, sm, sm_link); - - free(sm); - } - // ts_check_deliver(ts, tms); -} - - - -/** - * Packet input. - * - * We get the entire streaming message cause we might need to hold and - * enqueue the packet. So we can just reuse that allocation from now - * on - */ -static void -tsm_packet_input(tsmuxer_t *tsm, streaming_message_t *sm) -{ - tsmuxer_es_t *te; - th_pkt_t *pkt = sm->sm_data; - - LIST_FOREACH(te, &tsm->tsm_eslist, te_link) - if(te->te_input_index == pkt->pkt_componentindex) - break; - - if(te == NULL) { - // Stream not in use - streaming_msg_free(sm); - return; - } - - lookahead_dequeue(tsm, te); - - te->te_lookahead_depth += pkt->pkt_payloadlen; - te->te_lookahead_packets++; - - TAILQ_INSERT_TAIL(&te->te_smq, sm, sm_link); -} - - - -/** - * - */ -static void -te_destroy(tsmuxer_es_t *te) -{ - streaming_queue_clear(&te->te_smq); - LIST_REMOVE(te, te_link); - free(te); -} - - -/** - * - */ -static void -tsm_start(tsmuxer_t *tsm, const streaming_start_t *ss) -{ - int i, sc; - - tsmuxer_es_t *te; - - tsm->tsm_pcr_start = AV_NOPTS_VALUE; - tsm->tsm_pcr_ref = AV_NOPTS_VALUE; - tsm->tsm_pcr_last = INT64_MIN; - - - for(i = 0; i < ss->ss_num_components; i++) { - const streaming_start_component_t *ssc = &ss->ss_components[i]; - int dopcr = 0; - - switch(ssc->ssc_type) { - - case SCT_MPEG2VIDEO: - sc = 0x1e0; - dopcr = 1; - break; - - case SCT_MPEG2AUDIO: - sc = 0x1c0; - break; - - case SCT_AC3: - sc = 0x1bd; - break; - - case SCT_H264: - sc = 0x1e0; - dopcr = 1; - break; - default: - continue; - } - - te = calloc(1, sizeof(tsmuxer_es_t)); - tsf_init(&te->te_delivery_fifo); - memcpy(te->te_lang, ssc->ssc_lang, 4); - te->te_input_index = ssc->ssc_index; - te->te_type = ssc->ssc_type; - te->te_pid = PID_ES_BASE + i; - te->te_startcode = sc; - te->te_mux_offset = AV_NOPTS_VALUE; - - TAILQ_INIT(&te->te_smq); - - LIST_INSERT_HEAD(&tsm->tsm_eslist, te, te_link); - } -} - - -/** - * - */ -static void -tsm_stop(tsmuxer_t *tsm) -{ - tsmuxer_es_t *te; - - while((te = LIST_FIRST(&tsm->tsm_eslist)) != NULL) - te_destroy(te); -} - -/** - * - */ -static void -tsm_handle_input(tsmuxer_t *tsm, streaming_message_t *sm) -{ - switch(sm->sm_type) { - case SMT_PACKET: - tsm_packet_input(tsm, sm); - sm = NULL; - break; - - case SMT_START: - assert(LIST_FIRST(&tsm->tsm_eslist) == NULL); - tsm_start(tsm, sm->sm_data); - break; - - case SMT_STOP: - tsm_stop(tsm); - break; - - case SMT_TRANSPORT_STATUS: - // htsp_subscription_transport_status(hs, sm->sm_code); - break; - - case SMT_NOSOURCE: - // htsp_subscription_status(hs, "No available sources"); - break; - - case SMT_EXIT: - tsm->tsm_run = 0; - break; - } - - if(sm != NULL) - streaming_msg_free(sm); -} - - -/** - * - */ -static void * -tsm_thread(void *aux) -{ - tsmuxer_t *tsm = aux; - streaming_queue_t *sq = &tsm->tsm_input; - streaming_message_t *sm; - int64_t now, next = 0; - - tsm->tsm_run = 1; - - while(tsm->tsm_run) { - - pthread_mutex_lock(&sq->sq_mutex); - - sm = TAILQ_FIRST(&sq->sq_queue); - if(sm == NULL) { - - if(next == 0) { - pthread_cond_wait(&sq->sq_cond, &sq->sq_mutex); - } else { - struct timespec ts; - - ts.tv_sec = next / 1000000; - ts.tv_nsec = (next % 1000000) * 1000; - pthread_cond_timedwait(&sq->sq_cond, &sq->sq_mutex, &ts); - } - } - - sm = TAILQ_FIRST(&sq->sq_queue); - - if(sm != NULL) - TAILQ_REMOVE(&sq->sq_queue, sm, sm_link); - - pthread_mutex_unlock(&sq->sq_mutex); - - now = getmonoclock(); - - if(sm != NULL) - tsm_handle_input(tsm, sm); - - } - return NULL; -} - - -/** - * - */ -void -tsm_init(void) -{ - tsmuxer_t *tsm = calloc(1, sizeof(tsmuxer_t)); - - streaming_queue_init(&tsm->tsm_input); - - pthread_create(&tsm->tsm_thread, NULL, tsm_thread, tsm); -} - - - - - - -#if 0 - -#define _GNU_SOURCE -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "tvhead.h" -#include "dispatch.h" -#include "transports.h" -#include "subscriptions.h" -#include "psi.h" -#include "buffer.h" -#include "mux.h" -#include "tsmux.h" - -static void lookahead_dequeue(ts_muxer_t *ts, th_muxstream_t *tms); - - -/** - * Send current packet - */ -static void -ts_muxer_send_packet(ts_muxer_t *ts) -{ - int i; - int64_t t, tlow, pcr; - uint8_t *d; - service_t *tr; - - if(ts->ts_block == 0) - return; - - d = ts->ts_packet; - - /* Update PCR */ - - if(ts->ts_pcr_ref != AV_NOPTS_VALUE) { - for(i = 0; i < ts->ts_block; i++) { - d = ts->ts_packet + i * 188; - if((d[3] & 0xf0) == 0x30 && d[4] >= 7 && d[5] & 0x10) { - tr = ts->ts_muxer->tm_subscription->ths_transport; - - pcr = getclock_hires() - ts->ts_pcr_ref - tr->s_pcr_drift; - t = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc_27M); - tlow = t % 300LL; - t = t / 300LL; - - d[6] = t >> 25; - d[7] = t >> 17; - d[8] = t >> 9; - d[9] = t >> 1; - d[10] = ((t & 1) << 7) | ((tlow >> 8) & 1); - d[11] = tlow; - } - } - } - ts->ts_output(ts->ts_output_opaque, ts->ts_subscription, ts->ts_packet, - ts->ts_block, 0); - ts->ts_block = 0; -} - -/** - * Push a MPEG TS packet to output - */ -static void -ts_muxer_add_packet(ts_muxer_t *ts, void *data, uint16_t pid) -{ - uint8_t *tsb; - - tsb = ts->ts_packet + ts->ts_block * 188; - ts->ts_block++; - - memcpy(tsb, data, 188); - - tsb[2] = pid; - tsb[1] = (tsb[1] & 0xf0) | (pid >> 8); - - if(ts->ts_block == ts->ts_blocks_per_packet) - ts_muxer_send_packet(ts); -} - -/** - * Raw TS input - */ -static void -ts_muxer_raw_input(struct th_subscription *s, void *data, int len, - elementary_stream_t *st, void *opaque) -{ - th_muxer_t *tm = s->ths_muxer; - ts_muxer_t *ts = opaque; - th_muxstream_t *tms; - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) - if(tms->tms_stream == st) - break; - - if(tms == NULL || tms->tms_index == 0) - return; /* Unknown / non-mapped stream */ - ts_muxer_add_packet(ts, data, tms->tms_index); -} - - - -/** - * Function for encapsulating a short table into a transport stream packet - */ -static void -ts_muxer_build_table(ts_muxer_t *ts, void *table, int tlen, int cc, int pid) -{ - int pad; - uint8_t tsb0[188], *tsb; - tsb = tsb0; - - pad = 184 - (tlen + 1); - - *tsb++ = 0x47; - *tsb++ = 0x40; - *tsb++ = 0; - *tsb++ = (cc & 0xf) | 0x30; - *tsb++ = --pad; - memset(tsb, 0, pad); - tsb += pad; - - *tsb++ = 0; /* Pointer field for tables */ - memcpy(tsb, table, tlen); - ts_muxer_add_packet(ts, tsb0, pid); -} - - - -/** - * - */ -static void -ts_muxer_generate_tables(void *aux, int64_t now) -{ - ts_muxer_t *ts = aux; - th_muxer_t *tm = ts->ts_muxer; - service_t *t; - th_muxstream_t *tms; - uint8_t table[180]; - int l, pcrpid; - - /* rearm timer */ - dtimer_arm_hires(&ts->ts_patpmt_timer, ts_muxer_generate_tables, - ts, now + 100000); - - l = psi_build_pat(NULL, table, sizeof(table), PID_PMT); - ts_muxer_build_table(ts, table, l, ts->ts_pat_cc, 0); - ts->ts_pat_cc++; - - switch(ts->ts_source) { - case TS_SRC_MUX: - pcrpid = ts->ts_pcr_stream->tms_index; - break; - - case TS_SRC_RAW_TS: - t = tm->tm_subscription->ths_transport; - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) - if(tms->tms_stream->st_pid == t->s_pcr_pid) - break; - - pcrpid = tms ? tms->tms_index : 0x1fff; - break; - default: - pcrpid = 0x1fff; - break; - } - - l = psi_build_pmt(tm, table, sizeof(table), pcrpid); - ts_muxer_build_table(ts, table, l, ts->ts_pmt_cc, PID_PMT); - ts->ts_pmt_cc++; - - ts_muxer_send_packet(ts); -} - - - - -/** - * - */ -static void -tmf_enq(th_muxfifo_t *tmf, th_muxpkt_t *tm) -{ - /* record real content size */ - tmf->tmf_contentsize += tm->tm_contentsize; - - /* Enqueue packet */ - TAILQ_INSERT_TAIL(&tmf->tmf_queue, tm, tm_link); - tmf->tmf_len++; -} - -/** - * - */ -static void -tmf_remove(th_muxfifo_t *tmf, th_muxpkt_t *tm) -{ - tmf->tmf_contentsize -= tm->tm_contentsize; - TAILQ_REMOVE(&tmf->tmf_queue, tm, tm_link); - tmf->tmf_len--; -} - - -/** - * - */ -static th_muxpkt_t * -tmf_deq(th_muxfifo_t *tmf) -{ - th_muxpkt_t *tm; - - tm = TAILQ_FIRST(&tmf->tmf_queue); - if(tm != NULL) - tmf_remove(tmf, tm); - return tm; -} - - - - -/** - * - */ -static void -tmf_init(th_muxfifo_t *tmf) -{ - TAILQ_INIT(&tmf->tmf_queue); -} - - -/** - * - */ -static void -ts_deliver(void *opaque, int64_t now) -{ - th_muxstream_t *tms = opaque; - th_muxer_t *tm = tms->tms_muxer; - service_t *t = tm->tm_subscription->ths_transport; - ts_muxer_t *ts = tm->tm_opaque; - th_muxpkt_t *f; - th_muxfifo_t *tmf = &tms->tms_delivery_fifo; - int64_t pcr = now - ts->ts_pcr_ref - t->s_pcr_drift; - int64_t dl, next, delta; - - f = tmf_deq(tmf); - dl = f->tm_deadline; - - delta = pcr - dl; - - ts_muxer_add_packet(ts, f->tm_pkt, tms->tms_index); - free(f); - - f = TAILQ_FIRST(&tmf->tmf_queue); /* next packet we are going to send */ - if(f == NULL) { - lookahead_dequeue(ts, tms); - f = TAILQ_FIRST(&tmf->tmf_queue); - if(f == NULL) - return; - } - - next = f->tm_deadline + ts->ts_pcr_ref - t->s_pcr_drift; - if(next < now + 100) - next = now + 100; - - dtimer_arm_hires(&tms->tms_mux_timer, ts_deliver, tms, next - 500); -} - - - -/** - * Check if we need to start delivery timer for the given stream - * - * Also, if it is the PCR stream and we're not yet runnig, figure out - * PCR and start generating packets - */ -static void -ts_check_deliver(ts_muxer_t *ts, th_muxstream_t *tms) -{ - int64_t now; - th_muxpkt_t *f; - th_muxfifo_t *tmf = &tms->tms_delivery_fifo; - service_t *t = ts->ts_muxer->tm_subscription->ths_transport; - int64_t next; - - if(dtimer_isarmed(&tms->tms_mux_timer)) - return; /* timer already running, we're fine */ - - assert(tms->tms_delivery_fifo.tmf_len != 0); - - now = getclock_hires(); - - if(ts->ts_pcr_ref == AV_NOPTS_VALUE) { - - if(ts->ts_pcr_start == AV_NOPTS_VALUE) - return; /* dont know anything yet */ - - ts->ts_pcr_ref = now - ts->ts_pcr_start + t->s_pcr_drift; - } - - f = TAILQ_FIRST(&tmf->tmf_queue); /* next packet we are going to send */ - next = f->tm_deadline + ts->ts_pcr_ref - t->s_pcr_drift; - - if(next < now + 100) - next = now + 100; - - dtimer_arm_hires(&tms->tms_mux_timer, ts_deliver, tms, next - 500); -} - - - - -/** - * Generate TS packet, see comments inline - * - * TODO: Dont insert both DTS and PTS if they're equal - */ - -static void -lookahead_dequeue(ts_muxer_t *ts, th_muxstream_t *tms) -{ - // elementary_stream_t *st = tms->tms_stream; - th_pkt_t *pkt; - th_muxpkt_t *tm; - th_refpkt_t *o; - uint8_t *tsb; - uint16_t u16; - int frrem, pad, tsrem, len, off, cc, hlen, flags; - int64_t t, tdur, toff, tlen, dts, pcr, basedelivery; - - tlen = 0; - TAILQ_FOREACH(o, &tms->tms_lookahead, trp_link) { - pkt = o->trp_pkt; - tlen += pkt->pkt_payloadlen; - - if(pkt->pkt_pts != pkt->pkt_dts) { - tlen += 19; /* pes header with DTS and PTS */ - } else { - tlen += 14; /* pes header with PTS only */ - } - } - - if(tlen == 0) - return; - - o = TAILQ_FIRST(&tms->tms_lookahead); - pkt = o->trp_pkt; - toff = 0; - - /* XXX: assumes duration is linear, but it's probably ok */ - tdur = pkt->pkt_duration * tms->tms_lookahead_packets; - - if(tms->tms_mux_offset == AV_NOPTS_VALUE) { - if(tms->tms_stream->st_vbv_delay == -1) - tms->tms_mux_offset = tdur / 2 - pkt->pkt_duration; - else - tms->tms_mux_offset = tms->tms_stream->st_vbv_delay; - } - - if(ts->ts_pcr_start == AV_NOPTS_VALUE && ts->ts_pcr_stream == tms) - ts->ts_pcr_start = pkt->pkt_dts - tms->tms_mux_offset; - - basedelivery = pkt->pkt_dts - tms->tms_mux_offset; - - while((o = TAILQ_FIRST(&tms->tms_lookahead)) != NULL) { - - off = 0; - pkt = o->trp_pkt; - - pkt_load(pkt); - - if(pkt->pkt_dts == pkt->pkt_pts) { - hlen = 8; - flags = 0x80; - } else { - hlen = 13; - flags = 0xc0; - } - - while(off < pkt->pkt_payloadlen) { - - tm = malloc(sizeof(th_muxpkt_t) + 188); - tm->tm_deadline = basedelivery + tdur * toff / tlen; - - dts = (int64_t)pkt->pkt_duration * - (int64_t)off / (int64_t)pkt->pkt_payloadlen; - dts += pkt->pkt_dts; - - tm->tm_dts = dts; - tsb = tm->tm_pkt; - - if(ts->ts_flags & TS_HTSCLIENT) { - /* Temporary hack */ - *tsb++ = tms->tms_stream->st_type; - } else { - /* TS marker */ - *tsb++ = 0x47; - } - - - /* Write PID and optionally payload unit start indicator */ - *tsb++ = tms->tms_index >> 8 | (off ? 0 : 0x40); - *tsb++ = tms->tms_index; - - cc = tms->tms_cc & 0xf; - tms->tms_cc++; - - /* Remaing bytes after 4 bytes of TS header */ - tsrem = 184; - - if(off == 0) { - /* When writing the packet header, shave of a bit of available - payload size */ - tsrem -= hlen + 6; - } - - /* Remaining length of frame */ - frrem = pkt->pkt_payloadlen - off; - - /* Compute amout of padding needed */ - pad = tsrem - frrem; - - pcr = tm->tm_deadline; - tm->tm_pcr = AV_NOPTS_VALUE; - - if(ts->ts_pcr_stream == tms && ts->ts_pcr_last + 20000 < pcr) { - - tm->tm_pcr = pcr; - /* Insert PCR */ - - tlen += 8; /* compensate total length */ - tsrem -= 8; - pad -= 8; - if(pad < 0) - pad = 0; - - *tsb++ = 0x30 | cc; - *tsb++ = 7 + pad; - *tsb++ = 0x10; /* PCR flag */ - - t = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = t >> 25; - *tsb++ = t >> 17; - *tsb++ = t >> 9; - *tsb++ = t >> 1; - *tsb++ = (t & 1) << 7; - *tsb++ = 0; - - memset(tsb, 0xff, pad); - tsb += pad; - tsrem -= pad; - - ts->ts_pcr_last = pcr + 20000; - - } else if(pad > 0) { - /* Must pad TS packet */ - - *tsb++ = 0x30 | cc; - tsrem -= pad; - *tsb++ = --pad; - - memset(tsb, 0x00, pad); - tsb += pad; - } else { - *tsb++ = 0x10 | cc; - } - - - if(off == 0) { - /* Insert PES header */ - - /* Write startcode */ - *tsb++ = 0; - *tsb++ = 0; - *tsb++ = tms->tms_sc >> 8; - *tsb++ = tms->tms_sc; - - /* Write total frame length (without accounting for startcode and - length field itself */ - len = pkt_len(pkt) + hlen; - - if(tms->tms_stream->st_type == HTSTV_MPEG2VIDEO) { - /* It's okay to write len as 0 in transport streams, - but only for video frames, and i dont expect any of the - audio frames to exceed 64k - */ - len = 0; - } - - *tsb++ = len >> 8; - *tsb++ = len; - - *tsb++ = 0x80; /* MPEG2 */ - *tsb++ = flags; - *tsb++ = hlen - 3; /* length of rest of header (pts & dts) */ - - /* Write PTS */ - - if(flags == 0xc0) { - t = av_rescale_q(pkt->pkt_pts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - } - - /* Write DTS */ - - t = av_rescale_q(pkt->pkt_dts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - } - - memcpy(tsb, pkt->pkt_payload + off, tsrem); - - if(tms->tms_corruption_interval != 0) { - /* Only corruption payload, never the header */ - if(tms->tms_corruption_last + tms->tms_corruption_interval < pcr && - off != 0 && pkt->pkt_frametype == PKT_I_FRAME) { - tms->tms_corruption_counter = 50; - tms->tms_corruption_last = pcr; - } - - if(tms->tms_corruption_counter) { - tms->tms_cc++; - tms->tms_corruption_counter--; - } - } - - tm->tm_contentsize = tsrem; - - tmf_enq(&tms->tms_delivery_fifo, tm); - - toff += tsrem; - off += tsrem; - - } - - tms->tms_lookahead_depth -= pkt->pkt_payloadlen; - tms->tms_lookahead_packets--; - pkt_deref(pkt); - TAILQ_REMOVE(&tms->tms_lookahead, o, trp_link); - - free(o); - } - ts_check_deliver(ts, tms); -} - - - -/** - * - */ -static void -ts_mux_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt) -{ - ts_muxer_t *ts = opaque; - elementary_stream_t *st = tms->tms_stream; - th_refpkt_t *trp; - - if(tms->tms_index == 0) - return; - - if(st->st_vbv_delay == -1) { - if(tms->tms_lookahead_depth + pkt->pkt_payloadlen > st->st_vbv_size) - lookahead_dequeue(ts, tms); - } else { - lookahead_dequeue(ts, tms); - } - - trp = malloc(sizeof(th_refpkt_t)); - trp->trp_pkt = pkt_ref(pkt); - tms->tms_lookahead_depth += pkt->pkt_payloadlen; - tms->tms_lookahead_packets++; - TAILQ_INSERT_TAIL(&tms->tms_lookahead, trp, trp_link); - -} - -/** - * - */ -ts_muxer_t * -ts_muxer_init(th_subscription_t *s, ts_mux_output_t *output, - void *opaque, int flags, int corruption) -{ - ts_muxer_t *ts = calloc(1, sizeof(ts_muxer_t)); - int dopcr; - int pididx = PID_ES_BASE; - th_muxstream_t *tms; - th_muxer_t *tm; - elementary_stream_t *st; - - - ts->ts_output = output; - ts->ts_output_opaque = opaque; - ts->ts_flags = flags; - - ts->ts_subscription = s; - tm = ts->ts_muxer = muxer_init(s, ts_mux_packet_input, ts); - - ts->ts_blocks_per_packet = 7; - ts->ts_packet = malloc(188 * ts->ts_blocks_per_packet); - - ts->ts_pcr_start = AV_NOPTS_VALUE; - ts->ts_pcr_ref = AV_NOPTS_VALUE; - ts->ts_pcr_last = INT64_MIN; - - - /* Do TS MUX specific init per stream */ - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - st = tms->tms_stream; - - dopcr = 0; - switch(st->st_type) { - case HTSTV_MPEG2VIDEO: - tms->tms_corruption_interval = (int64_t)corruption * 1000000LL; - tms->tms_sc = 0x1e0; - dopcr = 1; - break; - case HTSTV_MPEG2AUDIO: - tms->tms_sc = 0x1c0; - st->st_vbv_delay = 45000; - break; - case HTSTV_AC3: - tms->tms_sc = 0x1bd; - st->st_vbv_delay = 50000; - break; - case HTSTV_H264: - tms->tms_sc = 0x1e0; - dopcr = 1; - break; - default: - continue; - } - - tms->tms_mux_offset = AV_NOPTS_VALUE; - tmf_init(&tms->tms_delivery_fifo); - TAILQ_INIT(&tms->tms_lookahead); - - if(dopcr && ts->ts_pcr_stream == NULL) - ts->ts_pcr_stream = tms; - - tms->tms_index = pididx++; - } - return ts; -} - - -/** - * - */ -void -ts_muxer_deinit(ts_muxer_t *ts, th_subscription_t *s) -{ - th_muxstream_t *tms; - th_muxer_t *tm = s->ths_muxer; - th_muxpkt_t *f; - th_refpkt_t *o; - - dtimer_disarm(&ts->ts_patpmt_timer); - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - dtimer_disarm(&tms->tms_mux_timer); - - /* Free mux packets */ - while((f = tmf_deq(&tms->tms_delivery_fifo)) != NULL) - free(f); - - /* Unreference lookahead queue */ - while((o = TAILQ_FIRST(&tms->tms_lookahead)) != NULL) { - pkt_deref(o->trp_pkt); - TAILQ_REMOVE(&tms->tms_lookahead, o, trp_link); - free(o); - } - } - - free(ts->ts_packet); - muxer_deinit(tm, s); - free(ts); -} - - -/** - * - */ -void -ts_muxer_play(ts_muxer_t *ts, int64_t toffset) -{ - th_subscription_t *s = ts->ts_muxer->tm_subscription; - - if(!(ts->ts_flags & TS_SEEK) && - s->ths_transport->s_source_type == S_MPEG_TS) { - /* We dont need to seek and source is MPEG TS, we can use a - shortcut to avoid remuxing stream */ - - ts->ts_source = TS_SRC_RAW_TS; - } else { - ts->ts_source = TS_SRC_MUX; - } - - /* Start PAT / PMT generator */ - ts_muxer_generate_tables(ts, getclock_hires()); - - switch(ts->ts_source) { - case TS_SRC_MUX: - muxer_play(ts->ts_muxer, toffset); - break; - case TS_SRC_RAW_TS: - s->ths_raw_input = ts_muxer_raw_input; - s->ths_opaque = ts; - break; - } -} - - -/** - * - */ -void -ts_muxer_pause(ts_muxer_t *ts) -{ - dtimer_disarm(&ts->ts_patpmt_timer); - muxer_pause(ts->ts_muxer); -} -#endif diff --git a/src/tsmux.h b/src/tsmux.h deleted file mode 100644 index a041c923..00000000 --- a/src/tsmux.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * tvheadend, MPEG transport stream muxer - * Copyright (C) 2008 - 2009 Andreas Öman - * - * 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 TSMUX_H -#define TSMUX_H - -void tsm_init(void); - - -#if 0 - -typedef void (ts_mux_output_t)(void *opaque, th_subscription_t *s, - uint8_t *pkt, int npackets, int64_t pcr_ref); - -typedef struct ts_muxer { - th_subscription_t *ts_subscription; - int ts_flags; -#define TS_SEEK 0x1 -#define TS_HTSCLIENT 0x2 - - enum { - TS_SRC_MUX, - TS_SRC_RAW_TS, - - } ts_source; - - th_muxer_t *ts_muxer; - ts_mux_output_t *ts_output; - void *ts_output_opaque; - - int ts_pat_cc; - int ts_pmt_cc; - dtimer_t ts_patpmt_timer; - - uint8_t *ts_packet; - int ts_block; - int ts_blocks_per_packet; - - th_muxstream_t *ts_pcr_stream; - - int64_t ts_pcr_start; - int64_t ts_pcr_ref; /* System clock when PCR was/is/will be 0 */ - int64_t ts_pcr_last; -} ts_muxer_t; - - - - -ts_muxer_t *ts_muxer_init(th_subscription_t *s, ts_mux_output_t *output, - void *opaque, int flags, int corruption); - -void ts_muxer_deinit(ts_muxer_t *ts, th_subscription_t *s); - -void ts_muxer_play(ts_muxer_t *ts, int64_t toffset); - -void ts_muxer_pause(ts_muxer_t *ts); -#endif - - -#endif /* TSMUX_H */ diff --git a/src/tvheadend.h b/src/tvheadend.h index d2f2b4e5..41415724 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -185,7 +185,6 @@ typedef enum { SCT_TELETEXT, SCT_DVBSUB, SCT_CA, - SCT_PAT, SCT_PMT, SCT_AAC, SCT_MPEGTS, From 8b8868e5ff37925429e08adb1629eb9ce8c447cd Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Wed, 6 Mar 2013 21:12:42 +0000 Subject: [PATCH 414/503] Adding initial code for periodic saving of epgdb to disk --- src/epgdb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/epgdb.c b/src/epgdb.c index 33e53143..30ee6caa 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -226,6 +226,9 @@ void epg_init ( void ) /* Close file */ munmap(mem, st.st_size); close(fd); + + /* Create timer thread */ + } /* ************************************************************************** From 8925083ea7b74c192d2a9fd9b65ab72c1350b271 Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Wed, 6 Mar 2013 21:17:28 +0000 Subject: [PATCH 415/503] Add flush command to simplewebui to trigger a write to disk of the epgdb --- src/webui/simpleui.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index 1601ab18..af768ae1 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -483,4 +483,5 @@ simpleui_start(void) http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE); http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE); http_path_add("/status.xml", NULL, page_status, ACCESS_SIMPLE); + http_path_add("/epgflush", NULL, epg_save, ACCESS_SIMPLE); } From 9b877c90034593e6d1dde426ae290928f1d241b7 Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Thu, 7 Mar 2013 10:12:41 +0000 Subject: [PATCH 416/503] Add /epgsave to the webui so you can trigger a write of the epgdb to disk to help debugging, etc --- src/epgdb.c | 2 -- src/webui/simpleui.c | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/epgdb.c b/src/epgdb.c index 30ee6caa..616ab4ed 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -227,8 +227,6 @@ void epg_init ( void ) munmap(mem, st.st_size); close(fd); - /* Create timer thread */ - } /* ************************************************************************** diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index af768ae1..36dcda10 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -471,6 +471,25 @@ page_status(http_connection_t *hc, return 0; } +/** + * flush epgdb to disk on call + */ +static int +page_epgsave(http_connection_t *hc, + const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + + htsbuf_qprintf(hq, "\n" + "1\n"); + + epg_save(); + + http_output_content(hc, "text/xml"); + + return 0; +} + /** @@ -483,5 +502,5 @@ simpleui_start(void) http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE); http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE); http_path_add("/status.xml", NULL, page_status, ACCESS_SIMPLE); - http_path_add("/epgflush", NULL, epg_save, ACCESS_SIMPLE); + http_path_add("/epgsave", NULL, page_epgsave, ACCESS_SIMPLE); } From 5d8af1a209a2c40b60252942ad77ff1df4d92712 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Thu, 7 Mar 2013 14:53:20 +0000 Subject: [PATCH 417/503] Swap the order of the PAT and PMT packets injected by the passthrough muxer - it is more logical to write the PAT first, followed by the PMT, as that is the order parsers will need to process the packets. --- src/muxer/muxer_pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muxer/muxer_pass.c b/src/muxer/muxer_pass.c index 50dd5e23..97e9b156 100644 --- a/src/muxer/muxer_pass.c +++ b/src/muxer/muxer_pass.c @@ -230,8 +230,8 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) if(!rem) { pm->pm_pat[3] = (pm->pm_pat[3] & 0xf0) | (pm->pm_ic & 0x0f); pm->pm_pmt[3] = (pm->pm_pmt[3] & 0xf0) | (pm->pm_ic & 0x0f); - pass_muxer_write(m, pm->pm_pmt, 188); pass_muxer_write(m, pm->pm_pat, 188); + pass_muxer_write(m, pm->pm_pmt, 188); pm->pm_ic++; } } From c862269c622579629a2ca80c3bfe521a751cb8ba Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 7 Mar 2013 15:10:37 +0000 Subject: [PATCH 418/503] timeshift: minor mod to htsp to flush output buffer on skip --- src/htsp_server.c | 9 +++++++++ src/timeshift/timeshift_reader.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 7e5d8c0d..3d0eb9cf 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -267,6 +267,10 @@ htsp_flush_queue(htsp_connection_t *htsp, htsp_msg_q_t *hmq) TAILQ_REMOVE(&hmq->hmq_q, hm, hm_link); htsp_msg_destroy(hm); } + + // reset + hmq->hmq_length = 0; + hmq->hmq_payload = 0; pthread_mutex_unlock(&htsp->htsp_out_mutex); } @@ -2434,6 +2438,11 @@ htsp_subscription_skip(htsp_subscription_t *hs, streaming_skip_t *skip) htsmsg_t *m = htsmsg_create_map(); htsmsg_add_str(m, "method", "subscriptionSkip"); htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); + + /* Flush pkt buffers */ + if (skip->type != SMT_SKIP_ERROR) + htsp_flush_queue(hs->hs_htsp, &hs->hs_q); + if (skip->type == SMT_SKIP_ABS_TIME || skip->type == SMT_SKIP_ABS_SIZE) htsmsg_add_u32(m, "absolute", 1); if (skip->type == SMT_SKIP_ERROR) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 1e9e5483..1f9e739b 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -545,7 +545,7 @@ void *timeshift_reader ( void *p ) /* Convert */ skip_time = ts_rescale(skip->time, 1000000); - tvhlog(LOG_DEBUG, "timeshift", "ts %d skip %"PRId64" requested", ts->id, skip->time); + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip %"PRId64" requested %"PRId64, ts->id, skip_time, skip->time); /* Live playback (stage1) */ if (ts->state == TS_LIVE) { @@ -562,7 +562,8 @@ void *timeshift_reader ( void *p ) /* May have failed */ if (skip) { - tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64, ts->id, last_time); + tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64" pts_delta %"PRId64, + ts->id, last_time, ts->pts_delta); skip_time += (skip->type == SMT_SKIP_ABS_TIME) ? ts->pts_delta : last_time; /* Live (stage2) */ From f339c08c49d20a1ea0d23d2cb15b1339d2ee7adb Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 7 Mar 2013 15:17:09 +0000 Subject: [PATCH 419/503] time: hide some debug messages to limit spamming logs --- src/dvb/dvb_tables.c | 3 --- src/tvhtime.c | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 126b78f8..64ca69fa 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -1118,9 +1118,6 @@ dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, } 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; diff --git a/src/tvhtime.c b/src/tvhtime.c index 66c55b89..6b01883b 100644 --- a/src/tvhtime.c +++ b/src/tvhtime.c @@ -82,6 +82,11 @@ tvhtime_update ( struct tm *tm ) ntp_shm_t *ntp_shm; int64_t t1, t2; +#if TIME_TRACE + tvhlog(LOG_DEBUG, "time", "current time is %04d/%02d/%02d %02d:%02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); +#endif + /* Current and reported time */ now = mktime(tm); gettimeofday(&tv, NULL); @@ -89,20 +94,23 @@ tvhtime_update ( struct tm *tm ) /* 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) + if (tvhtime_update_enabled) { + if (llabs(t2 - t1) > tvhtime_tolerance) { + tvhlog(LOG_DEBUG, "time", "updated system clock"); stime(&now); + } + } /* NTP */ if (tvhtime_ntp_enabled) { if (!(ntp_shm = ntp_shm_init())) return; +#if TIME_TRACE + tvhlog(LOG_DEBUG, "time", "ntp delta = %"PRId64" us\n", t2 - t1); +#endif ntp_shm->valid = 0; ntp_shm->count++; ntp_shm->clockTimeStampSec = now; From 3752158b92ec20b448badc69f35681bca4134d5b Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Thu, 7 Mar 2013 15:27:29 +0000 Subject: [PATCH 420/503] Added checkbox to the epggrab dialog to enable 8Hr flush to disk EPG data on user request via thread --- docs/html/config_epggrab.html | 5 +++++ src/epgdb.c | 33 +++++++++++++++++++++++++++++++++ src/epggrab.c | 16 ++++++++++++++++ src/epggrab.h | 2 ++ src/webui/extjs.c | 3 +++ src/webui/static/app/epggrab.js | 9 +++++++-- 6 files changed, 66 insertions(+), 2 deletions(-) diff --git a/docs/html/config_epggrab.html b/docs/html/config_epggrab.html index 2eda58ef..822348fa 100644 --- a/docs/html/config_epggrab.html +++ b/docs/html/config_epggrab.html @@ -61,6 +61,11 @@
Update channel name
Automatically update channel icons using information provided by the enabled EPG providers. +
Periodic save EPG to disk +
Writes the current in-memory EPG database to disk every 8 Hours + when checked, so should a crash/unexpected shutdown occur EPG + data is saved periodically to the database (Re-read on + next startup)

Internal Grabber

diff --git a/src/epgdb.c b/src/epgdb.c index 616ab4ed..71a3ff26 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "tvheadend.h" #include "queue.h" @@ -35,6 +36,9 @@ extern epg_object_tree_t epg_brands; extern epg_object_tree_t epg_seasons; extern epg_object_tree_t epg_episodes; extern epg_object_tree_t epg_serieslinks; +static pthread_cond_t _epgdbsave_cond; +static void* _epgdbsave_thread ( void *p ); +pthread_mutex_t epgdbsave_mutex; /* ************************************************************************** * Load @@ -135,6 +139,29 @@ static void _epgdb_v2_process ( htsmsg_t *m, epggrab_stats_t *stats ) } } +/* + * Thread functions + */ +static void *_epgdbsave_thread ( void *p ) +{ + struct timespec ts; + ts.tv_nsec = 0; + tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", epggrab_epgdb_periodicsave); + while (1) { + if (epggrab_epgdb_periodicsave) { + tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", + epggrab_epgdb_periodicsave); + epg_save(); + }; + pthread_mutex_lock(&epgdbsave_mutex); + time(&ts.tv_sec); + ts.tv_sec += 28800; /* Every 8 hours */ + pthread_cond_timedwait(&_epgdbsave_cond, &epgdbsave_mutex, &ts); + pthread_mutex_unlock(&epgdbsave_mutex); + }; +return NULL; +} + /* * Load data */ @@ -227,6 +254,12 @@ void epg_init ( void ) munmap(mem, st.st_size); close(fd); + /* Create thread */ + pthread_mutex_init(&epgdbsave_mutex, NULL); + pthread_cond_init(&_epgdbsave_cond, NULL); + /* Start thread */ + pthread_t tid; + pthread_create(&tid, NULL, _epgdbsave_thread, NULL); } /* ************************************************************************** diff --git a/src/epggrab.c b/src/epggrab.c index 09dfc122..18b97e91 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -49,6 +49,7 @@ epggrab_module_list_t epggrab_modules; uint32_t epggrab_channel_rename; uint32_t epggrab_channel_renumber; uint32_t epggrab_channel_reicon; +uint32_t epggrab_epgdb_periodicsave; /* ************************************************************************** * Internal Grab Thread @@ -138,6 +139,7 @@ static void _epggrab_load ( void ) htsmsg_get_u32(m, "channel_rename", &epggrab_channel_rename); htsmsg_get_u32(m, "channel_renumber", &epggrab_channel_renumber); htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon); + htsmsg_get_u32(m, "epgdb_periodicsave", &epggrab_epgdb_periodicsave); if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval", &epggrab_interval)) { if (old) epggrab_interval *= 3600; @@ -234,6 +236,7 @@ void epggrab_save ( void ) htsmsg_add_u32(m, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(m, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(m, "channel_reicon", epggrab_channel_reicon); + htsmsg_add_u32(m, "epgdb_periodicsave", epggrab_epgdb_periodicsave); htsmsg_add_u32(m, "interval", epggrab_interval); if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); @@ -299,6 +302,19 @@ int epggrab_set_channel_renumber ( uint32_t e ) return save; } +/* + * Config from the webui for period save of db to disk + */ +int epggrab_set_periodicsave ( uint32_t e ) +{ + int save = 0; + if ( e != epggrab_epgdb_periodicsave ) { + epggrab_epgdb_periodicsave = e; + save = 1; + } + return save; +} + int epggrab_set_channel_reicon ( uint32_t e ) { int save = 0; diff --git a/src/epggrab.h b/src/epggrab.h index ddb46a41..d9496f58 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -226,6 +226,7 @@ extern epggrab_module_int_t* epggrab_module; extern uint32_t epggrab_channel_rename; extern uint32_t epggrab_channel_renumber; extern uint32_t epggrab_channel_reicon; +extern uint32_t epggrab_epgdb_periodicsave; /* * Set configuration @@ -236,6 +237,7 @@ int epggrab_set_module_by_id ( const char *id ); int epggrab_set_channel_rename ( uint32_t e ); int epggrab_set_channel_renumber ( uint32_t e ); int epggrab_set_channel_reicon ( uint32_t e ); +int epggrab_set_periodicsave ( uint32_t e ); int epggrab_enable_module ( epggrab_module_t *mod, uint8_t e ); int epggrab_enable_module_by_id ( const char *id, uint8_t e ); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 4d365f81..9546ef7b 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -589,6 +589,7 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(r, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(r, "channel_reicon", epggrab_channel_reicon); + htsmsg_add_u32(r, "epgdb_periodicsave", epggrab_epgdb_periodicsave); pthread_mutex_unlock(&epggrab_mutex); out = json_single_record(r, "epggrabSettings"); @@ -619,6 +620,8 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) save |= epggrab_set_channel_renumber(str ? 1 : 0); str = http_arg_get(&hc->hc_req_args, "channel_reicon"); save |= epggrab_set_channel_reicon(str ? 1 : 0); + str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave"); + save |= epggrab_set_periodicsave(str ? 1 : 0); if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) save |= epggrab_set_interval(atoi(str)); if ( (str = http_arg_get(&hc->hc_req_args, "module")) ) diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index 8e1a8f6b..af18c6df 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -82,7 +82,7 @@ tvheadend.epggrab = function() { var confreader = new Ext.data.JsonReader({ root : 'epggrabSettings' }, [ 'module', 'interval', 'channel_rename', 'channel_renumber', - 'channel_reicon' ]); + 'channel_reicon', 'epgdb_periodicsave' ]); /* **************************************************************** * Basic Fields @@ -184,6 +184,11 @@ tvheadend.epggrab = function() { fieldLabel : 'Update channel icon' }); + var epgPeriodicSave = new Ext.form.Checkbox({ + name : 'epgdb_periodicsave', + fieldLabel : 'Periodic save EPG to disk' + }); + /* * Simple fields */ @@ -192,7 +197,7 @@ tvheadend.epggrab = function() { width : 700, autoHeight : true, collapsible : true, - items : [ channelRename, channelRenumber, channelReicon ] + items : [ channelRename, channelRenumber, channelReicon, epgPeriodicSave ] }); /* From 404316f08f0b5c30b94dd4c5983c2359f3a51de2 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 7 Mar 2013 16:42:59 +0000 Subject: [PATCH 421/503] timeshift: add return to live function to HTSP --- src/htsp_server.c | 30 +++++++++++++++++++++++++++++- src/timeshift/timeshift_reader.c | 25 +++++++++++++++++++++++-- src/tvheadend.h | 3 ++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 3d0eb9cf..f0728cfb 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -57,7 +57,7 @@ static void *htsp_server, *htsp_server_2; -#define HTSP_PROTO_VERSION 8 +#define HTSP_PROTO_VERSION 9 #define HTSP_ASYNC_OFF 0x00 #define HTSP_ASYNC_ON 0x01 @@ -1463,6 +1463,33 @@ htsp_method_speed(htsp_connection_t *htsp, htsmsg_t *in) return NULL; } +/* + * Revert to live + */ +static htsmsg_t * +htsp_method_live(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsp_subscription_t *hs; + uint32_t sid; + streaming_skip_t skip; + + if(htsmsg_get_u32(in, "subscriptionId", &sid)) + return htsp_error("Missing argument 'subscriptionId'"); + + LIST_FOREACH(hs, &htsp->htsp_subscriptions, hs_link) + if(hs->hs_sid == sid) + break; + + if(hs == NULL) + return htsp_error("Requested subscription does not exist"); + + skip.type = SMT_SKIP_LIVE; + subscription_set_skip(hs->hs_s, &skip); + + htsp_reply(htsp, in, htsmsg_create_map()); + return NULL; +} + /** * Open file */ @@ -1641,6 +1668,7 @@ struct { { "subscriptionSeek", htsp_method_skip, ACCESS_STREAMING}, { "subscriptionSkip", htsp_method_skip, ACCESS_STREAMING}, { "subscriptionSpeed", htsp_method_speed, ACCESS_STREAMING}, + { "subscriptionLive", htsp_method_live, ACCESS_STREAMING}, { "fileOpen", htsp_method_file_open, ACCESS_RECORDER}, { "fileRead", htsp_method_file_read, ACCESS_RECORDER}, { "fileClose", htsp_method_file_close, ACCESS_RECORDER}, diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 1f9e739b..a89bd810 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -534,6 +534,27 @@ void *timeshift_reader ( void *p ) } else if (ctrl->sm_type == SMT_SKIP) { skip = ctrl->sm_data; switch (skip->type) { + case SMT_SKIP_LIVE: + if (ts->state != TS_LIVE) { + + /* Reset */ + if (ts->full) { + pthread_mutex_lock(&ts->rdwr_mutex); + timeshift_filemgr_flush(ts, NULL); + ts->full = 0; + pthread_mutex_unlock(&ts->rdwr_mutex); + } + + /* Release */ + if (sm) + streaming_msg_free(sm); + + /* Find end */ + skip_time = 0x7fffffffffffffff; + // TODO: change this sometime! + } + break; + case SMT_SKIP_ABS_TIME: if (ts->pts_delta == PTS_UNSET) { tvhlog(LOG_ERR, "timeshift", "ts %d abs skip not possible no PTS delta", ts->id); @@ -745,8 +766,8 @@ void *timeshift_reader ( void *p ) if (!end) end = (cur_speed > 0) ? 1 : -1; - /* Back to live */ - if (end == 1) { + /* Back to live (unless buffer is full) */ + if (end == 1 && !ts->full) { tvhlog(LOG_DEBUG, "timeshift", "ts %d eob revert to live mode", ts->id); ts->state = TS_LIVE; cur_speed = 100; diff --git a/src/tvheadend.h b/src/tvheadend.h index 41415724..9e6d137a 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -220,7 +220,8 @@ typedef struct streaming_skip SMT_SKIP_REL_TIME, SMT_SKIP_ABS_TIME, SMT_SKIP_REL_SIZE, - SMT_SKIP_ABS_SIZE + SMT_SKIP_ABS_SIZE, + SMT_SKIP_LIVE } type; union { off_t size; From 4b7ffc058fc1a8222d60fb43b265a79aa40a65e4 Mon Sep 17 00:00:00 2001 From: andyb2000 Date: Thu, 7 Mar 2013 21:35:15 +0000 Subject: [PATCH 422/503] Changed gui parameter to take a numeric value which is the number of hours to write data to disk --- docs/html/config_epggrab.html | 7 ++++--- src/epgdb.c | 4 ++-- src/webui/extjs.c | 4 ++-- src/webui/static/app/epggrab.js | 14 ++++++++++---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/html/config_epggrab.html b/docs/html/config_epggrab.html index 822348fa..7fbb33c2 100644 --- a/docs/html/config_epggrab.html +++ b/docs/html/config_epggrab.html @@ -61,11 +61,12 @@
Update channel name
Automatically update channel icons using information provided by the enabled EPG providers. -
Periodic save EPG to disk -
Writes the current in-memory EPG database to disk every 8 Hours - when checked, so should a crash/unexpected shutdown occur EPG +
Periodic save EPG to disk Interval +
Writes the current in-memory EPG database to disk every x Hours + (user defined), so should a crash/unexpected shutdown occur EPG data is saved periodically to the database (Re-read on next startup) + Set to 0 to disable.

Internal Grabber

diff --git a/src/epgdb.c b/src/epgdb.c index 71a3ff26..bb61fbc1 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -148,14 +148,14 @@ static void *_epgdbsave_thread ( void *p ) ts.tv_nsec = 0; tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", epggrab_epgdb_periodicsave); while (1) { - if (epggrab_epgdb_periodicsave) { + if (epggrab_epgdb_periodicsave != 0) { tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", epggrab_epgdb_periodicsave); epg_save(); }; pthread_mutex_lock(&epgdbsave_mutex); time(&ts.tv_sec); - ts.tv_sec += 28800; /* Every 8 hours */ + ts.tv_sec += epggrab_epgdb_periodicsave * 3600; /* User defined in hours */ pthread_cond_timedwait(&_epgdbsave_cond, &epgdbsave_mutex, &ts); pthread_mutex_unlock(&epgdbsave_mutex); }; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 9546ef7b..07673914 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -620,8 +620,8 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) save |= epggrab_set_channel_renumber(str ? 1 : 0); str = http_arg_get(&hc->hc_req_args, "channel_reicon"); save |= epggrab_set_channel_reicon(str ? 1 : 0); - str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave"); - save |= epggrab_set_periodicsave(str ? 1 : 0); + if ( (str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave")) ) + save |= epggrab_set_periodicsave(atoi(str)); if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) save |= epggrab_set_interval(atoi(str)); if ( (str = http_arg_get(&hc->hc_req_args, "module")) ) diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index af18c6df..e8fc3738 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -184,10 +184,16 @@ tvheadend.epggrab = function() { fieldLabel : 'Update channel icon' }); - var epgPeriodicSave = new Ext.form.Checkbox({ - name : 'epgdb_periodicsave', - fieldLabel : 'Periodic save EPG to disk' - }); + var epgPeriodicSave = new Ext.form.NumberField({ + width : 30, + allowNegative : false, + allowDecimals : false, + minValue : 0, + maxValue : 24, + value : 0, + fieldLabel : 'Periodic save EPG to disk', + name : 'epgdb_periodicsave', + }); /* * Simple fields From 9b385a78c5f94d2ec0188189b8ea9ba038c5def7 Mon Sep 17 00:00:00 2001 From: "Andrew C. Martin" Date: Thu, 7 Mar 2013 20:16:54 -0700 Subject: [PATCH 423/503] allow build path spaces in support scripts - allow build path spaces in configure & support scripts - use the same die function for all scripts --- configure | 8 ++++---- support/changelog | 16 ++++++++-------- support/configure.inc | 29 +++++++++++++++-------------- support/getmuxlist | 10 +++++----- support/launchpad-ppa | 10 +++++----- support/tarball | 12 ++++++------ support/version | 22 +++++++++++----------- 7 files changed, 54 insertions(+), 53 deletions(-) mode change 100644 => 100755 support/configure.inc diff --git a/configure b/configure index 542ec423..84f73154 100755 --- a/configure +++ b/configure @@ -9,7 +9,7 @@ # Setup # ########################################################################### -ROOTDIR=$(cd $(dirname $0); pwd) +ROOTDIR=$(cd "$(dirname "$0")"; pwd) # # Options @@ -34,7 +34,7 @@ OPTIONS=( # Begin # -. $ROOTDIR/support/configure.inc +. "$ROOTDIR/support/configure.inc" parse_args $* # ########################################################################### @@ -180,7 +180,7 @@ fi # if enabled linuxdvb && enabled dvbscan; then printf "${TAB}" "fetching dvb-scan files ..." - ${ROOTDIR}/support/getmuxlist + "${ROOTDIR}/support/getmuxlist" if [ $? -ne 0 ]; then echo "fail" die "Failed to fetch dvb-scan data (use --disable-dvbscan)" @@ -194,7 +194,7 @@ fi # Write config write_config -cat >> ${CONFIG_H} <> "${CONFIG_H}" <${CHANGELOG} "tvheadend (${VER}) ${DIST}; urgency=low" -echo >>${CHANGELOG} -echo >>${CHANGELOG} " * The full changelog can be found at " -echo >>${CHANGELOG} " http://www.lonelycoder.com/tvheadend/download" -echo >>${CHANGELOG} -echo >>${CHANGELOG} " -- ${DEBFULLNAME} <${DEBEMAIL}> ${NOW}" +echo >"${CHANGELOG}" "tvheadend (${VER}) ${DIST}; urgency=low" +echo >>"${CHANGELOG}" +echo >>"${CHANGELOG}" " * The full changelog can be found at " +echo >>"${CHANGELOG}" " http://www.lonelycoder.com/tvheadend/download" +echo >>"${CHANGELOG}" +echo >>"${CHANGELOG}" " -- ${DEBFULLNAME} <${DEBEMAIL}> ${NOW}" diff --git a/support/configure.inc b/support/configure.inc old mode 100644 new mode 100755 index f5737f1f..9ff2a1e8 --- a/support/configure.inc +++ b/support/configure.inc @@ -31,7 +31,7 @@ CONFIGURE_ARGS="$*" [ -z "$LDFLAGS" ] && LDFLAGS= # Environment -[ -z "$ROOTDIR" ] && ROOTDIR=$(cd $(dirname $0); pwd) +[ -z "$ROOTDIR" ] && ROOTDIR=$(cd "$(dirname "$0")"; pwd) [ -z "$BUILDDIR" ] && BUILDDIR=$ROOTDIR/build.$PLATFORM [ -z "$TMPDIR" ] && TMPDIR=/tmp @@ -52,9 +52,10 @@ function toupper echo "$@" | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ } +# Terminate function die { - [ -z "$1" ] || echo "ERROR: $1" + echo >&2 "ERROR: $@" exit 1 } @@ -255,7 +256,7 @@ function check_cc cat >$TMPDIR/$$.c < /dev/null @@ -455,12 +456,12 @@ function write_config local pkg= opt= k= v= # Create build directory - mkdir -p ${BUILDDIR} - BUILDDIR=`cd ${BUILDDIR} && pwd` + mkdir -p "${BUILDDIR}" + BUILDDIR=`cd "${BUILDDIR}" && pwd` # Create make include - CONFIG_MK=${ROOTDIR}/.config.mk - cat > ${CONFIG_MK} < "${CONFIG_MK}" < ${CONFIG_H} < "${CONFIG_H}" <>${CONFIG_MK} <>"${CONFIG_MK}" <>${CONFIG_H} <>"${CONFIG_H}" <>${CONFIG_MK} <>"${CONFIG_MK}" <>${CONFIG_H} <>"${CONFIG_H}" <>${CONFIG_MK} <>"${CONFIG_MK}" < /dev/null +if [ -d "${DIR}/.git" ]; then + (cd "${DIR}"; git pull) &> /dev/null # Fetch -elif [ ! -d ${DIR} ]; then +elif [ ! -d "${DIR}" ]; then URL=git://linuxtv.org/dtv-scan-tables.git - git clone $URL ${DIR} &> /dev/null + git clone $URL "${DIR}" &> /dev/null fi # Note: will not update existing set (if not .git) diff --git a/support/launchpad-ppa b/support/launchpad-ppa index ff367d04..bda6c26e 100755 --- a/support/launchpad-ppa +++ b/support/launchpad-ppa @@ -8,15 +8,15 @@ # Terminate function die { - echo $* + echo >&2 "ERROR: $@" exit 1 } # CMD -CMD=$(basename $0) +CMD=$(basename "$0") # Configuration -TVH_ROOT=$(cd $(dirname $0)/..; pwd) +TVH_ROOT=$(cd "$(dirname "$0")"/..; pwd) [ -z "$TVH_DIST" ] && TVH_DIST="lucid natty oneiric precise quantal" [ -z "$TVH_ARCH" ] && TVH_ARCH="i386 amd64" @@ -25,7 +25,7 @@ TVH_ROOT=$(cd $(dirname $0)/..; pwd) [ ! -z "$2" ] && PPA=$2 || PPA=unstable # Setup -cd $TVH_ROOT || exit 1 +cd "$TVH_ROOT" || exit 1 NOW=`date -R` CHANGELOG=$TVH_ROOT/debian/changelog VERFILE=$TVH_ROOT/src/version.c @@ -35,7 +35,7 @@ git checkout $REL || die "could not checkout $REL" git clean -dfx || die "could not clean git tree" # Create version file -VER=$($TVH_ROOT/support/version $VERFILE) +VER=$("$TVH_ROOT/support/version" $VERFILE) # Fetch scan files ./support/getmuxlist || die "failed to fetch dvb-scan files" diff --git a/support/tarball b/support/tarball index 2d5b809e..3c3c0886 100755 --- a/support/tarball +++ b/support/tarball @@ -3,16 +3,16 @@ # Build tarball of the current directory # -# Exit +# Terminate function die { - echo "ERROR: $*" + echo >&2 "ERROR: $@" exit 1 } # Switch dir -SRCDIR=$(dirname $0)/.. -cd $SRCDIR +SRCDIR=$(dirname "$0")/.. +cd "$SRCDIR" # Arguments REL=$1 @@ -46,8 +46,8 @@ rm -rf $DSTDIR/.gitignore $DSTDIR/support/changelog $DSTDIR/debian/changelog "" $VER # Build tarball -TARFILE=$(cd $SRCDIR/..; pwd)/tvheadend-$VER1.tar.gz -tar -C $TMPDIR -zcf $TARFILE tvheadend-$VER1 +TARFILE=$(cd "$SRCDIR"/..; pwd)/tvheadend-$VER1.tar.gz +tar -C $TMPDIR -zcf "$TARFILE" tvheadend-$VER1 # Done echo "Created $TARFILE" diff --git a/support/version b/support/version index 68d4fc85..f3b37916 100755 --- a/support/version +++ b/support/version @@ -8,15 +8,15 @@ FILE=$1 # Calculate version if [ -d ".git" ]; then - VER=$(cd $(dirname $0)/..; git describe --dirty --match "v*" 2> /dev/null) - if [ $? -ne 0 ]; then - # Git describe failed, maybe "--dirty" option is not available - # Adding "-unknown" postfix to mark this situation - VER=$(cd $(dirname $0)/..; git describe --match "v*" 2> /dev/null)-unknown - fi - VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") + VER=$(cd "$(dirname "$0")"/..; git describe --dirty --match "v*" 2> /dev/null) + if [ $? -ne 0 ]; then + # Git describe failed, maybe "--dirty" option is not available + # Adding "-unknown" postfix to mark this situation + VER=$(cd "$(dirname "$0")/.."; git describe --match "v*" 2> /dev/null)-unknown + fi + VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") else - VER=$(head -1 $(dirname $0)/../debian/changelog | awk '{ print $2 }' | tr -d '()' | cut -d '-' -f 1) + VER=$(head -1 "$(dirname "$0")/../debian/changelog" | awk '{ print $2 }' | tr -d '()' | cut -d '-' -f 1) fi # Output @@ -27,14 +27,14 @@ fi # Leave (probably ppa build) if [ -z "$VER" -a -s "$FILE" ]; then - cat $FILE + cat "$FILE" exit fi # Update? NEW_VER="const char *tvheadend_version = \"$VER\";" -OLD_VER=$(cat $FILE 2> /dev/null) +OLD_VER=$(cat "$FILE" 2> /dev/null) if [ "$NEW_VER" != "$OLD_VER" ]; then - echo $NEW_VER > $FILE + echo $NEW_VER > "$FILE" fi echo $VER From 079c006f1f8c76572b2c972ada9f45d465f642fc Mon Sep 17 00:00:00 2001 From: "Andrew C. Martin" Date: Thu, 7 Mar 2013 20:23:14 -0700 Subject: [PATCH 424/503] error check argument passed into epgdump --- support/epgdump | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/support/epgdump b/support/epgdump index 1c440db5..d0db4a30 100755 --- a/support/epgdump +++ b/support/epgdump @@ -15,7 +15,7 @@ # along with this program. If not, see . # """ -Dump EPG in human readable format for analysis +Dump Electronic Program Guide (EPG) in human readable format for analysis """ # System libs @@ -26,6 +26,12 @@ import pprint sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'lib', 'py')) import tvh.htsmsg as htsmsg +if len(sys.argv) < 2: + sys.exit('Usage: %s epg-path' % sys.argv[0]) + +if not os.path.exists(sys.argv[1]): + sys.exit('ERROR: epg "%s" was not found!' % sys.argv[1]) + # Open file fp = open(sys.argv[1], 'rb') for msg in htsmsg.deserialize(fp, True): From f5ecd443f41beb11ceb456c6b0c867c7cfde55ce Mon Sep 17 00:00:00 2001 From: "Andrew C. Martin" Date: Thu, 7 Mar 2013 20:27:04 -0700 Subject: [PATCH 425/503] use http for cloning dtv-scan-tables git repo - this allows ./configure to not hang when ran behind a firewall --- support/getmuxlist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/getmuxlist b/support/getmuxlist index baa7d6dc..ec39018b 100755 --- a/support/getmuxlist +++ b/support/getmuxlist @@ -13,7 +13,7 @@ if [ -d "${DIR}/.git" ]; then # Fetch elif [ ! -d "${DIR}" ]; then - URL=git://linuxtv.org/dtv-scan-tables.git + URL=http://linuxtv.org/git/dtv-scan-tables.git git clone $URL "${DIR}" &> /dev/null fi From 62a1a6d3c277e800a404df3359c84d06d2e8fce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 18 Feb 2013 12:01:16 +0100 Subject: [PATCH 426/503] insert chapters when packets are marked as containing commercials --- src/muxer/tvh/mkmux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 258a5438..19880a38 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -56,6 +56,8 @@ typedef struct mk_track { uint16_t aspect_num; uint16_t aspect_den; + + uint8_t commercial; } mk_track_t; /** @@ -208,6 +210,7 @@ mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) mkm->tracks[i].channels = ssc->ssc_channels; mkm->tracks[i].aspect_num = ssc->ssc_aspect_num; mkm->tracks[i].aspect_den = ssc->ssc_aspect_den; + mkm->tracks[i].commercial = COMMERCIAL_UNKNOWN; mkm->tracks[i].sri = ssc->ssc_sri; mkm->tracks[i].nextpts = PTS_UNSET; @@ -1046,6 +1049,11 @@ mk_mux_write_pkt(mk_mux_t *mkm, th_pkt_t *pkt) mark = 1; t->sri = pkt->pkt_sri; } + if(pkt->pkt_commercial != t->commercial && + pkt->pkt_commercial != COMMERCIAL_UNKNOWN) { + mark = 1; + t->commercial = pkt->pkt_commercial; + } if(mark) mk_mux_insert_chapter(mkm); From 288edf74fda945ec9ce3120cb4a8841b7b2f3935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Thu, 7 Mar 2013 23:36:30 +0100 Subject: [PATCH 427/503] xmltv: fixed parsing of star-rating --- src/epggrab/module/xmltv.c | 22 +++++++++++++++------- src/main.c | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index d8616f9a..e00d002b 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -354,14 +354,22 @@ static int _xmltv_parse_previously_shown * Star rating */ static int _xmltv_parse_star_rating - ( epggrab_module_t *mod, epg_episode_t *ee, htsmsg_t *tags ) + ( epggrab_module_t *mod, epg_episode_t *ee, htsmsg_t *body ) { - int a, b; - const char *stars; - if (!mod || !ee || !tags) return 0; - if (!(stars = htsmsg_xml_get_cdata_str(tags, "star-rating"))) return 0; - if (sscanf(stars, "%d/%d", &a, &b) != 2) return 0; - return epg_episode_set_star_rating(ee, (5 * a) / b, mod); + double a, b; + htsmsg_t *stars, *tags; + const char *s1, *s2; + + if (!mod || !ee || !body) return 0; + if (!(stars = htsmsg_get_map(body, "star-rating"))) return 0; + if (!(tags = htsmsg_get_map(stars, "tags"))) return 0; + if (!(s1 = htsmsg_xml_get_cdata_str(tags, "value"))) return 0; + if (!(s2 = strstr(s1, "/"))) return 0; + + a = atof(s1); + b = atof(s2 + 1); + + return epg_episode_set_star_rating(ee, (100 * a) / b, mod); } /* diff --git a/src/main.c b/src/main.c index 5f37ec09..15d0100a 100644 --- a/src/main.c +++ b/src/main.c @@ -434,6 +434,7 @@ main(int argc, char **argv) /* Set locale */ setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* make sure the timezone is set */ tzset(); From 595c3b8574ee6577379f0cb6ceb5b119c7146695 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Mar 2013 10:41:39 +0000 Subject: [PATCH 428/503] epgdb: some simplifications and corrections to the epg periodic save saving is now done from the gtimer system since a global lock is required to be able to edit the EPG data. I've also added missing global lock processing elsewhere. --- src/epg.h | 2 +- src/epgdb.c | 39 +++++---------------------------------- src/epggrab.c | 23 +++++++++++++++++++++-- src/main.c | 6 +++++- src/webui/extjs.c | 4 ++-- src/webui/simpleui.c | 4 +++- 6 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/epg.h b/src/epg.h index bff1a5c2..f8724e7b 100644 --- a/src/epg.h +++ b/src/epg.h @@ -556,7 +556,7 @@ void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, * ***********************************************************************/ void epg_init (void); -void epg_save (void); +void epg_save (void*); void epg_updated (void); /* ************************************************************************ diff --git a/src/epgdb.c b/src/epgdb.c index bb61fbc1..ebc1513b 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -36,9 +36,6 @@ extern epg_object_tree_t epg_brands; extern epg_object_tree_t epg_seasons; extern epg_object_tree_t epg_episodes; extern epg_object_tree_t epg_serieslinks; -static pthread_cond_t _epgdbsave_cond; -static void* _epgdbsave_thread ( void *p ); -pthread_mutex_t epgdbsave_mutex; /* ************************************************************************** * Load @@ -139,29 +136,6 @@ static void _epgdb_v2_process ( htsmsg_t *m, epggrab_stats_t *stats ) } } -/* - * Thread functions - */ -static void *_epgdbsave_thread ( void *p ) -{ - struct timespec ts; - ts.tv_nsec = 0; - tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", epggrab_epgdb_periodicsave); - while (1) { - if (epggrab_epgdb_periodicsave != 0) { - tvhlog(LOG_DEBUG, "epgdb", "epgdbsave setting: %i", - epggrab_epgdb_periodicsave); - epg_save(); - }; - pthread_mutex_lock(&epgdbsave_mutex); - time(&ts.tv_sec); - ts.tv_sec += epggrab_epgdb_periodicsave * 3600; /* User defined in hours */ - pthread_cond_timedwait(&_epgdbsave_cond, &epgdbsave_mutex, &ts); - pthread_mutex_unlock(&epgdbsave_mutex); - }; -return NULL; -} - /* * Load data */ @@ -253,13 +227,6 @@ void epg_init ( void ) /* Close file */ munmap(mem, st.st_size); close(fd); - - /* Create thread */ - pthread_mutex_init(&epgdbsave_mutex, NULL); - pthread_cond_init(&_epgdbsave_cond, NULL); - /* Start thread */ - pthread_t tid; - pthread_create(&tid, NULL, _epgdbsave_thread, NULL); } /* ************************************************************************** @@ -296,13 +263,17 @@ static int _epg_write_sect ( int fd, const char *sect ) return _epg_write(fd, m); } -void epg_save ( void ) +void epg_save ( void *p ) { int fd; epg_object_t *eo; epg_broadcast_t *ebc; channel_t *ch; epggrab_stats_t stats; + extern gtimer_t epggrab_save_timer; + + if (epggrab_epgdb_periodicsave) + gtimer_arm(&epggrab_save_timer, epg_save, NULL, epggrab_epgdb_periodicsave); fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION); diff --git a/src/epggrab.c b/src/epggrab.c index 18b97e91..8d110376 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -38,9 +38,9 @@ #include "service.h" /* Thread protection */ -static int epggrab_confver; +static int epggrab_confver; pthread_mutex_t epggrab_mutex; -static pthread_cond_t epggrab_cond; +static pthread_cond_t epggrab_cond; /* Config */ uint32_t epggrab_interval; @@ -51,6 +51,8 @@ uint32_t epggrab_channel_renumber; uint32_t epggrab_channel_reicon; uint32_t epggrab_epgdb_periodicsave; +gtimer_t epggrab_save_timer; + /* ************************************************************************** * Internal Grab Thread * *************************************************************************/ @@ -140,6 +142,9 @@ static void _epggrab_load ( void ) htsmsg_get_u32(m, "channel_renumber", &epggrab_channel_renumber); htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon); htsmsg_get_u32(m, "epgdb_periodicsave", &epggrab_epgdb_periodicsave); + if (epggrab_epgdb_periodicsave) + gtimer_arm(&epggrab_save_timer, epg_save, NULL, + epggrab_epgdb_periodicsave); if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval", &epggrab_interval)) { if (old) epggrab_interval *= 3600; @@ -310,6 +315,12 @@ int epggrab_set_periodicsave ( uint32_t e ) int save = 0; if ( e != epggrab_epgdb_periodicsave ) { epggrab_epgdb_periodicsave = e; + pthread_mutex_lock(&global_lock); + if (!e) + gtimer_disarm(&epggrab_save_timer); + else + epg_save(NULL); // will arm the timer + pthread_mutex_unlock(&global_lock); save = 1; } return save; @@ -359,6 +370,14 @@ void epggrab_resched ( void ) */ void epggrab_init ( void ) { + /* Defaults */ + epggrab_interval = 0; + epggrab_module = NULL; + epggrab_channel_rename = 0; + epggrab_channel_renumber = 0; + epggrab_channel_reicon = 0; + epggrab_epgdb_periodicsave = 0; + /* Lists */ #if ENABLE_LINUXDVB extern TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all; diff --git a/src/main.c b/src/main.c index 15d0100a..7ac61715 100644 --- a/src/main.c +++ b/src/main.c @@ -686,11 +686,15 @@ main(int argc, char **argv) mainloop(); - epg_save(); + // Note: the locking is obviously a bit redundant, but without + // we need to disable the gtimer_arm call in epg_save() + pthread_mutex_lock(&global_lock); + epg_save(NULL); #if ENABLE_TIMESHIFT timeshift_term(); #endif + pthread_mutex_unlock(&global_lock); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 07673914..6e793562 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -589,7 +589,7 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_u32(r, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(r, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(r, "channel_reicon", epggrab_channel_reicon); - htsmsg_add_u32(r, "epgdb_periodicsave", epggrab_epgdb_periodicsave); + htsmsg_add_u32(r, "epgdb_periodicsave", epggrab_epgdb_periodicsave / 3600); pthread_mutex_unlock(&epggrab_mutex); out = json_single_record(r, "epggrabSettings"); @@ -621,7 +621,7 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) str = http_arg_get(&hc->hc_req_args, "channel_reicon"); save |= epggrab_set_channel_reicon(str ? 1 : 0); if ( (str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave")) ) - save |= epggrab_set_periodicsave(atoi(str)); + save |= epggrab_set_periodicsave(atoi(str) * 3600); if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) save |= epggrab_set_interval(atoi(str)); if ( (str = http_arg_get(&hc->hc_req_args, "module")) ) diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index 36dcda10..30f87699 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -483,7 +483,9 @@ page_epgsave(http_connection_t *hc, htsbuf_qprintf(hq, "\n" "1\n"); - epg_save(); + pthread_mutex_lock(&global_lock); + epg_save(NULL); + pthread_mutex_unlock(&global_lock); http_output_content(hc, "text/xml"); From 033495993ecf9f85f8ce9afbc908c4987cc37e11 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Mar 2013 10:56:09 +0000 Subject: [PATCH 429/503] cwc: cryptoworks - remove double malloc in the emm code --- src/cwc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cwc.c b/src/cwc.c index ebafd4ff..9c7e90dc 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -1816,7 +1816,6 @@ cwc_emm_cryptoworks(cwc_t *cwc, uint8_t *data, int len) if (cwc->cwc_cryptoworks_emm.shared_emm) { free(cwc->cwc_cryptoworks_emm.shared_emm); cwc->cwc_cryptoworks_emm.shared_len = 0; - cwc->cwc_cryptoworks_emm.shared_emm = (uint8_t *)malloc(len); } cwc->cwc_cryptoworks_emm.shared_emm = malloc(len); if (cwc->cwc_cryptoworks_emm.shared_emm) { From 1508fd6166b77965e65e1ea210bd84051b17d7aa Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Mar 2013 17:15:39 +0000 Subject: [PATCH 430/503] serviceprobe: add NULL ptr check on dvb mux to be safe --- src/serviceprobe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/serviceprobe.c b/src/serviceprobe.c index 2d82a713..f542ca33 100644 --- a/src/serviceprobe.c +++ b/src/serviceprobe.c @@ -108,7 +108,10 @@ serviceprobe_thread(void *aux) was_doing_work = 1; } - checksubscr = !t->s_dvb_mux_instance->tdmi_adapter->tda_skip_checksubscr; + if (t->s_dvb_mux_instance) + checksubscr = !t->s_dvb_mux_instance->tdmi_adapter->tda_skip_checksubscr; + else + checksubscr = 1; if (checksubscr) { tvhlog(LOG_INFO, "serviceprobe", "%20s: checking...", From a0a7539a731c6e84b8e0a3a95b1c1655a12515c7 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Mar 2013 17:26:05 +0000 Subject: [PATCH 431/503] Fix #1444 - dvr: ensure that DVR entries are not removed when EPG is updated previously an overlapping (replacement) event could result in the DVR entry being completely removed. This will now ensure that the original entry is left in place (with just info and times). It will also try and re-match with an EPG entry as and when it can. --- src/dvr/dvr.h | 2 + src/dvr/dvr_db.c | 113 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 32 deletions(-) diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 4bc56aa7..59d285cc 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -137,6 +137,8 @@ typedef struct dvr_entry { lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */ epg_genre_t de_content_type; /* Content type (from EPG) */ + uint16_t de_dvb_eid; + dvr_prio_t de_pri; uint32_t de_dont_reschedule; diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index d9cfe026..ecc2e0da 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -250,6 +250,39 @@ dvr_entry_link(dvr_entry_t *de) htsp_dvr_entry_add(de); } +/** + * Find dvr entry using 'fuzzy' search + */ +static int +dvr_entry_fuzzy_match(dvr_entry_t *de, epg_broadcast_t *e) +{ + time_t t1, t2; + const char *title1, *title2; + + /* Matching ID */ + if (de->de_dvb_eid && de->de_dvb_eid == e->dvb_eid) + return 1; + + /* No title */ + if (!(title1 = epg_broadcast_get_title(e, NULL))) + return 0; + if (!(title2 = lang_str_get(de->de_title, NULL))) + return 0; + + /* Wrong length (+/-20%) */ + t1 = de->de_stop - de->de_start; + t2 = e->stop - e->start; + if ( abs(t2 - t1) > (t1 / 5) ) + return 0; + + /* Outside of window (should it be configurable)? */ + if ( abs(e->start - de->de_start) > 86400 ) + return 0; + + /* Title match (or contains?) */ + return strcmp(title1, title2) == 0; +} + /** * Create the event */ @@ -301,6 +334,7 @@ static dvr_entry_t *_dvr_entry_create ( de->de_desc = NULL; // TODO: this really needs updating if (e) { + de->de_dvb_eid = e->dvb_eid; if (e->episode && e->episode->title) de->de_title = lang_str_copy(e->episode->title); if (e->description) @@ -478,7 +512,7 @@ dvr_db_load_one(htsmsg_t *c, int id) dvr_entry_t *de; const char *chname, *s, *creator; channel_t *ch; - uint32_t start, stop, bcid; + uint32_t start, stop, bcid, u32; int d; dvr_config_t *cfg; lang_str_t *title, *ls; @@ -519,6 +553,8 @@ dvr_db_load_one(htsmsg_t *c, int id) de->de_creator = strdup(creator); de->de_title = title; de->de_pri = dvr_pri2val(htsmsg_get_str(c, "pri")); + if (!htsmsg_get_u32(c, "dvb_eid", &u32)) + de->de_dvb_eid = (uint16_t)u32; if(htsmsg_get_s32(c, "start_extra", &d)) if (ch && ch->ch_dvr_extra_time_pre) @@ -619,6 +655,9 @@ dvr_entry_save(dvr_entry_t *de) lang_str_serialize(de->de_title, m, "title"); + if(de->de_dvb_eid) + htsmsg_add_u32(m, "dvb_eid", de->de_dvb_eid); + if(de->de_desc != NULL) lang_str_serialize(de->de_desc, m, "description"); @@ -696,6 +735,12 @@ static dvr_entry_t *_dvr_entry_update if (!de->de_title) de->de_title = lang_str_create(); save = lang_str_add(de->de_title, title, lang, 1); } + + /* EID */ + if (e && e->dvb_eid != de->de_dvb_eid) { + de->de_dvb_eid = e->dvb_eid; + save = 1; + } // TODO: description @@ -710,7 +755,8 @@ static dvr_entry_t *_dvr_entry_update /* Broadcast */ if (e && (de->de_bcast != e)) { - de->de_bcast->putref(de->de_bcast); + if (de->de_bcast) + de->de_bcast->putref(de->de_bcast); de->de_bcast = e; e->getref(e); save = 1; @@ -744,23 +790,33 @@ dvr_entry_update /** * Used to notify the DVR that an event has been replaced in the EPG - * - * TODO: I think this will record the title slot event if its now a - * completely different episode etc... */ void dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) { - dvr_entry_t *de, *ude; + dvr_entry_t *de; + assert(e != NULL); + assert(new_e != NULL); + + /* Ignore */ if ( e == new_e ) return; - de = dvr_entry_find_by_event(e); - if (de != NULL) { - ude = dvr_entry_find_by_event_fuzzy(new_e); - if (ude == NULL && de->de_sched_state == DVR_SCHEDULED) - dvr_entry_cancel(de); - else if(new_e->episode && new_e->episode->title) - _dvr_entry_update(de, new_e, NULL, NULL, NULL, 0, 0, 0, 0); + /* Existing entry */ + if ((de = dvr_entry_find_by_event(e))) { + + /* Unlink the broadcast */ + e->putref(e); + de->de_bcast = NULL; + + /* Find match */ + RB_FOREACH(e, &e->channel->ch_epg_schedule, sched_link) { + if (dvr_entry_fuzzy_match(de, e)) { + e->getref(e); + de->de_bcast = e; + _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); + break; + } + } } } @@ -768,7 +824,18 @@ void dvr_event_updated ( epg_broadcast_t *e ) { dvr_entry_t *de; de = dvr_entry_find_by_event(e); - if (de) _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); + if (de) + _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); + else { + LIST_FOREACH(de, &dvrentries, de_global_link) { + if (dvr_entry_fuzzy_match(de, e)) { + e->getref(e); + de->de_bcast = e; + _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); + break; + } + } + } } /** @@ -863,24 +930,6 @@ dvr_entry_find_by_event(epg_broadcast_t *e) return NULL; } -/** - * Find dvr entry using 'fuzzy' search - */ -dvr_entry_t * -dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e) -{ - dvr_entry_t *de; - - if (!e->episode || !e->episode->title) - return NULL; - - LIST_FOREACH(de, &e->channel->ch_dvrs, de_channel_link) - if ((abs(de->de_start - e->start) < 600) && (abs(de->de_stop - e->stop) < 600)) { - return de; - } - return NULL; -} - /* * Find DVR entry based on an episode */ From 55ed28cb53307f77c7c767cd6f87f56b15951bcd Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 25 Jan 2013 10:47:20 +0000 Subject: [PATCH 432/503] Fix #1643 - dvb: close dvr device on each re-tune --- src/dvb/dvb.h | 2 ++ src/dvb/dvb_adapter.c | 30 ++++++++++++++++++------------ src/dvb/dvb_fe.c | 1 + 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index b065467f..aaccd946 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -346,6 +346,8 @@ void dvb_adapter_start (th_dvb_adapter_t *tda); void dvb_adapter_stop (th_dvb_adapter_t *tda); +void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); + void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 861c1117..8b3878dc 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -715,6 +715,22 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } } +void +dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) +{ + /* Stop DVR thread */ + if (tda->tda_dvr_pipe.rd != -1) { + tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); + int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); + assert(!err); + pthread_join(tda->tda_dvr_thread, NULL); + close(tda->tda_dvr_pipe.rd); + close(tda->tda_dvr_pipe.wr); + tda->tda_dvr_pipe.rd = -1; + tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); + } +} + void dvb_adapter_stop ( th_dvb_adapter_t *tda ) { @@ -731,18 +747,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) tda->tda_fe_fd = -1; } - /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { - tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); - assert(!err); - pthread_join(tda->tda_dvr_thread, NULL); - close(tda->tda_dvr_pipe.rd); - close(tda->tda_dvr_pipe.wr); - tda->tda_dvr_pipe.rd = -1; - tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); - } - + dvb_adapter_stop_dvr(tda); + dvb_adapter_notify(tda); } diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 73a8cd0a..c21303e0 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -285,6 +285,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) dvb_mux_save(tdmi); } + dvb_adapter_stop_dvr(tda); dvb_table_flush_all(tdmi); assert(tdmi->tdmi_scan_queue == NULL); From aac905c18488e90ebb467de9c3e2eece4f1fd5ac Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 4 Feb 2013 17:04:53 +0000 Subject: [PATCH 433/503] dvb: fix problems with dvb satconf processing and storage --- src/dvb/dvb_multiplex.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index a5ac0d4d..ff615580 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -102,14 +102,17 @@ tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) */ static int tdmi_compare_key(const struct dvb_mux_conf *a, - const struct dvb_mux_conf *b) + const struct dvb_mux_conf *b, + const dvb_satconf_t *satconf) { int32_t fd = (int32_t)a->dmc_fe_params.frequency - (int32_t)b->dmc_fe_params.frequency; + if (!satconf) + satconf = b->dmc_satconf; fd = labs(fd); return fd < 2000 && a->dmc_polarisation == b->dmc_polarisation && - a->dmc_satconf == b->dmc_satconf; + a->dmc_satconf == satconf; } @@ -162,9 +165,12 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, lock_assert(&global_lock); + if (!satconf) + satconf = dmc->dmc_satconf; + /* HACK - we hash/compare based on 2KHz spacing and compare on +/-500Hz */ LIST_FOREACH(tdmi, &tda->tda_mux_list, tdmi_adapter_hash_link) { - if(tdmi_compare_key(&tdmi->tdmi_conf, dmc)) + if(tdmi_compare_key(&tdmi->tdmi_conf, dmc, satconf)) break; /* Mux already exist */ } @@ -248,9 +254,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, snprintf(buf, sizeof(buf), "%s%d%s%s%s", tda->tda_identifier, dmc->dmc_fe_params.frequency, qpsktxt, - (satconf || dmc->dmc_satconf) ? "_satconf_" : "", - (satconf ? satconf->sc_id : - (dmc->dmc_satconf ? dmc->dmc_satconf->sc_id : ""))); + satconf ? "_satconf_" : "", + satconf ? satconf->sc_id : ""); tdmi->tdmi_identifier = strdup(buf); } else { From 6c6e0541be21fc574241c3c244f2b28c24db2021 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Mar 2013 21:56:11 +0000 Subject: [PATCH 434/503] dvr: ensure unlinked entry updates are properly handled previously it updated things that were already linked and also on different channels entirely. --- src/dvr/dvr_db.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index ecc2e0da..a91f216f 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -828,6 +828,8 @@ void dvr_event_updated ( epg_broadcast_t *e ) _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); else { LIST_FOREACH(de, &dvrentries, de_global_link) { + if (de->de_bcast) continue; + if (de->de_channel != e->channel) continue; if (dvr_entry_fuzzy_match(de, e)) { e->getref(e); de->de_bcast = e; From ab7302ec99587576f76d22d03bd6853769868432 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 8 Mar 2013 21:59:37 +0000 Subject: [PATCH 435/503] dvr: another minor improvement to epg/dvr relinking Realised that we don't need to update things that are no longer scheduled (i.e. complete). --- src/dvr/dvr_db.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index a91f216f..100400c2 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -828,6 +828,7 @@ void dvr_event_updated ( epg_broadcast_t *e ) _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); else { LIST_FOREACH(de, &dvrentries, de_global_link) { + if (de->de_sched_state != DVR_SCHEDULED) continue; if (de->de_bcast) continue; if (de->de_channel != e->channel) continue; if (dvr_entry_fuzzy_match(de, e)) { From 271165a4c54d05f0f4dcdea6c50efff012715467 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Fri, 8 Mar 2013 21:42:30 +0000 Subject: [PATCH 436/503] opentv: hack to include episode/series number processing from description. --- src/epggrab/module/opentv.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index e1fd6ee3..0d245d3a 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "tvheadend.h" #include "dvb/dvb.h" @@ -397,6 +398,25 @@ static int _opentv_parse_event_section save |= epg_episode_set_genre(ee, egl, src); epg_genre_list_destroy(egl); } + if (ev.summary) { + regex_t preg; + regmatch_t match[3]; + + /* Parse Series/Episode + * TODO: HACK: this needs doing properly */ + regcomp(&preg, " *\\(S ?([0-9]+),? Ep? ?([0-9]+)\\)$", + REG_ICASE | REG_EXTENDED); + if (!regexec(&preg, ev.summary, 3, match, 0)) { + epg_episode_num_t en; + memset(&en, 0, sizeof(en)); + if (match[1].rm_so != -1) + en.s_num = atoi(ev.summary + match[1].rm_so); + if (match[2].rm_so != -1) + en.e_num = atoi(ev.summary + match[2].rm_so); + save |= epg_episode_set_epnum(ee, &en, src); + } + regfree(&preg); + } } /* Cleanup */ From 1c9e85b326874b7a6178fd73c28feae0aed3318b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 10 Mar 2013 19:18:21 +0000 Subject: [PATCH 437/503] dvb: fix mistake in dvr close commit (wasn't restarting thread) --- src/dvb/dvb_fe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index c21303e0..77f76217 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -434,8 +434,7 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if(tda->tda_mux_current != NULL) dvb_fe_stop(tda->tda_mux_current, 1); - else - dvb_adapter_start(tda); + dvb_adapter_start(tda); if(tda->tda_type == FE_QPSK) { From 819b8c83948becde08a6fdfad0228fd7607693ca Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 10 Mar 2013 19:18:48 +0000 Subject: [PATCH 438/503] Fix #1657 - timeshift: add constant length qualifier to avoid warning on some arch's --- src/timeshift/timeshift_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index a89bd810..742a04a2 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -550,7 +550,7 @@ void *timeshift_reader ( void *p ) streaming_msg_free(sm); /* Find end */ - skip_time = 0x7fffffffffffffff; + skip_time = 0x7fffffffffffffffLL; // TODO: change this sometime! } break; From 84b22640b29403f0b21d983c38f4eeeceb9aaded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Wed, 13 Mar 2013 14:56:24 +0100 Subject: [PATCH 439/503] libav: fix memory leak when converting packets to annex-b format --- src/muxer/muxer_libav.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 7837afe6..e5b1d746 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -397,6 +397,10 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) return -1; } + // h264_mp4toannexb filter might allocate new data. + if(packet.data != pktbuf_ptr(pkt->pkt_payload)) + av_free(packet.data); + break; } From 484b3f6d23971144fa538ae9172a772a6bbd307a Mon Sep 17 00:00:00 2001 From: Stefan Saraev Date: Wed, 13 Mar 2013 21:30:55 +0200 Subject: [PATCH 440/503] cwc: syslog: move some log messages to debug --- src/capmt.c | 4 ++-- src/cwc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/capmt.c b/src/capmt.c index ac507bd5..9e12e869 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -547,7 +547,7 @@ handle_ca0(capmt_t* capmt) { #endif if(ct->ct_keystate != CT_RESOLVED) - tvhlog(LOG_INFO, "capmt", "Obtained key for service \"%s\"",t->s_svcname); + tvhlog(LOG_DEBUG, "capmt", "Obtained key for service \"%s\"",t->s_svcname); ct->ct_keystate = CT_RESOLVED; } @@ -851,7 +851,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t, cce->cce_ecmsize = len; if(ct->ct_keystate != CT_RESOLVED) - tvhlog(LOG_INFO, "capmt", + tvhlog(LOG_DEBUG, "capmt", "Trying to obtain key for service \"%s\"",t->s_svcname); buf[9] = pmtversion; diff --git a/src/cwc.c b/src/cwc.c index ebafd4ff..904213f0 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -873,7 +873,7 @@ forbid: } if(ct->cs_keystate != CS_RESOLVED) - tvhlog(LOG_INFO, "cwc", + tvhlog(LOG_DEBUG, "cwc", "Obtained key for service \"%s\" in %"PRId64" ms, from %s:%i", t->s_svcname, delay, ct->cs_cwc->cwc_hostname, ct->cs_cwc->cwc_port); From 07cdfdaf6af06ae08a779ccc9082a517757c6fd3 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 12 Mar 2013 15:35:21 +0000 Subject: [PATCH 441/503] Fix #1660 - dvb: stop constant updating of dvb mux configuration files --- src/dvb/dvb_multiplex.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index ff615580..692ebf1b 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -58,9 +58,6 @@ static struct strtab muxfestatustab[] = { { "OK", TDMI_FE_OK }, }; -static void tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled); - - /** * */ @@ -856,6 +853,9 @@ dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) if (!networkname || !*networkname) return; + if (!strcmp(tdmi->tdmi_network ?: "", networkname)) + return; + free(tdmi->tdmi_network); tdmi->tdmi_network = strdup(networkname); dvb_mux_save(tdmi); @@ -879,8 +879,9 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force) if (tdmi->tdmi_transport_stream_id != 0xFFFF || tsid == 0xFFFF) return; - tdmi->tdmi_transport_stream_id = tsid; - + if (tdmi->tdmi_transport_stream_id == tsid) + return; + dvb_mux_save(tdmi); m = htsmsg_create_map(); @@ -900,6 +901,9 @@ dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force) if (force) if (tdmi->tdmi_network_id != 0 || onid == 0) return; + + if (tdmi->tdmi_network_id == onid) + return; tdmi->tdmi_network_id = onid; @@ -915,13 +919,13 @@ dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force) /** * */ -static void +static int tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; if(tdmi->tdmi_enabled == enabled) - return; + return 0; if(tdmi->tdmi_enabled) { @@ -940,6 +944,7 @@ tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) mux_link_initial(tda, tdmi); subscription_reschedule(); + return 1; } /** @@ -948,8 +953,8 @@ tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) void dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) { - tdmi_set_enable(tdmi, enabled); - dvb_mux_save(tdmi); + if (tdmi_set_enable(tdmi, enabled)) + dvb_mux_save(tdmi); } From 94a3af4c98f89f8045b764f02820ec023cc03f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 08:44:39 +0100 Subject: [PATCH 442/503] libav: added missing marker function --- src/muxer/muxer_libav.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index e5b1d746..451bb63f 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -420,6 +420,16 @@ lav_muxer_write_meta(muxer_t *m, struct epg_broadcast *eb) } +/** + * NOP + */ +static int +lav_muxer_add_marker(muxer_t* m) +{ + return 0; +} + + /** * Close the muxer and append trailer to output */ @@ -498,6 +508,7 @@ lav_muxer_create(muxer_container_type_t mc) lm->m_init = lav_muxer_init; lm->m_reconfigure = lav_muxer_reconfigure; lm->m_mime = lav_muxer_mime; + lm->m_add_marker = lav_muxer_add_marker; lm->m_write_meta = lav_muxer_write_meta; lm->m_write_pkt = lav_muxer_write_pkt; lm->m_close = lav_muxer_close; From 266dfee3a53450c62cde3ed2438c5bacad1de449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 08:47:46 +0100 Subject: [PATCH 443/503] libav: make sure we don't write a trailer if the header has not been written --- src/muxer/muxer_libav.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 451bb63f..2f549823 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -33,6 +33,7 @@ typedef struct lav_muxer { AVFormatContext *lm_oc; AVBitStreamFilterContext *lm_h264_filter; int lm_fd; + int lm_init; } lav_muxer_t; #define MUX_BUF_SIZE 4096 @@ -276,6 +277,8 @@ lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) return -1; } + lm->lm_init = 1; + return 0; } @@ -357,6 +360,12 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) return -1; } + if(!lm->lm_init) { + tvhlog(LOG_ERR, "libav", "Muxer not initialized correctly"); + lm->m_errors++; + return -1; + } + for(i=0; inb_streams; i++) { st = oc->streams[i]; @@ -440,7 +449,7 @@ lav_muxer_close(muxer_t *m) int ret = 0; lav_muxer_t *lm = (lav_muxer_t*)m; - if(lm->lm_oc->nb_streams && av_write_trailer(lm->lm_oc) < 0) { + if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) { tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer", muxer_container_type2txt(lm->m_container)); lm->m_errors++; @@ -517,6 +526,7 @@ lav_muxer_create(muxer_container_type_t mc) lm->lm_oc = avformat_alloc_context(); lm->lm_oc->oformat = fmt; lm->lm_fd = -1; + lm->lm_init = 0; return (muxer_t*)lm; } From 35ad10b1ab6fe2c9dcd41b5442aca45bf6611f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 08:48:15 +0100 Subject: [PATCH 444/503] libav: cosmetics --- src/muxer/muxer_libav.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 2f549823..495260f6 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -355,7 +355,7 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) oc = lm->lm_oc; if(!oc->nb_streams) { - tvhlog(LOG_ERR, "libav", "No streams to mux"); + tvhlog(LOG_ERR, "libav", "No streams to mux"); lm->m_errors++; return -1; } From ba9c4fd1f45dd20b6629f9baad684a0294c8c791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 09:21:32 +0100 Subject: [PATCH 445/503] libav: print warning if bitstream filter fails --- src/muxer/muxer_libav.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 495260f6..fbfa534d 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -378,14 +378,15 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) pkt = pkt_merge_header(pkt); if(lm->lm_h264_filter && st->codec->codec_id == CODEC_ID_H264) { - av_bitstream_filter_filter(lm->lm_h264_filter, + if(av_bitstream_filter_filter(lm->lm_h264_filter, st->codec, NULL, &packet.data, &packet.size, pktbuf_ptr(pkt->pkt_payload), pktbuf_len(pkt->pkt_payload), - pkt->pkt_frametype < PKT_P_FRAME); + pkt->pkt_frametype < PKT_P_FRAME) < 0) + tvhlog(LOG_WARNING, "libav", "Failed to filter bitstream"); } else { packet.data = pktbuf_ptr(pkt->pkt_payload); packet.size = pktbuf_len(pkt->pkt_payload); From e622db159f44a31fd82fc182a6f45642a9f2934a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 14:25:02 +0100 Subject: [PATCH 446/503] libav: fixed minor memory leak when errors accurs. --- src/muxer/muxer_libav.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index fbfa534d..f804e652 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -349,6 +349,7 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) AVPacket packet; th_pkt_t *pkt = (th_pkt_t*)data; lav_muxer_t *lm = (lav_muxer_t*)m; + int rc = 0; assert(smt == SMT_PACKET); @@ -356,14 +357,14 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) if(!oc->nb_streams) { tvhlog(LOG_ERR, "libav", "No streams to mux"); - lm->m_errors++; - return -1; + rc = -1; + goto ret; } if(!lm->lm_init) { tvhlog(LOG_ERR, "libav", "Muxer not initialized correctly"); - lm->m_errors++; - return -1; + rc = -1; + goto ret; } for(i=0; inb_streams; i++) { @@ -401,11 +402,8 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) if(pkt->pkt_frametype < PKT_P_FRAME) packet.flags |= AV_PKT_FLAG_KEY; - if (av_interleaved_write_frame(oc, &packet) != 0) { - tvhlog(LOG_WARNING, "libav", "Failed to write frame"); - lm->m_errors++; - return -1; - } + if((rc = av_interleaved_write_frame(oc, &packet))) + tvhlog(LOG_WARNING, "libav", "Failed to write frame"); // h264_mp4toannexb filter might allocate new data. if(packet.data != pktbuf_ptr(pkt->pkt_payload)) @@ -414,9 +412,11 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) break; } + ret: + lm->m_errors += (rc != 0); pkt_ref_dec(pkt); - return 0; + return rc; } From 5e7950275daca9a58b6c94a51565cb1c205e4100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Sun, 17 Mar 2013 14:28:53 +0100 Subject: [PATCH 447/503] libav: drop h264 packets when bitstream filter fails --- src/muxer/muxer_libav.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index f804e652..c8c12470 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -386,8 +386,10 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) &packet.size, pktbuf_ptr(pkt->pkt_payload), pktbuf_len(pkt->pkt_payload), - pkt->pkt_frametype < PKT_P_FRAME) < 0) + pkt->pkt_frametype < PKT_P_FRAME) < 0) { tvhlog(LOG_WARNING, "libav", "Failed to filter bitstream"); + break; + } } else { packet.data = pktbuf_ptr(pkt->pkt_payload); packet.size = pktbuf_len(pkt->pkt_payload); From ddc466c1bfa7405563a68a662114f5a3659d7cb5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 13 Mar 2013 19:36:59 +0000 Subject: [PATCH 448/503] Fix #1643 - dvb: alter the way we handle dvb tuning This ensures that demux filters are not installed until after tuning is locked. This should resolve most stale data issues. --- src/dvb/dvb.h | 14 +++- src/dvb/dvb_adapter.c | 169 +++++++++++++++++++++++++++--------------- src/dvb/dvb_fe.c | 25 +++++-- src/dvb/dvb_service.c | 11 ++- 4 files changed, 144 insertions(+), 75 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index aaccd946..335ac3c0 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -29,6 +29,12 @@ struct service; struct th_dvb_table; struct th_dvb_mux_instance; +#define TDA_OPT_FE 0x1 +#define TDA_OPT_DVR 0x2 +#define TDA_OPT_DMX 0x4 +#define TDA_OPT_PWR 0x8 +#define TDA_OPT_ALL (TDA_OPT_FE | TDA_OPT_DVR | TDA_OPT_DMX | TDA_OPT_PWR) + #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) #define DVB_VER_ATLEAST(maj, min) \ @@ -202,6 +208,8 @@ typedef struct th_dvb_adapter { uint32_t tda_enabled; + int tda_locked; + const char *tda_rootpath; char *tda_identifier; uint32_t tda_autodiscovery; @@ -342,11 +350,9 @@ void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile); void dvb_adapter_mux_scanner(void *aux); -void dvb_adapter_start (th_dvb_adapter_t *tda); +void dvb_adapter_start (th_dvb_adapter_t *tda, int opt); -void dvb_adapter_stop (th_dvb_adapter_t *tda); - -void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); +void dvb_adapter_stop (th_dvb_adapter_t *tda, int opt); void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8b3878dc..02b3fb8b 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -141,7 +141,7 @@ dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on) gtimer_disarm(&tda->tda_mux_scanner_timer); if (tda->tda_mux_current) dvb_fe_stop(tda->tda_mux_current, 0); - dvb_adapter_stop(tda); + dvb_adapter_stop(tda, TDA_OPT_ALL); } else { tda_init(tda); } @@ -692,22 +692,26 @@ static void tda_init (th_dvb_adapter_t *tda) * */ void -dvb_adapter_start ( th_dvb_adapter_t *tda ) +dvb_adapter_start ( th_dvb_adapter_t *tda, int opt ) { if(tda->tda_enabled == 0) { tvhlog(LOG_INFO, "dvb", "Adapter \"%s\" cannot be started - it's disabled", tda->tda_displayname); return; } + + /* Default to ALL */ + if (!opt) + opt = TDA_OPT_ALL; /* Open front end */ - if (tda->tda_fe_fd == -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd == -1)) { tda->tda_fe_fd = tvh_open(tda->tda_fe_path, O_RDWR | O_NONBLOCK, 0); if (tda->tda_fe_fd == -1) return; tvhlog(LOG_DEBUG, "dvb", "%s opened frontend %s", tda->tda_rootpath, tda->tda_fe_path); } /* Start DVR thread */ - if (tda->tda_dvr_pipe.rd == -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd == -1)) { int err = tvh_pipe(O_NONBLOCK, &tda->tda_dvr_pipe); assert(err != -1); pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); @@ -716,10 +720,14 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } void -dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) +dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt ) { + /* Poweroff */ + if (opt & TDA_OPT_PWR) + dvb_adapter_poweroff(tda); + /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); assert(!err); @@ -729,26 +737,17 @@ dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) tda->tda_dvr_pipe.rd = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } -} -void -dvb_adapter_stop ( th_dvb_adapter_t *tda ) -{ - /* Poweroff */ - dvb_adapter_poweroff(tda); - - /* Don't stop/close */ + /* Don't close FE */ if (!tda->tda_idleclose && tda->tda_enabled) return; /* Close front end */ - if (tda->tda_fe_fd != -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s closing frontend", tda->tda_rootpath); close(tda->tda_fe_fd); tda->tda_fe_fd = -1; } - dvb_adapter_stop_dvr(tda); - dvb_adapter_notify(tda); } @@ -980,8 +979,39 @@ dvb_adapter_clean(th_dvb_adapter_t *tda) service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); } +/** + * Install RAW PES filter + */ +static int +dvb_adapter_raw_filter(th_dvb_adapter_t *tda) +{ + int dmx = -1; + struct dmx_pes_filter_params dmx_param; + dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(dmx == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_demux_path, strerror(errno)); + return -1; + } + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = 0x2000; + dmx_param.input = DMX_IN_FRONTEND; + dmx_param.output = DMX_OUT_TS_TAP; + dmx_param.pes_type = DMX_PES_OTHER; + dmx_param.flags = DMX_IMMEDIATE_START; + + if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param) == -1) { + tvhlog(LOG_ERR, "dvb", + "Unable to configure demuxer \"%s\" for all PIDs -- %s", + tda->tda_demux_path, strerror(errno)); + close(dmx); + return -1; + } + + return dmx; +} /** * @@ -990,54 +1020,18 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r, c, efd, nfds, dmx = -1; + th_dvb_mux_instance_t *tdmi; + int fd = -1, i, r, c, efd, nfds, dmx = -1; uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; - - fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); - if(fd == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", tda->tda_dvr_path, strerror(errno)); - return NULL; - } - - if(tda->tda_rawmode) { - - // Receive unfiltered raw transport stream - - dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); - if(dmx == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", - tda->tda_demux_path, strerror(errno)); - close(fd); - return NULL; - } - - struct dmx_pes_filter_params dmx_param; - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = 0x2000; - dmx_param.input = DMX_IN_FRONTEND; - dmx_param.output = DMX_OUT_TS_TAP; - dmx_param.pes_type = DMX_PES_OTHER; - dmx_param.flags = DMX_IMMEDIATE_START; - - if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "Unable to configure demuxer \"%s\" for all PIDs -- %s", - tda->tda_demux_path, strerror(errno)); - close(dmx); - close(fd); - return NULL; - } - } + int delay = 10, locked = 0; + fe_status_t festat; /* Create poll */ efd = epoll_create(2); memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = fd; - epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); ev.data.fd = tda->tda_dvr_pipe.rd; epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev); @@ -1045,10 +1039,65 @@ dvb_adapter_input_dvr(void *aux) while(1) { /* Wait for input */ - nfds = epoll_wait(efd, &ev, 1, -1); - if (nfds < 1) continue; - if (ev.data.fd != fd) break; + nfds = epoll_wait(efd, &ev, 1, delay); + /* Exit */ + if ((nfds > 0) && (ev.data.fd != fd)) break; + + /* Check for lock */ + if (!locked) { + if (ioctl(tda->tda_fe_fd, FE_READ_STATUS, &festat)) + continue; + if (!(festat & FE_HAS_LOCK)) + continue; + + /* Open DVR */ + fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); + if (fd == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_dvr_path, strerror(errno)); + break; + } + ev.data.fd = fd; + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); + + /* Note: table handlers must be installed with global lock */ + pthread_mutex_lock(&global_lock); + tda->tda_locked = locked = 1; + delay = -1; + if ((tdmi = tda->tda_mux_current)) { + + /* Install table handlers */ + dvb_table_add_default(tdmi); + epggrab_mux_start(tdmi); + + /* Raw filter */ + if(tda->tda_rawmode) + dmx = dvb_adapter_raw_filter(tda); + + /* Service filters */ + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_FOREACH(t, &tda->tda_transports, s_active_link) { + if (t->s_dvb_mux_instance == tdmi) { + tda->tda_open_service(tda, t); + dvb_table_add_pmt(tdmi, t->s_pmt_pid); + } + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); + } + pthread_mutex_unlock(&global_lock); + + /* Error */ + if (tda->tda_rawmode && (dmx == -1)) { + tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter"); + break; + } + } + + /* No data */ + if (nfds < 1) continue; + + /* Read data */ c = read(fd, tsb+r, sizeof(tsb)-r); if (c < 0) { if (errno == EAGAIN || errno == EINTR) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 77f76217..d0924c69 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -273,6 +273,7 @@ void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + dvb_table_feed_t *dtf; lock_assert(&global_lock); @@ -285,8 +286,13 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) dvb_mux_save(tdmi); } - dvb_adapter_stop_dvr(tda); + dvb_adapter_stop(tda, TDA_OPT_DVR); + pthread_mutex_lock(&tda->tda_delivery_mutex); + while((dtf = TAILQ_FIRST(&tda->tda_table_feed))) + TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link); + pthread_mutex_unlock(&tda->tda_delivery_mutex); dvb_table_flush_all(tdmi); + tda->tda_locked = 0; assert(tdmi->tdmi_scan_queue == NULL); @@ -300,7 +306,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) if (!retune) { gtimer_disarm(&tda->tda_fe_monitor_timer); - dvb_adapter_stop(tda); + dvb_adapter_stop(tda, TDA_OPT_ALL); } } @@ -421,9 +427,11 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) char buf[256]; int r; - lock_assert(&global_lock); + if(tda->tda_enabled == 0) + return SM_CODE_TUNING_FAILED; + if(tda->tda_mux_current == tdmi) return 0; @@ -434,7 +442,8 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if(tda->tda_mux_current != NULL) dvb_fe_stop(tda->tda_mux_current, 1); - dvb_adapter_start(tda); + + dvb_adapter_start(tda, TDA_OPT_FE | TDA_OPT_PWR); if(tda->tda_type == FE_QPSK) { @@ -486,7 +495,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) tda->tda_fe_monitor_hold = 4; - #if DVB_API_VERSION >= 5 if (tda->tda_type == FE_QPSK) { tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, " @@ -517,15 +525,18 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if (errno == EINVAL) dvb_mux_set_enable(tdmi, 0); return SM_CODE_TUNING_FAILED; - } + } tda->tda_mux_current = tdmi; + dvb_adapter_start(tda, TDA_OPT_ALL); + gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); - +#if 0 dvb_table_add_default(tdmi); epggrab_mux_start(tdmi); +#endif dvb_adapter_notify(tda); return 0; diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 61242bef..31e25e64 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -86,18 +86,21 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) dvb_adapter_clean(tda); } + r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); + pthread_mutex_lock(&tda->tda_delivery_mutex); - r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); if(!r) LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); pthread_mutex_unlock(&tda->tda_delivery_mutex); - if(!r) - tda->tda_open_service(tda, t); + if (tda->tda_locked) { + if(!r) + tda->tda_open_service(tda, t); - dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + } return r; } From babe15958e235ee9ec7b12bf455a908b95532936 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 19 Mar 2013 14:42:48 +0000 Subject: [PATCH 449/503] support: remove confusing (and uncessary) default values. --- support/configure.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/support/configure.inc b/support/configure.inc index 9ff2a1e8..9b745c03 100755 --- a/support/configure.inc +++ b/support/configure.inc @@ -175,12 +175,12 @@ function show_help val=${opt#*:} opt=${opt%:*} if [ "$val" == "yes" ]; then - printf " $fmt Disable ${opt} [${val}]\n" "--disable-${opt}" + printf " $fmt Disable ${opt}\n" "--disable-${opt}" elif [ "$val" == "no" ]; then - printf " $fmt Enable ${opt} [${val}]\n" "--enable-${opt}" + printf " $fmt Enable ${opt}\n" "--enable-${opt}" else - printf " $fmt Disable ${opt} [${val}]\n" "--disable-${opt}" - printf " $fmt Enable ${opt} [${val}]\n" "--enable-${opt}" + printf " $fmt Disable ${opt}\n" "--disable-${opt}" + printf " $fmt Enable ${opt}\n" "--enable-${opt}" fi done exit 0 From 4f2f68a4b65a9ab5ecce80c867b9d7813de665a3 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 22 Mar 2013 10:50:03 +0000 Subject: [PATCH 450/503] gtimer: increased resolution of the gtimer system. This is needed to resolve a dvb tuner deadlock issued caused by my re-jigging of the tuning setup code. --- src/main.c | 116 ++++++++++++++++++++++++++++++++++++------------ src/tvheadend.h | 8 +++- 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/main.c b/src/main.c index 7ac61715..4b135f38 100644 --- a/src/main.c +++ b/src/main.c @@ -152,6 +152,7 @@ static int log_debug_to_syslog; static int log_debug_to_console; static int log_debug_to_path; static char* log_path; +static pthread_cond_t gtimer_cond; static void handle_sigpipe(int x) @@ -187,31 +188,50 @@ get_user_groups (const struct passwd *pw, gid_t* glist, size_t gmax) static int gtimercmp(gtimer_t *a, gtimer_t *b) { - if(a->gti_expire < b->gti_expire) + if(a->gti_expire.tv_sec < b->gti_expire.tv_sec) return -1; - else if(a->gti_expire > b->gti_expire) + if(a->gti_expire.tv_sec > b->gti_expire.tv_sec) return 1; - return 0; + if(a->gti_expire.tv_nsec < b->gti_expire.tv_nsec) + return -1; + if(a->gti_expire.tv_nsec > b->gti_expire.tv_nsec) + return 1; + return -1; } - /** * */ void -gtimer_arm_abs(gtimer_t *gti, gti_callback_t *callback, void *opaque, - time_t when) +gtimer_arm_abs2 + (gtimer_t *gti, gti_callback_t *callback, void *opaque, struct timespec *when) { lock_assert(&global_lock); - if(gti->gti_callback != NULL) + if (gti->gti_callback != NULL) LIST_REMOVE(gti, gti_link); - + gti->gti_callback = callback; - gti->gti_opaque = opaque; - gti->gti_expire = when; + gti->gti_opaque = opaque; + gti->gti_expire = *when; LIST_INSERT_SORTED(>imers, gti, gti_link, gtimercmp); + + if (LIST_FIRST(>imers) == gti) + pthread_cond_signal(>imer_cond); // force timer re-check +} + +/** + * + */ +void +gtimer_arm_abs + (gtimer_t *gti, gti_callback_t *callback, void *opaque, time_t when) +{ + struct timespec ts; + ts.tv_nsec = 0; + ts.tv_sec = when; + gtimer_arm_abs2(gti, callback, opaque, &ts); } /** @@ -220,10 +240,22 @@ gtimer_arm_abs(gtimer_t *gti, gti_callback_t *callback, void *opaque, void gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque, int delta) { - time_t now; - time(&now); - - gtimer_arm_abs(gti, callback, opaque, now + delta); + gtimer_arm_abs(gti, callback, opaque, dispatch_clock + delta); +} + +/** + * + */ +void +gtimer_arm_ms + (gtimer_t *gti, gti_callback_t *callback, void *opaque, long delta_ms ) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + ts.tv_nsec += (1000000 * delta_ms); + ts.tv_sec += (ts.tv_nsec / 1000000000); + ts.tv_nsec %= 1000000000; + gtimer_arm_abs2(gti, callback, opaque, &ts); } /** @@ -305,28 +337,51 @@ mainloop(void) { gtimer_t *gti; gti_callback_t *cb; + struct timespec ts; while(running) { - sleep(1); - spawn_reaper(); + clock_gettime(CLOCK_REALTIME, &ts); + //tvhlog(LOG_INFO, "main", "loop"); - time(&dispatch_clock); + /* 1sec stuff */ + if (ts.tv_sec > dispatch_clock) { + dispatch_clock = ts.tv_sec; - comet_flush(); /* Flush idle comet mailboxes */ + spawn_reaper(); /* reap spawned processes */ + comet_flush(); /* Flush idle comet mailboxes */ + } + + /* Global timers */ pthread_mutex_lock(&global_lock); + + // TODO: there is a risk that if timers re-insert themselves to + // the top of the list with a 0 offset we could loop indefinitely while((gti = LIST_FIRST(>imers)) != NULL) { - if(gti->gti_expire > dispatch_clock) - break; - + if ((gti->gti_expire.tv_sec > ts.tv_sec) || + ((gti->gti_expire.tv_sec == ts.tv_sec) && + (gti->gti_expire.tv_nsec > ts.tv_nsec))) { + ts = gti->gti_expire; + break; + } + cb = gti->gti_callback; + LIST_REMOVE(gti, gti_link); gti->gti_callback = NULL; cb(gti->gti_opaque); - } + + /* Bound wait */ + if ((LIST_FIRST(>imers) == NULL) || (ts.tv_sec > (dispatch_clock + 1))) { + ts.tv_sec = dispatch_clock + 1; + ts.tv_nsec = 0; + } + + /* Wait */ + pthread_cond_timedwait(>imer_cond, &global_lock, &ts); pthread_mutex_unlock(&global_lock); } } @@ -584,6 +639,7 @@ main(int argc, char **argv) pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_mutex_lock(&global_lock); + pthread_cond_init(>imer_cond, NULL); time(&dispatch_clock); @@ -726,11 +782,12 @@ tvhlogv(int notify, int severity, const char *subsys, const char *fmt, { char buf[2048]; char buf2[2048]; - char t[50]; - int l; + char t[64]; + int l, ms; struct tm tm; - time_t now; + struct timeval now; static int log_path_fail = 0; + size_t c; l = snprintf(buf, sizeof(buf), "%s: ", subsys); @@ -742,16 +799,17 @@ tvhlogv(int notify, int severity, const char *subsys, const char *fmt, /** * Get time (string) */ - time(&now); - localtime_r(&now, &tm); - strftime(t, sizeof(t), "%b %d %H:%M:%S", &tm); + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &tm); + ms = now.tv_usec / 1000; + c = strftime(t, sizeof(t), "%b %d %H:%M:%S", &tm); + snprintf(t+c, sizeof(t)-c, ".%03d", ms); /** * Send notification to Comet (Push interface to web-clients) */ if(notify) { htsmsg_t *m; - snprintf(buf2, sizeof(buf2), "%s %s", t, buf); m = htsmsg_create_map(); htsmsg_add_str(m, "notificationClass", "logmessage"); diff --git a/src/tvheadend.h b/src/tvheadend.h index 9e6d137a..01b4141b 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -113,15 +113,21 @@ typedef struct gtimer { LIST_ENTRY(gtimer) gti_link; gti_callback_t *gti_callback; void *gti_opaque; - time_t gti_expire; + struct timespec gti_expire; } gtimer_t; void gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque, int delta); +void gtimer_arm_ms(gtimer_t *gti, gti_callback_t *callback, void *opaque, + long delta_ms); + void gtimer_arm_abs(gtimer_t *gti, gti_callback_t *callback, void *opaque, time_t when); +void gtimer_arm_abs2(gtimer_t *gti, gti_callback_t *callback, void *opaque, + struct timespec *when); + void gtimer_disarm(gtimer_t *gti); From d75b99a610f4cab4c703f2f2dda5f84141524202 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 22 Mar 2013 12:12:29 +0000 Subject: [PATCH 451/503] Fix #1666 - dvb: rework the adapter tuning to stop possible deadlock --- src/dvb/dvb.h | 2 +- src/dvb/dvb_adapter.c | 76 +++++++++------------------------ src/dvb/dvb_fe.c | 97 ++++++++++++++----------------------------- 3 files changed, 52 insertions(+), 123 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 335ac3c0..3b3e8f1f 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -209,6 +209,7 @@ typedef struct th_dvb_adapter { uint32_t tda_enabled; int tda_locked; + time_t tda_monitor; const char *tda_rootpath; char *tda_identifier; @@ -250,7 +251,6 @@ typedef struct th_dvb_adapter { struct service_list tda_transports; /* Currently bound transports */ gtimer_t tda_fe_monitor_timer; - int tda_fe_monitor_hold; int tda_sat; // Set if this adapter is a satellite receiver (DVB-S, etc) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 02b3fb8b..79b6ea0a 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -1020,13 +1020,25 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - th_dvb_mux_instance_t *tdmi; int fd = -1, i, r, c, efd, nfds, dmx = -1; uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; - int delay = 10, locked = 0; - fe_status_t festat; + int delay = 10; + + /* Install RAW demux */ + if (tda->tda_rawmode) { + if ((dmx = dvb_adapter_raw_filter(tda)) == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter"); + return NULL; + } + } + + /* Open DVR */ + if ((fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0)) == -1) { + close(dmx); + return NULL; + } /* Create poll */ efd = epoll_create(2); @@ -1034,6 +1046,8 @@ dvb_adapter_input_dvr(void *aux) ev.events = EPOLLIN; ev.data.fd = tda->tda_dvr_pipe.rd; epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev); + ev.data.fd = fd; + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); r = i = 0; while(1) { @@ -1041,62 +1055,12 @@ dvb_adapter_input_dvr(void *aux) /* Wait for input */ nfds = epoll_wait(efd, &ev, 1, delay); - /* Exit */ - if ((nfds > 0) && (ev.data.fd != fd)) break; - - /* Check for lock */ - if (!locked) { - if (ioctl(tda->tda_fe_fd, FE_READ_STATUS, &festat)) - continue; - if (!(festat & FE_HAS_LOCK)) - continue; - - /* Open DVR */ - fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); - if (fd == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", - tda->tda_dvr_path, strerror(errno)); - break; - } - ev.data.fd = fd; - epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); - - /* Note: table handlers must be installed with global lock */ - pthread_mutex_lock(&global_lock); - tda->tda_locked = locked = 1; - delay = -1; - if ((tdmi = tda->tda_mux_current)) { - - /* Install table handlers */ - dvb_table_add_default(tdmi); - epggrab_mux_start(tdmi); - - /* Raw filter */ - if(tda->tda_rawmode) - dmx = dvb_adapter_raw_filter(tda); - - /* Service filters */ - pthread_mutex_lock(&tda->tda_delivery_mutex); - LIST_FOREACH(t, &tda->tda_transports, s_active_link) { - if (t->s_dvb_mux_instance == tdmi) { - tda->tda_open_service(tda, t); - dvb_table_add_pmt(tdmi, t->s_pmt_pid); - } - } - pthread_mutex_unlock(&tda->tda_delivery_mutex); - } - pthread_mutex_unlock(&global_lock); - - /* Error */ - if (tda->tda_rawmode && (dmx == -1)) { - tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter"); - break; - } - } - /* No data */ if (nfds < 1) continue; + /* Exit */ + if (ev.data.fd != fd) break; + /* Read data */ c = read(fd, tsb+r, sizeof(tsb)-r); if (c < 0) { diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index d0924c69..4b339952 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -101,8 +101,6 @@ dvb_fe_monitor(void *aux) int store = 0; int notify = 0; - gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); - if(tdmi == NULL) return; @@ -110,9 +108,8 @@ dvb_fe_monitor(void *aux) * Read out front end status */ if(ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status)) - fe_status = 0; - - if(fe_status & FE_HAS_LOCK) + status = TDMI_FE_UNKNOWN; + else if(fe_status & FE_HAS_LOCK) status = -1; else if(fe_status & (FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_CARRIER)) status = TDMI_FE_BAD_SIGNAL; @@ -121,18 +118,34 @@ dvb_fe_monitor(void *aux) else status = TDMI_FE_NO_SIGNAL; - if(tda->tda_fe_monitor_hold > 0) { - /* Post tuning threshold */ - if(status == -1) { /* We have a lock, don't hold off */ - tda->tda_fe_monitor_hold = 0; - /* Reset FEC counter */ - dvb_fe_get_unc(tda); + /** + * Waiting for initial lock + */ + if(tda->tda_locked == 0) { + + /* Read */ + if (status == -1) { + tda->tda_locked = 1; + dvb_adapter_start(tda, TDA_OPT_ALL); + gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); + + /* Re-arm (50ms) */ } else { - tda->tda_fe_monitor_hold--; - return; + gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50); + + /* Monitor (1 per sec) */ + if (dispatch_clock < tda->tda_monitor) + return; + tda->tda_monitor = dispatch_clock + 1; } + + } else { + gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); } + /* + * Update stats + */ if(status == -1) { /* Read FEC counter (delta) */ @@ -312,46 +325,6 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) #if DVB_API_VERSION >= 5 -static int check_frontend (int fe_fd, int dvr, int human_readable) { - (void)dvr; - fe_status_t status; - uint16_t snr, signal; - uint32_t ber; - int timeout = 0; - - do { - if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1) - perror("FE_READ_STATUS failed"); - /* some frontends might not support all these ioctls, thus we - * avoid printing errors - */ - if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1) - signal = -2; - if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1) - snr = -2; - if (ioctl(fe_fd, FE_READ_BER, &ber) == -1) - ber = -2; - - if (human_readable) { - printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | ", - status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber); - } else { - printf ("status %02x | signal %04x | snr %04x | ber %08x | ", - status, signal, snr, ber); - } - if (status & FE_HAS_LOCK) - printf("FE_HAS_LOCK"); - printf("\n"); - - if ((status & FE_HAS_LOCK) || (++timeout >= 10)) - break; - - usleep(1000000); - } while (1); - - return 0; -} - static struct dtv_property clear_p[] = { { .cmd = DTV_CLEAR }, }; @@ -361,7 +334,6 @@ static struct dtv_properties clear_cmdseq = { .props = clear_p }; - /** * */ @@ -404,8 +376,6 @@ dvb_fe_tune_s2(th_dvb_mux_instance_t *tdmi, dvb_mux_conf_t *dmc) /* do tuning now */ r = ioctl(tda->tda_fe_fd, FE_SET_PROPERTY, &_dvbs_cmdseq); - if(0) - check_frontend (tda->tda_fe_fd, 0, 1); return r; } @@ -493,8 +463,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) dvb_mux_nicename(buf, sizeof(buf), tdmi); - tda->tda_fe_monitor_hold = 4; - #if DVB_API_VERSION >= 5 if (tda->tda_type == FE_QPSK) { tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, " @@ -524,19 +492,16 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) /* Mark as bad */ if (errno == EINVAL) dvb_mux_set_enable(tdmi, 0); + + dvb_adapter_stop(tda, TDA_OPT_ALL); return SM_CODE_TUNING_FAILED; } tda->tda_mux_current = tdmi; - dvb_adapter_start(tda, TDA_OPT_ALL); - - gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); - -#if 0 - dvb_table_add_default(tdmi); - epggrab_mux_start(tdmi); -#endif + time(&tda->tda_monitor); + tda->tda_monitor += 4; // wait a few secs before monitoring (unlocked) + gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50); dvb_adapter_notify(tda); return 0; From 43326e2d4580d7d3a8af815c212ea2317d82544d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 27 Mar 2013 09:13:32 +0000 Subject: [PATCH 452/503] Fix #1674 - remove spurious (info) log message before daemon(). --- src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.c b/src/main.c index 4b135f38..41c16bba 100644 --- a/src/main.c +++ b/src/main.c @@ -527,7 +527,6 @@ main(int argc, char **argv) log_debug_to_syslog = opt_syslog; log_debug_to_path = opt_debug; tvheadend_webui_debug = opt_debug || opt_uidebug; - tvhlog(LOG_INFO, "START", "initialising"); #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; From 4e7506abbed9faa65d9230ca9b406469403e94d9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 27 Mar 2013 09:45:24 +0000 Subject: [PATCH 453/503] support: some updates to build system --- support/{launchpad-ppa => apt-update} | 32 ++++++++++++++++++--------- support/dput.cf | 21 ++++++++++++++++++ support/pbuilder | 2 +- 3 files changed, 44 insertions(+), 11 deletions(-) rename support/{launchpad-ppa => apt-update} (60%) create mode 100644 support/dput.cf diff --git a/support/launchpad-ppa b/support/apt-update similarity index 60% rename from support/launchpad-ppa rename to support/apt-update index bda6c26e..0d192779 100755 --- a/support/launchpad-ppa +++ b/support/apt-update @@ -14,10 +14,11 @@ function die # CMD CMD=$(basename "$0") +DIR=$(cd $(dirname "$0"); pwd) # Configuration TVH_ROOT=$(cd "$(dirname "$0")"/..; pwd) -[ -z "$TVH_DIST" ] && TVH_DIST="lucid natty oneiric precise quantal" +[ -z "$TVH_DIST" ] && TVH_DIST="wheezy lucid natty oneiric precise quantal" [ -z "$TVH_ARCH" ] && TVH_ARCH="i386 amd64" # Options @@ -27,25 +28,35 @@ TVH_ROOT=$(cd "$(dirname "$0")"/..; pwd) # Setup cd "$TVH_ROOT" || exit 1 NOW=`date -R` -CHANGELOG=$TVH_ROOT/debian/changelog -VERFILE=$TVH_ROOT/src/version.c +CHANGELOG=./debian/changelog +VERFILE=./src/version.c # Checkout git checkout $REL || die "could not checkout $REL" -git clean -dfx || die "could not clean git tree" -# Create version file -VER=$("$TVH_ROOT/support/version" $VERFILE) +# Get version +VER=$("./support/version" $VERFILE) + +# Export git tree +TMPDIR=/tmp/$CMD-$$ +trap "rm -rf $TMPDIR" EXIT +mkdir -p "$TMPDIR" +git archive --prefix=tvheadend/ HEAD | tar -C "${TMPDIR}" -x ||\ + die "failed to archive git tree" +cd "$TMPDIR/tvheadend" || die "failed to enter archived tree" # Fetch scan files ./support/getmuxlist || die "failed to fetch dvb-scan files" +cd .. # For each distro for d in $TVH_DIST; do V=${VER}~${d} + mv tvheadend "tvheadend-${V}" + cd "tvheadend-${V}" # Create changelog - $TVH_ROOT/support/changelog "$CHANGELOG" "$d" "$VER" || exit 1 + ./support/changelog "$CHANGELOG" "$d" "$VER" || exit 1 # Build source package dpkg-buildpackage -I.git* -S -sgpg -pgpg || exit 1 @@ -59,10 +70,11 @@ for d in $TVH_DIST; do # Upload else + [ ! -f "$HOME/.dput.cf" ] && DPUT_OPT="$DPUT_OPT -c $DIR/dput.cf" dput $DPUT_OPT tvh-${PPA} ../tvheadend_${V}_source.changes || exit 1 fi + # Rename back + cd .. + mv "tvheadend-${V}" tvheadend done - -# Cleanup -git checkout . diff --git a/support/dput.cf b/support/dput.cf new file mode 100644 index 00000000..600781aa --- /dev/null +++ b/support/dput.cf @@ -0,0 +1,21 @@ +# +# Tvheadend PPAs +# + +[tvh-unstable] +fqdn = apt.tvheadend.org +method = scp +incoming = /srv/reprepro/unstable/incoming +allow_unsigned_uploads = 0 + +[tvh-beta] +fqdn = apt.tvheadend.org +method = scp +incoming = /srv/reprepro/beta/incoming +allow_unsigned_uploads = 0 + +[tvh-stable] +fqdn = apt.tvheadend.org +method = scp +incoming = /srv/reprepro/stable/incoming +allow_unsigned_uploads = 0 diff --git a/support/pbuilder b/support/pbuilder index 5cd9a3a8..cba50a26 120000 --- a/support/pbuilder +++ b/support/pbuilder @@ -1 +1 @@ -launchpad-ppa \ No newline at end of file +apt-update \ No newline at end of file From 21e1e548caed34efb7b90aa650d06f12797d5f30 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 28 Mar 2013 10:59:48 +0000 Subject: [PATCH 454/503] gtimer: fix live lock problem caused by wrong clock usage --- src/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 41c16bba..34fcff0a 100644 --- a/src/main.c +++ b/src/main.c @@ -251,7 +251,7 @@ gtimer_arm_ms (gtimer_t *gti, gti_callback_t *callback, void *opaque, long delta_ms ) { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += (1000000 * delta_ms); ts.tv_sec += (ts.tv_nsec / 1000000000); ts.tv_nsec %= 1000000000; @@ -341,7 +341,6 @@ mainloop(void) while(running) { clock_gettime(CLOCK_REALTIME, &ts); - //tvhlog(LOG_INFO, "main", "loop"); /* 1sec stuff */ if (ts.tv_sec > dispatch_clock) { From e9ce02138e81d0dd94a2b155e57fda282063c008 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 28 Mar 2013 11:03:05 +0000 Subject: [PATCH 455/503] dvb: demux setup code went missing! --- src/dvb/dvb_fe.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 4b339952..99c431d9 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -129,6 +129,20 @@ dvb_fe_monitor(void *aux) dvb_adapter_start(tda, TDA_OPT_ALL); gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); + /* Install table handlers */ + dvb_table_add_default(tdmi); + epggrab_mux_start(tdmi); + + /* Service filters */ + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_FOREACH(t, &tda->tda_transports, s_active_link) { + if (t->s_dvb_mux_instance == tdmi) { + tda->tda_open_service(tda, t); + dvb_table_add_pmt(tdmi, t->s_pmt_pid); + } + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); + /* Re-arm (50ms) */ } else { gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50); From 23f788ff2b354fe1812ebaf2d934330fe0ff9404 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 3 Apr 2013 10:00:46 +0100 Subject: [PATCH 456/503] dvb: fix problem stopping DVB-S muxes being added on DVB v3 systems --- src/dvb/dvb_adapter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 79b6ea0a..0ce3c66c 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -1272,16 +1272,16 @@ dvb_fe_opts(th_dvb_adapter_t *tda, const char *which) return a; } -#if DVB_API_VERSION >= 5 if(!strcmp(which, "delsys")) { +#if DVB_API_VERSION >= 5 if(c & FE_CAN_QPSK) { fe_opts_add(a, "SYS_DVBS", SYS_DVBS); fe_opts_add(a, "SYS_DVBS2", SYS_DVBS2); } else +#endif fe_opts_add(a, "SYS_UNDEFINED", SYS_UNDEFINED); return a; } -#endif if(!strcmp(which, "transmissionmodes")) { if(c & FE_CAN_TRANSMISSION_MODE_AUTO) From 8bceacd96966211eddbd5cb1784e0f3864451483 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 4 Apr 2013 15:46:46 +0100 Subject: [PATCH 457/503] Fix #1679 - correct mistake in previous changes to stop constant IO TSID was not being properly updated, which could result in missing services. --- src/dvb/dvb_multiplex.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 692ebf1b..f9985fd0 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -882,6 +882,8 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force) if (tdmi->tdmi_transport_stream_id == tsid) return; + tdmi->tdmi_transport_stream_id = tsid; + dvb_mux_save(tdmi); m = htsmsg_create_map(); @@ -898,7 +900,7 @@ dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force) { htsmsg_t *m; - if (force) + if (!force) if (tdmi->tdmi_network_id != 0 || onid == 0) return; From 63b35164bdf03ae6d1296b3e5d7c16281165e0ca Mon Sep 17 00:00:00 2001 From: BtbN Date: Sat, 6 Apr 2013 05:14:51 +0200 Subject: [PATCH 458/503] capmt: fix capmt for multi-frontend adapters --- src/capmt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/capmt.c b/src/capmt.c index 9e12e869..341abce5 100644 --- a/src/capmt.c +++ b/src/capmt.c @@ -620,6 +620,8 @@ capmt_thread(void *aux) #if ENABLE_LINUXDVB th_dvb_adapter_t *tda; TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + if (!tda->tda_enabled) + continue; if (tda->tda_rootpath) { //if rootpath is NULL then can't rely on tda_adapter_num because it is always 0 if (tda->tda_adapter_num > MAX_CA) { tvhlog(LOG_ERR, "capmt", "adapter number > MAX_CA"); From bd592fb913e76727a46033734af234307871f0ab Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 7 Apr 2013 13:09:55 +0100 Subject: [PATCH 459/503] debug: added new tvhtrace logging routine Note: this can be compile switch disabled (./configure --disable-trace) Note: to enable subsystem output use --trace X,Y,Z etc... (--trace all will enable the lot). Note: the only way to know what subsystems are valie is to read the code! Note: --trace implies -d --- configure | 1 + src/main.c | 43 +++++++++++++++++++++++++++++++++++++++++-- src/tvheadend.h | 12 ++++++------ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/configure b/configure index 84f73154..198e4d1b 100755 --- a/configure +++ b/configure @@ -21,6 +21,7 @@ OPTIONS=( "linuxdvb:yes" "dvbscan:yes" "timeshift:yes" + "trace:yes" "imagecache:auto" "avahi:auto" "zlib:auto" diff --git a/src/main.c b/src/main.c index 34fcff0a..bc82bb70 100644 --- a/src/main.c +++ b/src/main.c @@ -151,6 +151,7 @@ static LIST_HEAD(, gtimer) gtimers; static int log_debug_to_syslog; static int log_debug_to_console; static int log_debug_to_path; +static htsmsg_t* log_debug_trace; static char* log_path; static pthread_cond_t gtimer_cond; @@ -404,6 +405,7 @@ main(int argc, char **argv) log_debug_to_syslog = 0; log_debug_to_console = 0; log_debug_to_path = 0; + log_debug_trace = NULL; log_path = NULL; tvheadend_webui_port = 9981; tvheadend_webroot = NULL; @@ -429,6 +431,7 @@ main(int argc, char **argv) *opt_dvb_adapters = NULL, *opt_dvb_raw = NULL, #endif + *opt_trace = NULL, *opt_rawts = NULL, *opt_bindaddr = NULL, *opt_subscribe = NULL; @@ -470,6 +473,9 @@ main(int argc, char **argv) { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, { 'l', "log", "Log to file", OPT_STR, &log_path }, +#if ENABLE_TRACE + { 0, "trace", "Enable low level debug", OPT_STR, &opt_trace }, +#endif { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, @@ -522,10 +528,25 @@ main(int argc, char **argv) } /* Additional cmdline processing */ + opt_debug |= (opt_trace != NULL); log_debug_to_console = opt_debug; log_debug_to_syslog = opt_syslog; log_debug_to_path = opt_debug; tvheadend_webui_debug = opt_debug || opt_uidebug; + if (opt_trace) { + log_debug_trace = htsmsg_create_map(); + htsmsg_add_u32(log_debug_trace, "START", 1); + uint32_t u32; + char *trace = strdup(opt_trace); + char *p, *r = NULL; + p = strtok_r(trace, ",", &r); + while (p) { + if (htsmsg_get_u32(log_debug_trace, p, &u32)) + htsmsg_add_u32(log_debug_trace, p, 1); + p = strtok_r(NULL, ",", &r); + } + free(trace); + } #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; @@ -545,12 +566,11 @@ main(int argc, char **argv) adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } + free(dvb_adapters); if (!adapter_mask) { tvhlog(LOG_ERR, "START", "No adapters specified!"); - free(dvb_adapters); return 1; } - free(dvb_adapters); } #endif if (tvheadend_webroot) { @@ -734,6 +754,7 @@ main(int argc, char **argv) "running as PID:%d UID:%d GID:%d, settings located in '%s'", tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root()); + tvhtrace("START", "TRACE debug enabled"); if(opt_abort) abort(); @@ -865,6 +886,24 @@ tvhlog(int severity, const char *subsys, const char *fmt, ...) va_end(ap); } +/** + * TVH trace + */ +#ifdef ENABLE_TRACE +void +tvhtrace(const char *subsys, const char *fmt, ...) +{ + va_list ap; + if (!log_debug_trace) + return; + if (!htsmsg_get_u32_or_default(log_debug_trace, "all", 0)) + if (!htsmsg_get_u32_or_default(log_debug_trace, subsys, 0)) + return; + va_start(ap, fmt); + tvhlogv(0, LOG_DEBUG, subsys, fmt, ap); + va_end(ap); +} +#endif /** * May be invoked from a forked process so we can't do any notification diff --git a/src/tvheadend.h b/src/tvheadend.h index 01b4141b..deab273d 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -455,14 +455,14 @@ void tvhlog_spawn(int severity, const char *subsys, const char *fmt, ...) #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ +#ifndef ENABLE_TRACE +#define tvhtrace(...) ((void)0) +#else +void tvhtrace(const char *subsys, const char *fmt, ...); +#endif + extern int log_debug; -#define DEBUGLOG(subsys, fmt...) do { \ - if(log_debug) \ - tvhlog(LOG_DEBUG, subsys, fmt); \ -} while(0) - - #ifndef CLOCK_MONOTONIC_COARSE #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC #endif From e7b220471d305f50454281bfd41d804f3a644907 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 7 Apr 2013 13:34:48 +0100 Subject: [PATCH 460/503] debug: convert all TRACE code to new tvhtrace routine --- src/dvb/diseqc.c | 14 ++++------ src/dvb/dvb_tables.c | 41 ++++++++++----------------- src/epg.c | 42 ++++++++++------------------ src/epggrab/module/eit.c | 46 ++++++++++++++----------------- src/timeshift/timeshift_filemgr.c | 22 ++++----------- src/timeshift/timeshift_reader.c | 36 ++++++++---------------- src/tvhtime.c | 8 ++---- 7 files changed, 73 insertions(+), 136 deletions(-) diff --git a/src/dvb/diseqc.c b/src/dvb/diseqc.c index 1c49ff22..7b736f35 100644 --- a/src/dvb/diseqc.c +++ b/src/dvb/diseqc.c @@ -22,10 +22,8 @@ diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, { struct dvb_diseqc_master_cmd message; -#if DISEQC_TRACE - tvhlog(LOG_DEBUG, "diseqc", "sending %X %X %X %X %X %X", - framing_byte, address, cmd, data_1, data_2, data_3); -#endif + tvhtrace("diseqc", "sending %X %X %X %X %X %X", + framing_byte, address, cmd, data_1, data_2, data_3); message.msg[0] = framing_byte; message.msg[1] = address; @@ -45,11 +43,9 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, int j = lnb_num / 4; int k, err; -#if DISEQC_TRACE - tvhlog(LOG_DEBUG, "diseqc", - "fe_fd %i, lnb_num %i, voltage %i, band %i, version %i, repeats %i", - fe_fd, lnb_num, voltage, band, version, repeats); -#endif + tvhtrace("diseqc", + "fe_fd %i, lnb_num %i, voltage %i, band %i, version %i, repeats %i", + fe_fd, lnb_num, voltage, band, version, repeats); /* verify lnb number and diseqc data */ if(lnb_num < 0 || lnb_num >=64 || i < 0 || i >= 16 || j < 0 || j >= 16) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 64ca69fa..f2bc4922 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -44,14 +44,6 @@ #include "cwc.h" #include "tvhtime.h" -#if TDT_TRACE -#define TRACE(_pre, _fmt, ...)\ -tvhlog(LOG_DEBUG, "tdt-"_pre, _fmt, __VA_ARGS__) -#else -#define TRACE(_pre, _fmt, ...) (void)0 -#endif - - /** * */ @@ -330,7 +322,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, char provider[256]; char chname0[256], *chname; uint8_t stype; -#if TDT_TRACE +#if ENABLE_TRACE uint8_t running_status; #endif int l; @@ -355,7 +347,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, break; if (!tdmi) return -1; } - TRACE("sdt", "onid %04X tsid %04X", onid, tsid); + tvhtrace("sdt", "onid %04X tsid %04X", onid, tsid); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; @@ -375,13 +367,13 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, int save = 0; service_id = ptr[0] << 8 | ptr[1]; // reserved = ptr[2]; -#if TDT_TRACE +#if ENABLE_TRACE running_status = (ptr[3] >> 5) & 0x7; #endif free_ca_mode = (ptr[3] >> 4) & 0x1; dllen = ((ptr[3] & 0x0f) << 8) | ptr[4]; - TRACE("sdt", " sid %04X running %d free_ca %d", - service_id, running_status, free_ca_mode); + tvhtrace("sdt", " sid %04X running %d free_ca %d", + service_id, running_status, free_ca_mode); len -= 5; ptr += 5; @@ -406,8 +398,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(dvb_desc_service(ptr, dlen, &stype, provider, sizeof(provider), chname0, sizeof(chname0)) == 0) { - TRACE("sdt", " stype = %d, provider = %s, name = %s", - stype, provider, chname0); + tvhtrace("sdt", " stype = %d, provider = %s, name = %s", + stype, provider, chname0); chname = chname0; /* Some providers insert spaces. Clean up that (both heading and trailing) */ @@ -675,7 +667,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq * 100; - TRACE("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); + tvhtrace("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + @@ -719,7 +711,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]); dmc.dmc_fe_params.frequency = freq * 10; - TRACE("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); + tvhtrace("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); if(!freq) return -1; @@ -807,7 +799,7 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, return -1; dmc.dmc_fe_params.frequency = freq; - TRACE("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); + tvhtrace("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); dmc.dmc_fe_params.u.ofdm.bandwidth = bandwidth_tab[(ptr[4] & 0xe0) >> 5]; dmc.dmc_fe_params.u.ofdm.constellation=constellation_tab[(ptr[5] & 0xc0) >> 6]; dmc.dmc_fe_params.u.ofdm.hierarchy_information=hierarchy_info_tab[(ptr[5] & 0x38) >> 3]; @@ -878,10 +870,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint16_t network_id = (ptr[0] << 8) | ptr[1]; netname[0] = '\0'; - TRACE("nit", "tableid 0x%02x", tableid); -#if TDT_TRACE - hexdump("nit", ptr, len); -#endif + tvhtrace("nit", "tableid 0x%02x", tableid); /* Check NID */ if(tdmi->tdmi_adapter->tda_nitoid && @@ -903,7 +892,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dtag = ptr[0]; dlen = ptr[1]; - TRACE("nit", "dtag %02X dlen %d", dtag, dlen); + tvhtrace("nit", "dtag %02X dlen %d", dtag, dlen); switch(dtag) { case DVB_DESC_NETWORK_NAME: @@ -917,7 +906,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, ptr += dlen + 2; llen -= dlen + 2; } - TRACE("nit", "network %d/%s", network_id, netname); + tvhtrace("nit", "network %d/%s", network_id, netname); if (llen) return -1; @@ -932,7 +921,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, onid = ( ptr[2] << 8) | ptr[3]; llen = ((ptr[4] & 0xf) << 8) | ptr[5]; - TRACE("nit", " onid %04X tsid %04X", onid, tsid); + tvhtrace("nit", " onid %04X tsid %04X", onid, tsid); ptr += 6; len -= llen + 6; @@ -943,7 +932,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dtag = ptr[0]; dlen = ptr[1]; - TRACE("nit", " dtag %02X dlen %d", dtag, dlen); + tvhtrace("nit", " dtag %02X dlen %d", dtag, dlen); switch(dtag) { case DVB_DESC_SAT: diff --git a/src/epg.c b/src/epg.c index 697dae1f..89c40904 100644 --- a/src/epg.c +++ b/src/epg.c @@ -126,10 +126,8 @@ static void _epg_object_destroy ( epg_object_t *eo, epg_object_tree_t *tree ) { assert(eo->refcount == 0); -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] destroy", - eo, eo->id, eo->type, eo->uri); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] destroy", + eo, eo->id, eo->type, eo->uri); if (eo->uri) free(eo->uri); if (tree) RB_REMOVE(tree, eo, uri_link); if (eo->_updated) LIST_REMOVE(eo, up_link); @@ -139,10 +137,8 @@ static void _epg_object_destroy static void _epg_object_getref ( void *o ) { epg_object_t *eo = o; -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] getref %d", - eo, eo->id, eo->type, eo->uri, eo->refcount+1); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] getref %d", + eo, eo->id, eo->type, eo->uri, eo->refcount+1); if (eo->refcount == 0) LIST_REMOVE(eo, un_link); eo->refcount++; } @@ -150,10 +146,8 @@ static void _epg_object_getref ( void *o ) static void _epg_object_putref ( void *o ) { epg_object_t *eo = o; -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] putref %d", - eo, eo->id, eo->type, eo->uri, eo->refcount-1); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] putref %d", + eo, eo->id, eo->type, eo->uri, eo->refcount-1); assert(eo->refcount>0); eo->refcount--; if (!eo->refcount) eo->destroy(eo); @@ -163,10 +157,8 @@ static void _epg_object_set_updated ( void *o ) { epg_object_t *eo = o; if (!eo->_updated) { -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] updated", - eo, eo->id, eo->type, eo->uri); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] updated", + eo, eo->id, eo->type, eo->uri); eo->_updated = 1; eo->updated = dispatch_clock; LIST_INSERT_HEAD(&epg_object_updated, eo, up_link); @@ -180,10 +172,8 @@ static void _epg_object_create ( void *o ) else if (eo->id > _epg_object_idx) _epg_object_idx = eo->id; if (!eo->getref) eo->getref = _epg_object_getref; if (!eo->putref) eo->putref = _epg_object_putref; -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] created", - eo, eo->id, eo->type, eo->uri); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] created", + eo, eo->id, eo->type, eo->uri); _epg_object_set_updated(eo); LIST_INSERT_HEAD(&epg_object_unref, eo, un_link); LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, id_link); @@ -231,10 +221,8 @@ epg_object_t *epg_object_find_by_id ( uint32_t id, epg_object_type_t type ) static htsmsg_t * _epg_object_serialize ( void *o ) { epg_object_t *eo = o; -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] serialize", - eo, eo->id, eo->type, eo->uri); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] serialize", + eo, eo->id, eo->type, eo->uri); htsmsg_t *m; if ( !eo->id || !eo->type ) return NULL; m = htsmsg_create_map(); @@ -263,10 +251,8 @@ static epg_object_t *_epg_object_deserialize ( htsmsg_t *m, epg_object_t *eo ) _epg_object_set_updated(eo); eo->updated = s64; } -#ifdef EPG_TRACE - tvhlog(LOG_DEBUG, "epg", "eo [%p, %u, %d, %s] deserialize", - eo, eo->id, eo->type, eo->uri); -#endif + tvhtrace("epg", "eo [%p, %u, %d, %s] deserialize", + eo, eo->id, eo->type, eo->uri); return eo; } diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index c9169fb8..f610c0a4 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -533,10 +533,8 @@ static int _eit_process_event /* Find broadcast */ ebc = epg_broadcast_find_by_time(svc->s_ch, start, stop, eid, 1, &save2); -#ifdef EPG_EIT_TRACE - tvhlog(LOG_DEBUG, mod->id, "eid=%5d, start=%lu, stop=%lu, ebc=%p", + tvhtrace("eit", "eid=%5d, start=%lu, stop=%lu, ebc=%p", eid, start, stop, ebc); -#endif if (!ebc) return dllen + 12; /* Mark re-schedule detect (only now/next) */ @@ -698,20 +696,17 @@ static int _eit_callback lst = ptr[4]; seg = ptr[9]; ver = (ptr[2] >> 1) & 0x1f; -#ifdef EPG_EIT_TRACE - tvhlog(LOG_DEBUG, mod->id, - "tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X, sec=%3d/%3d, seg=%3d, ver=%2d, cur=%d", - tableid, onid, tsid, sid, sec, lst, seg, ver, ptr[2] & 1); -#endif + tvhtrace("eit", + "tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X" + ", sec=%3d/%3d, seg=%3d, ver=%2d, cur=%d", + tableid, onid, tsid, sid, sec, lst, seg, ver, ptr[2] & 1); /* Don't process */ if((ptr[2] & 1) == 0) return 0; /* Current status */ tsta = eit_status_find(sta, tableid, onid, tsid, sid, sec, lst, seg, ver); -#ifdef EPG_EIT_TRACE - tvhlog(LOG_DEBUG, mod->id, tsta && tsta->state != EIT_STATUS_DONE ? "section process" : "section seen"); -#endif + tvhtrace("eit", tsta && tsta->state != EIT_STATUS_DONE ? "section process" : "section seen"); if (!tsta) return 0; // already seen, no state change if (tsta->state == EIT_STATUS_DONE) goto done; @@ -724,12 +719,10 @@ static int _eit_callback } else { if (tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) { -#ifdef EPG_EIT_TRACE - tvhlog(LOG_DEBUG, mod->id, - "invalid transport id found tid 0x%02X, onid:tsid %d:%d != %d:%d", - tableid, tdmi->tdmi_network_id, tdmi->tdmi_transport_stream_id, - onid, tsid); -#endif + tvhtrace("eit", + "invalid tsid found tid 0x%02X, onid:tsid %d:%d != %d:%d", + tableid, tdmi->tdmi_network_id, tdmi->tdmi_transport_stream_id, + onid, tsid); tdmi = NULL; } } @@ -773,7 +766,7 @@ done: if (!tsta) epggrab_ota_complete(ota); } } -#ifdef EPG_EIT_TRACE +#if ENABLE_TRACE if (ota->state != EPGGRAB_OTA_MUX_COMPLETE) { int total = 0; @@ -781,16 +774,17 @@ done: tvhlog(LOG_DEBUG, mod->id, "scan status"); LIST_FOREACH(tsta, &sta->tables, link) { total++; - tvhlog(LOG_DEBUG, mod->id, - " tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X, ver=%02d, done=%d, " - "mask=%08X|%08X|%08X|%08X|%08X|%08X|%08X|%08X", - tsta->tid, tsta->onid, tsta->tsid, tsta->sid, tsta->ver, - tsta->state == EIT_STATUS_DONE, - tsta->sec[7], tsta->sec[6], tsta->sec[5], tsta->sec[4], - tsta->sec[3], tsta->sec[2], tsta->sec[1], tsta->sec[0]); + tvhtrace("eit", + " tid=0x%02X, onid=0x%04X, tsid=0x%04X, sid=0x%04X, ver=%02d" + ", done=%d, " + "mask=%08X|%08X|%08X|%08X|%08X|%08X|%08X|%08X", + tsta->tid, tsta->onid, tsta->tsid, tsta->sid, tsta->ver, + tsta->state == EIT_STATUS_DONE, + tsta->sec[7], tsta->sec[6], tsta->sec[5], tsta->sec[4], + tsta->sec[3], tsta->sec[2], tsta->sec[1], tsta->sec[0]); if (tsta->state == EIT_STATUS_DONE) finished++; } - tvhlog(LOG_DEBUG, mod->id, " completed %d of %d", finished, total); + tvhtrace("eit", " completed %d of %d", finished, total); } #endif diff --git a/src/timeshift/timeshift_filemgr.c b/src/timeshift/timeshift_filemgr.c index 56a4c2cd..c7debb9b 100644 --- a/src/timeshift/timeshift_filemgr.c +++ b/src/timeshift/timeshift_filemgr.c @@ -64,9 +64,7 @@ static void* timeshift_reaper_callback ( void *p ) TAILQ_REMOVE(×hift_reaper_list, tsf, link); pthread_mutex_unlock(×hift_reaper_lock); -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "remove file %s", tsf->path); -#endif + tvhtrace("timeshift", "remove file %s", tsf->path); /* Remove */ unlink(tsf->path); @@ -91,17 +89,13 @@ static void* timeshift_reaper_callback ( void *p ) free(tsf); } pthread_mutex_unlock(×hift_reaper_lock); -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "reaper thread exit"); -#endif + tvhtrace("timeshift", "reaper thread exit"); return NULL; } static void timeshift_reaper_remove ( timeshift_file_t *tsf ) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "queue file for removal %s", tsf->path); -#endif + tvhtrace("timeshift", "queue file for removal %s", tsf->path); pthread_mutex_lock(×hift_reaper_lock); TAILQ_INSERT_TAIL(×hift_reaper_list, tsf, link); pthread_cond_signal(×hift_reaper_cond); @@ -252,9 +246,7 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d create file %s", ts->id, path); -#endif + tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = time; @@ -268,10 +260,8 @@ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d copy smt_start to new file", - ts->id); -#endif + tvhtrace("timeshift", "ts %d copy smt_start to new file", + ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 742a04a2..1642f8c3 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -30,8 +30,6 @@ #include #include -//#define TSHFT_TRACE - /* ************************************************************************** * File Reading * *************************************************************************/ @@ -316,15 +314,11 @@ static int _timeshift_read /* Open file */ if (*fd == -1) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", - ts->id, (*cur_file)->path); -#endif + tvhtrace("timeshift", "ts %d open file %s", + ts->id, (*cur_file)->path); *fd = open((*cur_file)->path, O_RDONLY); } -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d seek to %lu", ts->id, *cur_off); -#endif + tvhtrace("timeshift", "ts %d seek to %lu", ts->id, *cur_off); lseek(*fd, *cur_off, SEEK_SET); /* Read msg */ @@ -335,10 +329,8 @@ static int _timeshift_read tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d read msg %p (%ld)", - ts->id, *sm, r); -#endif + tvhtrace("timeshift", "ts %d read msg %p (%ld)", + ts->id, *sm, r); /* Incomplete */ if (r == 0) { @@ -451,9 +443,7 @@ void *timeshift_reader ( void *p ) /* Exit */ if (ctrl->sm_type == SMT_EXIT) { -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d read exit request", ts->id); -#endif + tvhtrace("timeshift", "ts %d read exit request", ts->id); run = 0; streaming_msg_free(ctrl); ctrl = NULL; @@ -734,7 +724,7 @@ void *timeshift_reader ( void *p ) (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { -#ifndef TSHFT_TRACE +#if (!ENABLE_TRACE) if (skip) #endif { @@ -742,7 +732,7 @@ void *timeshift_reader ( void *p ) int64_t delta = now - sm->sm_time; if (sm->sm_type == SMT_PACKET) pts = ((th_pkt_t*)sm->sm_data)->pkt_pts; - tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, + tvhtrace("timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, ts->id, sm->sm_time, pts, delta); } streaming_target_deliver2(ts->output, sm); @@ -755,10 +745,8 @@ void *timeshift_reader ( void *p ) else wait = (deliver - sm->sm_time) / 1000; if (wait == 0) wait = 1; -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d wait %d", - ts->id, wait); -#endif + tvhtrace("timeshift", "ts %d wait %d", + ts->id, wait); } /* Terminate */ @@ -818,9 +806,7 @@ void *timeshift_reader ( void *p ) if (fd != -1) close(fd); if (sm) streaming_msg_free(sm); if (ctrl) streaming_msg_free(ctrl); -#ifdef TSHFT_TRACE - tvhlog(LOG_DEBUG, "timeshift", "ts %d exit reader thread", ts->id); -#endif + tvhtrace("timeshift", "ts %d exit reader thread", ts->id); return NULL; } diff --git a/src/tvhtime.c b/src/tvhtime.c index 6b01883b..81ef8fa6 100644 --- a/src/tvhtime.c +++ b/src/tvhtime.c @@ -82,10 +82,8 @@ tvhtime_update ( struct tm *tm ) ntp_shm_t *ntp_shm; int64_t t1, t2; -#if TIME_TRACE - tvhlog(LOG_DEBUG, "time", "current time is %04d/%02d/%02d %02d:%02d:%02d", + tvhtrace("time", "current time is %04d/%02d/%02d %02d:%02d:%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); -#endif /* Current and reported time */ now = mktime(tm); @@ -108,9 +106,7 @@ tvhtime_update ( struct tm *tm ) if (!(ntp_shm = ntp_shm_init())) return; -#if TIME_TRACE - tvhlog(LOG_DEBUG, "time", "ntp delta = %"PRId64" us\n", t2 - t1); -#endif + tvhtrace("time", "ntp delta = %"PRId64" us\n", t2 - t1); ntp_shm->valid = 0; ntp_shm->count++; ntp_shm->clockTimeStampSec = now; From 0b01dc138aa2813c54aaab778f1b8dfd5762d2e5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 7 Apr 2013 14:01:59 +0100 Subject: [PATCH 461/503] dvr/epg: added some more debug (trace) to look at what might be going on --- src/dvr/dvr_db.c | 45 ++++++++++++++++++++++++++++++++++++--------- src/epg.c | 16 ++++++++++++---- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 100400c2..4e577499 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -215,19 +215,12 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) } } -/** - * - */ static void -dvr_entry_link(dvr_entry_t *de) +dvr_entry_set_timer(dvr_entry_t *de) { time_t now, preamble; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); - de->de_refcnt = 1; - - LIST_INSERT_HEAD(&dvrentries, de, de_global_link); - time(&now); preamble = de->de_start - (60 * de->de_start_extra) - 30; @@ -243,10 +236,25 @@ dvr_entry_link(dvr_entry_t *de) } else if (de->de_channel) { de->de_sched_state = DVR_SCHEDULED; + tvhtrace("dvr", "entry timer scheduled for %"PRItime_t, preamble); gtimer_arm_abs(&de->de_timer, dvr_timer_start_recording, de, preamble); } else { de->de_sched_state = DVR_NOSTATE; } +} + +/** + * + */ +static void +dvr_entry_link(dvr_entry_t *de) +{ + de->de_refcnt = 1; + + LIST_INSERT_HEAD(&dvrentries, de, de_global_link); + + dvr_entry_set_timer(de); + htsp_dvr_entry_add(de); } @@ -369,8 +377,9 @@ static dvr_entry_t *_dvr_entry_create ( LIST_INSERT_HEAD(&dae->dae_spawns, de, de_autorec_link); } - tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, " + tvhlog(LOG_INFO, "dvr", "entry %d \"%s\" on \"%s\" starting at %s, " "scheduled for recording by \"%s\"", + de->de_id, lang_str_get(de->de_title, NULL), DVR_CH_NAME(de), tbuf, creator); dvrdb_changed(); @@ -726,6 +735,8 @@ static dvr_entry_t *_dvr_entry_update de->de_stop_extra = stop_extra; save = 1; } + if (save) + dvr_entry_set_timer(de); /* Title */ if (e && e->episode && e->episode->title) { @@ -803,6 +814,11 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) /* Existing entry */ if ((de = dvr_entry_find_by_event(e))) { + tvhtrace("dvr", + "dvr entry %d event replaced %s on %s @ %"PRItime_t + " to %"PRItime_t, + de->de_id, epg_broadcast_get_title(e, NULL), e->channel->ch_name, + e->start, e->stop); /* Unlink the broadcast */ e->putref(e); @@ -811,6 +827,11 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) /* Find match */ RB_FOREACH(e, &e->channel->ch_epg_schedule, sched_link) { if (dvr_entry_fuzzy_match(de, e)) { + tvhtrace("dvr", + " replacement event %s on %s @ %"PRItime_t + " to %"PRItime_t, + epg_broadcast_get_title(e, NULL), e->channel->ch_name, + e->start, e->stop); e->getref(e); de->de_bcast = e; _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); @@ -832,6 +853,12 @@ void dvr_event_updated ( epg_broadcast_t *e ) if (de->de_bcast) continue; if (de->de_channel != e->channel) continue; if (dvr_entry_fuzzy_match(de, e)) { + tvhtrace("dvr", + "dvr entry %d link to event %s on %s @ %"PRItime_t + " to %"PRItime_t, + de->de_id, epg_broadcast_get_title(e, NULL), + e->channel->ch_name, + e->start, e->stop); e->getref(e); de->de_bcast = e; _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); diff --git a/src/epg.c b/src/epg.c index 89c40904..e031ae07 100644 --- a/src/epg.c +++ b/src/epg.c @@ -100,8 +100,8 @@ void epg_updated ( void ) /* Remove unref'd */ while ((eo = LIST_FIRST(&epg_object_unref))) { - tvhlog(LOG_DEBUG, "epg", - "unref'd object %u (%s) created during update", eo->id, eo->uri); + tvhtrace("epg", + "unref'd object %u (%s) created during update", eo->id, eo->uri); LIST_REMOVE(eo, un_link); eo->destroy(eo); } @@ -1377,8 +1377,8 @@ static void _epg_channel_timer_callback ( void *p ) /* Expire */ if ( ebc->stop <= dispatch_clock ) { - tvhlog(LOG_DEBUG, "epg", "expire event %u from %s", - ebc->id, ch->ch_name); + tvhlog(LOG_DEBUG, "epg", "expire event %u (%s) from %s", + ebc->id, epg_broadcast_get_title(ebc, NULL), ch->ch_name); _epg_channel_rem_broadcast(ch, ebc, NULL); continue; // skip to next @@ -1447,6 +1447,8 @@ static epg_broadcast_t *_epg_channel_add_broadcast _epg_object_create(ret); // Note: sets updated _epg_object_getref(ret); + tvhtrace("epg", "added event %u (%s) on %s @ %"PRItime_t " to %"PRItime_t, + ret->id, epg_broadcast_get_title(ret, NULL), ch->ch_name, ret->start, ret->stop); /* Existing */ } else { @@ -1460,6 +1462,8 @@ static epg_broadcast_t *_epg_channel_add_broadcast } else { ret->stop = (*bcast)->stop; _epg_object_set_updated(ret); + tvhtrace("epg", "updated event %u (%s) on %s @ %"PRItime_t " to %"PRItime_t, + ret->id, epg_broadcast_get_title(ret, NULL), ch->ch_name, ret->start, ret->stop); } } } @@ -1470,12 +1474,16 @@ static epg_broadcast_t *_epg_channel_add_broadcast /* Remove overlapping (before) */ while ( (ebc = RB_PREV(ret, sched_link)) != NULL ) { if ( ebc->stop <= ret->start ) break; + tvhtrace("epg", "remove overlap (b) event %u (%s) on %s @ %"PRItime_t " to %"PRItime_t, + ebc->id, epg_broadcast_get_title(ebc, NULL), ch->ch_name, ebc->start, ebc->stop); _epg_channel_rem_broadcast(ch, ebc, ret); } /* Remove overlapping (after) */ while ( (ebc = RB_NEXT(ret, sched_link)) != NULL ) { if ( ebc->start >= ret->stop ) break; + tvhtrace("epg", "remove overlap (a) event %u (%s) on %s @ %"PRItime_t " to %"PRItime_t, + ebc->id, epg_broadcast_get_title(ebc, NULL), ch->ch_name, ebc->start, ebc->stop); _epg_channel_rem_broadcast(ch, ebc, ret); } From 447f0377199ee393eb600d0a8c5b950b6b19b081 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 7 Apr 2013 14:04:53 +0100 Subject: [PATCH 462/503] init: add trace options to start scripts --- debian/tvheadend.default | 4 ++++ debian/tvheadend.init | 1 + debian/tvheadend.upstart | 1 + 3 files changed, 6 insertions(+) diff --git a/debian/tvheadend.default b/debian/tvheadend.default index 29b6dbef..1fda3e62 100644 --- a/debian/tvheadend.default +++ b/debian/tvheadend.default @@ -47,6 +47,10 @@ TVH_HTSP_PORT="" # if set to 1 will output debug to syslog TVH_DEBUG=0 +# TVH_TRACE +# enable trace in subsystems +TVH_TRACE="" + # TVH_DELAY # if set startup will be delayed N seconds to allow hardware init TVH_DELAY="" diff --git a/debian/tvheadend.init b/debian/tvheadend.init index b4aea9cb..5c93f8bb 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -38,6 +38,7 @@ ARGS="-f" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" +[ -z "$TVH_TRACE" ] || ARGS="$ARGS --trace $TVH_TRACE" # Load the VERBOSE setting and other rcS variables [ -f /etc/default/rcS ] && . /etc/default/rcS diff --git a/debian/tvheadend.upstart b/debian/tvheadend.upstart index a5d70404..745b373f 100644 --- a/debian/tvheadend.upstart +++ b/debian/tvheadend.upstart @@ -27,6 +27,7 @@ script [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" + [ -z "$TVH_TRACE" ] || ARGS="$ARGS --trace $TVH_TRACE" [ ! -z "$TVH_DELAY" ] && sleep $TVH_DELAY From 7588429c334246f5ab5544083b85a6548b71742d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 7 Apr 2013 22:10:21 +0100 Subject: [PATCH 463/503] dvb: adapter notification happening at wrong point meant UI not always updated --- src/dvb/dvb_adapter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 0ce3c66c..be8e32a0 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -738,6 +738,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt ) tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } + dvb_adapter_notify(tda); + /* Don't close FE */ if (!tda->tda_idleclose && tda->tda_enabled) return; @@ -747,8 +749,6 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt ) close(tda->tda_fe_fd); tda->tda_fe_fd = -1; } - - dvb_adapter_notify(tda); } /** From 4bc51699b9bba8a113b6e1862946768f779afe7d Mon Sep 17 00:00:00 2001 From: Joakim Hernberg Date: Tue, 9 Apr 2013 10:22:27 +0200 Subject: [PATCH 464/503] make diseqc tracing comprehensive --- src/dvb/diseqc.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) mode change 100644 => 100755 src/dvb/diseqc.c diff --git a/src/dvb/diseqc.c b/src/dvb/diseqc.c old mode 100644 new mode 100755 index 7b736f35..8f070bfb --- a/src/dvb/diseqc.c +++ b/src/dvb/diseqc.c @@ -20,6 +20,7 @@ int diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len) { + int err; struct dvb_diseqc_master_cmd message; tvhtrace("diseqc", "sending %X %X %X %X %X %X", @@ -32,7 +33,11 @@ diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, message.msg[4] = data_2; message.msg[5] = data_3; message.msg_len = msg_len; - return ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message); + if ((err = ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message))) { + return err; + tvhtrace("diseqc", "Error sending diseqc command"); + } + return 0; } int @@ -44,7 +49,7 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, int k, err; tvhtrace("diseqc", - "fe_fd %i, lnb_num %i, voltage %i, band %i, version %i, repeats %i", + "diseqc_setup() called with: fe_fd=%i, lnb_num=%i, voltage=%i, band=%i, version=%i, repeats=%i", fe_fd, lnb_num, voltage, band, version, repeats); /* verify lnb number and diseqc data */ @@ -52,13 +57,18 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, return -1; /* turn off continuous tone */ - if ((err = ioctl(fe_fd, FE_SET_TONE, SEC_TONE_OFF))) + tvhtrace("diseqc", "Turning off continuous tone"); + if ((err = ioctl(fe_fd, FE_SET_TONE, SEC_TONE_OFF))) { + tvhtrace("diseqc", "Error trying to turn off continuous tone"); return err; + } /* set lnb voltage */ - if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, - (i/2) % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13))) + tvhtrace("diseqc", "Setting lnb voltage to %iV", (i/2) % 2 ? 18 : 13); + if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, (i/2) % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13))) { + tvhtrace("diseqc", "Error setting lnb voltage"); return err; + } msleep(15); if (repeats == 0) { /* uncommited msg, wait 15ms, commited msg */ @@ -82,19 +92,31 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, msleep(15); /* set toneburst */ - if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, - (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) + tvhtrace("diseqc", (i/4) % 2 ? "Sending mini diseqc B" : "Sending mini diseqc A"); + if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) { + tvhtrace("diseqc", "Error sending mini diseqc command"); return err; + } msleep(15); /* set continuous tone */ - if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) + tvhtrace("diseqc", i % 2 ? "Setting continous 22KHz to on" : "Setting continous 22KHz to off"); + if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) { + tvhtrace("diseqc", "Error setting continuous tone"); return err; + } return 0; } int diseqc_voltage_off(int fe_fd) { - return ioctl(fe_fd, FE_SET_VOLTAGE, SEC_VOLTAGE_OFF); + int err; + + tvhtrace("diseqc", "Sending diseqc voltage off command"); + if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, SEC_VOLTAGE_OFF))) { + tvhtrace("diseqc", "Error sending diseqc voltage off command"); + return err; + } + return 0; } From c00e5e0af4915c297ef0ea2ddb5b47ee6ac03357 Mon Sep 17 00:00:00 2001 From: Joakim Hernberg Date: Tue, 9 Apr 2013 11:00:32 +0200 Subject: [PATCH 465/503] tidy up trace spamming --- src/dvb/diseqc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/dvb/diseqc.c b/src/dvb/diseqc.c index 8f070bfb..29c948d3 100755 --- a/src/dvb/diseqc.c +++ b/src/dvb/diseqc.c @@ -34,8 +34,8 @@ diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, message.msg[5] = data_3; message.msg_len = msg_len; if ((err = ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message))) { + tvhlog(LOG_ERR, "diseqc", "error sending diseqc command"); return err; - tvhtrace("diseqc", "Error sending diseqc command"); } return 0; } @@ -49,7 +49,7 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, int k, err; tvhtrace("diseqc", - "diseqc_setup() called with: fe_fd=%i, lnb_num=%i, voltage=%i, band=%i, version=%i, repeats=%i", + "fe_fd=%i, lnb_num=%i, voltage=%i, band=%i, version=%i, repeats=%i", fe_fd, lnb_num, voltage, band, version, repeats); /* verify lnb number and diseqc data */ @@ -57,16 +57,16 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, return -1; /* turn off continuous tone */ - tvhtrace("diseqc", "Turning off continuous tone"); + tvhtrace("diseqc", "disabling continuous tone"); if ((err = ioctl(fe_fd, FE_SET_TONE, SEC_TONE_OFF))) { - tvhtrace("diseqc", "Error trying to turn off continuous tone"); + tvhlog(LOG_ERR, "diseqc", "error trying to turn off continuous tone"); return err; } /* set lnb voltage */ - tvhtrace("diseqc", "Setting lnb voltage to %iV", (i/2) % 2 ? 18 : 13); + tvhtrace("diseqc", "setting lnb voltage to %iV", (i/2) % 2 ? 18 : 13); if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, (i/2) % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13))) { - tvhtrace("diseqc", "Error setting lnb voltage"); + tvhlog(LOG_ERR, "diseqc", "error setting lnb voltage"); return err; } msleep(15); @@ -92,17 +92,17 @@ diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, msleep(15); /* set toneburst */ - tvhtrace("diseqc", (i/4) % 2 ? "Sending mini diseqc B" : "Sending mini diseqc A"); + tvhtrace("diseqc", (i/4) % 2 ? "sending mini diseqc B" : "sending mini diseqc A"); if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) { - tvhtrace("diseqc", "Error sending mini diseqc command"); + tvhlog(LOG_ERR, "diseqc", "error sending mini diseqc command"); return err; } msleep(15); /* set continuous tone */ - tvhtrace("diseqc", i % 2 ? "Setting continous 22KHz to on" : "Setting continous 22KHz to off"); + tvhtrace("diseqc", i % 2 ? "enabling continous tone" : "disabling continuous tone"); if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) { - tvhtrace("diseqc", "Error setting continuous tone"); + tvhlog(LOG_ERR, "diseqc", "error setting continuous tone"); return err; } return 0; @@ -113,9 +113,9 @@ diseqc_voltage_off(int fe_fd) { int err; - tvhtrace("diseqc", "Sending diseqc voltage off command"); + tvhtrace("diseqc", "sending diseqc voltage off command"); if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, SEC_VOLTAGE_OFF))) { - tvhtrace("diseqc", "Error sending diseqc voltage off command"); + tvhlog(LOG_ERR, "diseqc", "error sending diseqc voltage off command"); return err; } return 0; From 13fad234d5a6a9325fe6275e35d67ef613895e82 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 9 Apr 2013 14:59:22 +0100 Subject: [PATCH 466/503] dvb: redo sdt parser to make it more robust Fixes some problems reported in #hts regarding missing services --- src/dvb/dvb_tables.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index f2bc4922..ac0226c5 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -326,6 +326,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t running_status; #endif int l; + uint8_t *dlptr, *dptr; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; @@ -348,6 +349,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if (!tdmi) return -1; } tvhtrace("sdt", "onid %04X tsid %04X", onid, tsid); + //hexdump("sdt", ptr, len); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; @@ -362,7 +364,6 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, len -= 8; ptr += 8; - while(len >= 5) { int save = 0; service_id = ptr[0] << 8 | ptr[1]; @@ -372,13 +373,13 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, #endif free_ca_mode = (ptr[3] >> 4) & 0x1; dllen = ((ptr[3] & 0x0f) << 8) | ptr[4]; - tvhtrace("sdt", " sid %04X running %d free_ca %d", - service_id, running_status, free_ca_mode); + dlptr = ptr + 5; + tvhtrace("sdt", " sid %04X running %d free_ca %d dllen %d", + service_id, running_status, free_ca_mode, dllen); - len -= 5; - ptr += 5; - - if(dllen > len) + ptr += (5 + dllen); + len -= (5 + dllen); + if (len < 0) break; stype = 0; @@ -386,16 +387,18 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, *crid = 0; while(dllen > 2) { - dtag = ptr[0]; - dlen = ptr[1]; + dtag = dlptr[0]; + dlen = dlptr[1]; + dptr = dlptr + 2; - len -= 2; ptr += 2; dllen -= 2; + dlptr += (2 + dlen); + dllen -= (2 + dlen); - if(dlen > len) break; + if(dllen < 0) break; switch(dtag) { case DVB_DESC_SERVICE: - if(dvb_desc_service(ptr, dlen, &stype, + if(dvb_desc_service(dptr, dlen, &stype, provider, sizeof(provider), chname0, sizeof(chname0)) == 0) { tvhtrace("sdt", " stype = %d, provider = %s, name = %s", @@ -419,10 +422,9 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } break; case DVB_DESC_DEF_AUTHORITY: - dvb_desc_def_authority(ptr, dlen, crid, sizeof(crid)); + dvb_desc_def_authority(dptr, dlen, crid, sizeof(crid)); break; } - len -= dlen; ptr += dlen; dllen -= dlen; } if (!servicetype_is_tv(stype) && From 2c8835e4700eb6d0c9dff3ecab5beb4269dfb50c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 9 Apr 2013 15:01:07 +0100 Subject: [PATCH 467/503] eit: forgot to set message as trace --- src/epggrab/module/eit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index f610c0a4..c20c32d5 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -771,7 +771,7 @@ done: { int total = 0; int finished = 0; - tvhlog(LOG_DEBUG, mod->id, "scan status"); + tvhtrace("eit", "scan status:"); LIST_FOREACH(tsta, &sta->tables, link) { total++; tvhtrace("eit", From 4706b29f19cde1e5f758e29d157eca680a82ae95 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 9 Apr 2013 22:23:02 +0100 Subject: [PATCH 468/503] htsmsg: add ability to set/update a u32 --- src/htsmsg.c | 15 +++++++++++++++ src/htsmsg.h | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/htsmsg.c b/src/htsmsg.c index 90f4e4eb..62307dac 100644 --- a/src/htsmsg.c +++ b/src/htsmsg.c @@ -186,6 +186,21 @@ htsmsg_add_u32(htsmsg_t *msg, const char *name, uint32_t u32) f->hmf_s64 = u32; } +/* + * + */ +int +htsmsg_set_u32(htsmsg_t *msg, const char *name, uint32_t u32) +{ + htsmsg_field_t *f = htsmsg_field_find(msg, name); + if (!f) + f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED); + if (f->hmf_type != HMF_S64) + return 1; + f->hmf_s64 = u32; + return 0; +} + /* * */ diff --git a/src/htsmsg.h b/src/htsmsg.h index 8343634f..05beb2cf 100644 --- a/src/htsmsg.h +++ b/src/htsmsg.h @@ -111,6 +111,11 @@ void htsmsg_destroy(htsmsg_t *msg); */ void htsmsg_add_u32(htsmsg_t *msg, const char *name, uint32_t u32); +/** + * Add/update an integer field + */ +int htsmsg_set_u32(htsmsg_t *msg, const char *name, uint32_t u32); + /** * Add an integer field where source is signed 32 bit. */ From 2c502b977f120b3973ae8ca049fff072ee9e25e5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 9 Apr 2013 22:39:37 +0100 Subject: [PATCH 469/503] tvhlog: created a new logging system that will allow more configuration Note: I still need to sort out the command line switches --- Makefile | 1 + src/main.c | 229 +++++++++--------------------------------------- src/tvheadend.h | 24 +---- src/tvhlog.c | 201 ++++++++++++++++++++++++++++++++++++++++++ src/tvhlog.h | 74 ++++++++++++++++ 5 files changed, 320 insertions(+), 209 deletions(-) create mode 100644 src/tvhlog.c create mode 100644 src/tvhlog.h diff --git a/Makefile b/Makefile index ac9a3e89..00ee4147 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,7 @@ endif # SRCS = src/version.c \ src/main.c \ + src/tvhlog.c \ src/utils.c \ src/wrappers.c \ src/access.c \ diff --git a/src/main.c b/src/main.c index bc82bb70..02ee83a3 100644 --- a/src/main.c +++ b/src/main.c @@ -145,14 +145,7 @@ pthread_mutex_t atomic_lock; * Locals */ static int running; -static int log_stderr; -static int log_decorate; static LIST_HEAD(, gtimer) gtimers; -static int log_debug_to_syslog; -static int log_debug_to_console; -static int log_debug_to_path; -static htsmsg_t* log_debug_trace; -static char* log_path; static pthread_cond_t gtimer_cond; static void @@ -398,15 +391,11 @@ main(int argc, char **argv) #if ENABLE_LINUXDVB uint32_t adapter_mask; #endif + int log_level = LOG_INFO; + int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG; + const char *log_subsys = NULL; /* Defaults */ - log_stderr = 1; - log_decorate = isatty(2); - log_debug_to_syslog = 0; - log_debug_to_console = 0; - log_debug_to_path = 0; - log_debug_trace = NULL; - log_path = NULL; tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; @@ -417,21 +406,24 @@ main(int argc, char **argv) opt_version = 0, opt_fork = 0, opt_firstrun = 0, - opt_debug = 0, + opt_stderr = 0, opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, + opt_trace = 0, + opt_fileline = 0, opt_ipv6 = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, + *opt_logpath = NULL, + *opt_log_subsys = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, *opt_dvb_raw = NULL, #endif - *opt_trace = NULL, *opt_rawts = NULL, *opt_bindaddr = NULL, *opt_subscribe = NULL; @@ -469,13 +461,15 @@ main(int argc, char **argv) OPT_INT, &tvheadend_htsp_port_extra }, { 0, NULL, "Debug Options", OPT_BOOL, NULL }, - { 'd', "debug", "Enable all debug", OPT_BOOL, &opt_debug }, + { 'd', "stderr", "Enable debug on stderr", OPT_BOOL, &opt_stderr }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, - { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, - { 'l', "log", "Log to file", OPT_STR, &log_path }, + { 'l', "logfile", "Enable debug to file", OPT_STR, &opt_logpath }, + { 0, "subsys", "Enable debug subsystems", OPT_STR, &opt_log_subsys }, + { 0, "fileline", "Add file and line numbers to debug", OPT_BOOL, &opt_fileline }, #if ENABLE_TRACE - { 0, "trace", "Enable low level debug", OPT_STR, &opt_trace }, + { 0, "trace", "Enable low level debug", OPT_BOOL, &opt_trace }, #endif + { 0, "uidebug", "Enable webUI debug (non-minified JS)", OPT_BOOL, &opt_uidebug }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, @@ -528,25 +522,6 @@ main(int argc, char **argv) } /* Additional cmdline processing */ - opt_debug |= (opt_trace != NULL); - log_debug_to_console = opt_debug; - log_debug_to_syslog = opt_syslog; - log_debug_to_path = opt_debug; - tvheadend_webui_debug = opt_debug || opt_uidebug; - if (opt_trace) { - log_debug_trace = htsmsg_create_map(); - htsmsg_add_u32(log_debug_trace, "START", 1); - uint32_t u32; - char *trace = strdup(opt_trace); - char *p, *r = NULL; - p = strtok_r(trace, ",", &r); - while (p) { - if (htsmsg_get_u32(log_debug_trace, p, &u32)) - htsmsg_add_u32(log_debug_trace, p, 1); - p = strtok_r(NULL, ",", &r); - } - free(trace); - } #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; @@ -587,6 +562,29 @@ main(int argc, char **argv) tvheadend_webroot = tmp; } + /* Setup logging */ + if (isatty(2)) + log_options |= TVHLOG_OPT_DECORATE; + if (opt_stderr || opt_syslog || opt_logpath) { + log_subsys = "all"; + log_level = LOG_DEBUG; + if (opt_stderr) + log_options |= TVHLOG_OPT_DBG_STDERR; + if (opt_syslog) + log_options |= TVHLOG_OPT_DBG_SYSLOG; + if (opt_logpath) + log_options |= TVHLOG_OPT_DBG_FILE; + } + if (opt_fileline) + log_options |= TVHLOG_OPT_FILELINE; + if (opt_trace) + log_level = LOG_TRACE; + if (opt_log_subsys) + log_subsys = opt_log_subsys; + + tvhlog_init(log_level, log_options, opt_logpath); + tvhlog_set_subsys(log_subsys); + signal(SIGPIPE, handle_sigpipe); // will be redundant later /* Daemonise */ @@ -643,11 +641,12 @@ main(int argc, char **argv) umask(0); } - /* Setup logging */ - log_stderr = !opt_fork; - log_decorate = isatty(2); - openlog("tvheadend", LOG_PID, LOG_DAEMON); - + /* Alter logging */ + if (opt_fork) + tvhlog_options &= ~TVHLOG_OPT_STDERR; + if (!isatty(2)) + tvhlog_options &= ~TVHLOG_OPT_DECORATE; + /* Initialise configuration */ hts_settings_init(opt_config); @@ -754,7 +753,6 @@ main(int argc, char **argv) "running as PID:%d UID:%d GID:%d, settings located in '%s'", tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root()); - tvhtrace("START", "TRACE debug enabled"); if(opt_abort) abort(); @@ -780,147 +778,6 @@ main(int argc, char **argv) } - -static const char *logtxtmeta[8][2] = { - {"EMERGENCY", "\033[31m"}, - {"ALERT", "\033[31m"}, - {"CRITICAL", "\033[31m"}, - {"ERROR", "\033[31m"}, - {"WARNING", "\033[33m"}, - {"NOTICE", "\033[36m"}, - {"INFO", "\033[32m"}, - {"DEBUG", "\033[32m"}, -}; - -/** - * Internal log function - */ -static void -tvhlogv(int notify, int severity, const char *subsys, const char *fmt, - va_list ap) -{ - char buf[2048]; - char buf2[2048]; - char t[64]; - int l, ms; - struct tm tm; - struct timeval now; - static int log_path_fail = 0; - size_t c; - - l = snprintf(buf, sizeof(buf), "%s: ", subsys); - - vsnprintf(buf + l, sizeof(buf) - l, fmt, ap); - - if(log_debug_to_syslog || severity < LOG_DEBUG) - syslog(severity, "%s", buf); - - /** - * Get time (string) - */ - gettimeofday(&now, NULL); - localtime_r(&now.tv_sec, &tm); - ms = now.tv_usec / 1000; - c = strftime(t, sizeof(t), "%b %d %H:%M:%S", &tm); - snprintf(t+c, sizeof(t)-c, ".%03d", ms); - - /** - * Send notification to Comet (Push interface to web-clients) - */ - if(notify) { - htsmsg_t *m; - snprintf(buf2, sizeof(buf2), "%s %s", t, buf); - m = htsmsg_create_map(); - htsmsg_add_str(m, "notificationClass", "logmessage"); - htsmsg_add_str(m, "logtxt", buf2); - comet_mailbox_add_message(m, severity == LOG_DEBUG); - htsmsg_destroy(m); - } - - /** - * Write to stderr - */ - - if(log_stderr && (log_debug_to_console || severity < LOG_DEBUG)) { - const char *leveltxt = logtxtmeta[severity][0]; - const char *sgr = logtxtmeta[severity][1]; - const char *sgroff; - - if(!log_decorate) { - sgr = ""; - sgroff = ""; - } else { - sgroff = "\033[0m"; - } - fprintf(stderr, "%s%s [%7s]:%s%s\n", sgr, t, leveltxt, buf, sgroff); - } - - /** - * Write to file - */ - if (log_path && (log_debug_to_path || severity < LOG_DEBUG)) { - const char *leveltxt = logtxtmeta[severity][0]; - FILE *fp = fopen(log_path, "a"); - if (fp) { - log_path_fail = 0; - fprintf(fp, "%s [%7s]:%s\n", t, leveltxt, buf); - fclose(fp); - } else { - if (!log_path_fail) - syslog(LOG_WARNING, "failed to write log file %s", log_path); - log_path_fail = 1; - } - } -} - - -/** - * - */ -void -tvhlog(int severity, const char *subsys, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - tvhlogv(1, severity, subsys, fmt, ap); - va_end(ap); -} - -/** - * TVH trace - */ -#ifdef ENABLE_TRACE -void -tvhtrace(const char *subsys, const char *fmt, ...) -{ - va_list ap; - if (!log_debug_trace) - return; - if (!htsmsg_get_u32_or_default(log_debug_trace, "all", 0)) - if (!htsmsg_get_u32_or_default(log_debug_trace, subsys, 0)) - return; - va_start(ap, fmt); - tvhlogv(0, LOG_DEBUG, subsys, fmt, ap); - va_end(ap); -} -#endif - -/** - * May be invoked from a forked process so we can't do any notification - * to comet directly. - * - * @todo Perhaps do it via a pipe? - */ -void -tvhlog_spawn(int severity, const char *subsys, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - tvhlogv(0, severity, subsys, fmt, ap); - va_end(ap); -} - - /** * */ diff --git a/src/tvheadend.h b/src/tvheadend.h index deab273d..f0dbdcd4 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -32,6 +32,7 @@ #include "avg.h" #include "hts_strtab.h" #include "htsmsg.h" +#include "tvhlog.h" #include "redblack.h" @@ -440,29 +441,6 @@ static inline unsigned int tvh_strhash(const char *s, unsigned int mod) void tvh_str_set(char **strp, const char *src); int tvh_str_update(char **strp, const char *src); -void tvhlog(int severity, const char *subsys, const char *fmt, ...) - __attribute__((format(printf,3,4))); - -void tvhlog_spawn(int severity, const char *subsys, const char *fmt, ...) - __attribute__((format(printf,3,4))); - -#define LOG_EMERG 0 /* system is unusable */ -#define LOG_ALERT 1 /* action must be taken immediately */ -#define LOG_CRIT 2 /* critical conditions */ -#define LOG_ERR 3 /* error conditions */ -#define LOG_WARNING 4 /* warning conditions */ -#define LOG_NOTICE 5 /* normal but significant condition */ -#define LOG_INFO 6 /* informational */ -#define LOG_DEBUG 7 /* debug-level messages */ - -#ifndef ENABLE_TRACE -#define tvhtrace(...) ((void)0) -#else -void tvhtrace(const char *subsys, const char *fmt, ...); -#endif - -extern int log_debug; - #ifndef CLOCK_MONOTONIC_COARSE #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC #endif diff --git a/src/tvhlog.c b/src/tvhlog.c new file mode 100644 index 00000000..ce243077 --- /dev/null +++ b/src/tvhlog.c @@ -0,0 +1,201 @@ +/* + * Tvheadend - logging + * Copyright (C) 2012 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 . + */ + +#include "tvhlog.h" +#include +#include +#include +#include +#include + +#include "webui/webui.h" + +int tvhlog_level; +int tvhlog_options; +char *tvhlog_path; +int tvhlog_path_fail; +htsmsg_t *tvhlog_subsys; +pthread_mutex_t tvhlog_mutex; + +static const char *logtxtmeta[9][2] = { + {"EMERGENCY", "\033[31m"}, + {"ALERT", "\033[31m"}, + {"CRITICAL", "\033[31m"}, + {"ERROR", "\033[31m"}, + {"WARNING", "\033[33m"}, + {"NOTICE", "\033[36m"}, + {"INFO", "\033[32m"}, + {"DEBUG", "\033[32m"}, + {"TRACE", "\033[32m"}, +}; + +/* Initialise */ +void +tvhlog_init ( int level, int options, const char *path ) +{ + tvhlog_level = level; + tvhlog_options = options; + tvhlog_path = path ? strdup(path) : NULL; + tvhlog_subsys = NULL; + openlog("tvheadend", LOG_PID, LOG_DAEMON); + pthread_mutex_init(&tvhlog_mutex, NULL); +} + +/* Set subsys */ +void tvhlog_set_subsys ( const char *subsys ) +{ + uint32_t a; + char *t, *r, *s; + + if (tvhlog_subsys) + htsmsg_destroy(tvhlog_subsys); + tvhlog_subsys = NULL; + + if (!subsys) + return; + + s = strdup(subsys); + t = strtok_r(s, ",", &r); + while ( t ) { + subsys = NULL; + a = 1; + if (t[0] == '+' || t[0] == '-') { + a = t[0] == '+'; + t++; + } + if (!strcmp(t, "all")) { + if (tvhlog_subsys) + htsmsg_destroy(tvhlog_subsys); + tvhlog_subsys = NULL; + } + if (!tvhlog_subsys) + tvhlog_subsys = htsmsg_create_map(); + htsmsg_set_u32(tvhlog_subsys, t, a); + t = strtok_r(NULL, ",", &r); + } + free(s); +} + +/* Log */ +void tvhlogv ( const char *file, int line, + int notify, int severity, + const char *subsys, const char *fmt, va_list args ) +{ + struct timeval now; + struct tm tm; + char t[128], buf[2048], buf2[2048]; + uint32_t a; + size_t l; + int s; + + /* Map down */ + if (severity > LOG_DEBUG) + s = LOG_DEBUG; + else + s = severity; + + /* Check debug enabled */ + if (severity >= LOG_DEBUG) { + if (!tvhlog_subsys) + return; + if (severity > tvhlog_level) + return; + a = htsmsg_get_u32_or_default(tvhlog_subsys, "all", 0); + if (!htsmsg_get_u32_or_default(tvhlog_subsys, subsys, a)) + return; + } + + /* Get time */ + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &tm); + l = strftime(t, sizeof(t), "%b %d %H:%M:%S", &tm); + if (tvhlog_options & TVHLOG_OPT_MILLIS) { + int ms = now.tv_usec / 1000; + snprintf(t+l, sizeof(t)-l, ".%03d", ms); + } + + /* Basic message */ + l = snprintf(buf, sizeof(buf), "%s: ", subsys); + if (tvhlog_options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) + l += snprintf(buf + l, sizeof(buf) - l, "(%s:%d) ", file, line); + l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); + + /* Syslog */ + if (tvhlog_options & TVHLOG_OPT_SYSLOG) { + if (tvhlog_options & TVHLOG_OPT_DBG_SYSLOG || severity < LOG_DEBUG) { + syslog(s, "%s", buf); + } + } + + /* Comet (debug must still be enabled??) */ + if(notify && severity < LOG_TRACE) { + htsmsg_t *m = htsmsg_create_map(); + snprintf(buf2, sizeof(buf2), "%s %s", t, buf); + htsmsg_add_str(m, "notificationClass", "logmessage"); + htsmsg_add_str(m, "logtxt", buf2); + comet_mailbox_add_message(m, severity >= LOG_DEBUG); + htsmsg_destroy(m); + } + + /* Console */ + if (tvhlog_options & TVHLOG_OPT_STDERR) { + if (tvhlog_options & TVHLOG_OPT_DBG_STDERR || severity < LOG_DEBUG) { + const char *leveltxt = logtxtmeta[severity][0]; + const char *sgr = logtxtmeta[severity][1]; + const char *sgroff; + + if (tvhlog_options & TVHLOG_OPT_DECORATE) + sgroff = "\033[0m"; + else { + sgr = ""; + sgroff = ""; + } + fprintf(stderr, "%s%s [%7s] %s%s\n", sgr, t, leveltxt, buf, sgroff); + } + } + + /* File */ + if (tvhlog_path) { + if (tvhlog_options & TVHLOG_OPT_DBG_FILE || severity < LOG_DEBUG) { + const char *leveltxt = logtxtmeta[severity][0]; + FILE *fp = fopen(tvhlog_path, "a"); + if (fp) { + tvhlog_path_fail = 0; + fprintf(fp, "%s [%7s]:%s\n", t, leveltxt, buf); + fclose(fp); + } else { + if (!tvhlog_path_fail) + syslog(LOG_WARNING, "failed to write log file %s", tvhlog_path); + tvhlog_path_fail = 1; + } + } + } +} + +/* + * Map args + */ +void _tvhlog ( const char *file, int line, + int notify, int severity, + const char *subsys, const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + tvhlogv(file, line, notify, severity, subsys, fmt, args); + va_end(args); +} diff --git a/src/tvhlog.h b/src/tvhlog.h new file mode 100644 index 00000000..a00a0dad --- /dev/null +++ b/src/tvhlog.h @@ -0,0 +1,74 @@ +/* + * Tvheadend - logging + * Copyright (C) 2012 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_LOGGING_H__ +#define __TVH_LOGGING_H__ + +#include +#include +#include + +#include "htsmsg.h" + +/* Config */ +extern int tvhlog_level; +extern htsmsg_t *tvhlog_subsys; +extern char *tvhlog_path; +extern int tvhlog_options; +extern pthread_mutex_t tvhlog_mutex; + +/* Initialise */ +void tvhlog_init ( int level, int options, const char *path ); +void tvhlog_set_subsys ( const char *subsys ); +void tvhlogv ( const char *file, int line, + int notify, int severity, + const char *subsys, const char *fmt, va_list args ); +void _tvhlog ( const char *file, int line, + int notify, int severity, + const char *subsys, const char *fmt, ... ) + __attribute__((format(printf,6,7))); + + +/* Options */ +#define TVHLOG_OPT_DBG_SYSLOG 0x0001 +#define TVHLOG_OPT_DBG_STDERR 0x0002 +#define TVHLOG_OPT_DBG_FILE 0x0004 +#define TVHLOG_OPT_SYSLOG 0x0010 +#define TVHLOG_OPT_STDERR 0x0020 +#define TVHLOG_OPT_MILLIS 0x0100 +#define TVHLOG_OPT_DECORATE 0x0200 +#define TVHLOG_OPT_FILELINE 0x0400 +#define TVHLOG_OPT_ALL 0xFFFF + +/* Levels */ +#ifndef LOG_TRACE +#define LOG_TRACE (LOG_DEBUG+1) +#endif + +/* Macros */ +#define tvhlog(severity, subsys, fmt, ...)\ + _tvhlog(__FILE__, __LINE__, 1, severity, subsys, fmt, ##__VA_ARGS__) +#define tvhlog_spawn(severity, subsys, fmt, ...)\ + _tvhlog(__FILE__, __LINE__, 0, severity, subsys, fmt, ##__VA_ARGS__) +#if ENABLE_TRACE +#define tvhtrace(subsys, fmt, ...)\ + _tvhlog(__FILE__, __LINE__, 0, LOG_TRACE, subsys, fmt, ##__VA_ARGS__) +#else +#define tvhtrace(...) (void)0 +#endif + +#endif /* __TVH_LOGGING_H__ */ From aa591a32633ef2eb84d6ff9d8cd27e098016817b Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 11:04:45 +0100 Subject: [PATCH 470/503] timeshift: fix trace debug. --- src/timeshift/timeshift_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 1642f8c3..3701ddee 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -724,9 +724,8 @@ void *timeshift_reader ( void *p ) (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { -#if (!ENABLE_TRACE) +#if ENABLE_TRACE if (skip) -#endif { time_t pts = 0; int64_t delta = now - sm->sm_time; @@ -735,6 +734,7 @@ void *timeshift_reader ( void *p ) tvhtrace("timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t " shift=%"PRIu64, ts->id, sm->sm_time, pts, delta); } +#endif streaming_target_deliver2(ts->output, sm); last_time = sm->sm_time; sm = NULL; From cd7c5dd9b9e5a61dfdb69ec406c572434b00de91 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 11:30:11 +0100 Subject: [PATCH 471/503] tvhlog: added hexdump routine. --- src/tvhlog.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/tvhlog.h | 7 +++++++ 2 files changed, 51 insertions(+) diff --git a/src/tvhlog.c b/src/tvhlog.c index ce243077..e0466e94 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -195,7 +195,51 @@ void _tvhlog ( const char *file, int line, const char *subsys, const char *fmt, ... ) { va_list args; + pthread_mutex_lock(&tvhlog_mutex); va_start(args, fmt); tvhlogv(file, line, notify, severity, subsys, fmt, args); va_end(args); + pthread_mutex_unlock(&tvhlog_mutex); } + +/* + * Log a hexdump + */ +#define HEXDUMP_WIDTH 16 +void +_tvhlog_hexdump(const char *file, int line, + int notify, int severity, + const char *subsys, + const uint8_t *data, ssize_t len) +{ + int i, c; + char str[1024]; + va_list args; + + pthread_mutex_lock(&tvhlog_mutex); + while (len > 0) { + c = 0; + for (i = 0; i < HEXDUMP_WIDTH; i++) { + if (i >= len) + c += snprintf(str+c, sizeof(str)-c, " "); + else + c += snprintf(str+c, sizeof(str)-c, "%02X ", data[i]); + } + for (i = 0; i < HEXDUMP_WIDTH; i++) { + if (i < len) { + if (data[i] < ' ' || data[i] > '~') + str[c] = '.'; + else + str[c] = data[i]; + } else + str[c] = ' '; + c++; + } + str[c] = '\0'; + tvhlogv(file, line, notify, severity, subsys, str, args); + len -= HEXDUMP_WIDTH; + data += HEXDUMP_WIDTH; + } + pthread_mutex_unlock(&tvhlog_mutex); +} + diff --git a/src/tvhlog.h b/src/tvhlog.h index a00a0dad..69587e0d 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -41,6 +41,10 @@ void _tvhlog ( const char *file, int line, int notify, int severity, const char *subsys, const char *fmt, ... ) __attribute__((format(printf,6,7))); +void _tvhlog_hexdump ( const char *file, int line, + int notify, int severity, + const char *subsys, + const uint8_t *data, ssize_t len ); /* Options */ @@ -67,8 +71,11 @@ void _tvhlog ( const char *file, int line, #if ENABLE_TRACE #define tvhtrace(subsys, fmt, ...)\ _tvhlog(__FILE__, __LINE__, 0, LOG_TRACE, subsys, fmt, ##__VA_ARGS__) +#define tvhlog_hexdump(subsys, data, len)\ + _tvhlog_hexdump(__FILE__, __LINE__, 0, LOG_TRACE, subsys, (uint8_t*)data, len) #else #define tvhtrace(...) (void)0 +#define tvhlog_hexdump(...) (void)0 #endif #endif /* __TVH_LOGGING_H__ */ From 03ec14e0f58e6801803e61dec45375101364e743 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 11:31:21 +0100 Subject: [PATCH 472/503] tvhlog: ignore empty subsys values --- src/tvhlog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tvhlog.c b/src/tvhlog.c index e0466e94..6e0a2332 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -74,6 +74,7 @@ void tvhlog_set_subsys ( const char *subsys ) while ( t ) { subsys = NULL; a = 1; + if (!*t) goto next; if (t[0] == '+' || t[0] == '-') { a = t[0] == '+'; t++; @@ -86,6 +87,7 @@ void tvhlog_set_subsys ( const char *subsys ) if (!tvhlog_subsys) tvhlog_subsys = htsmsg_create_map(); htsmsg_set_u32(tvhlog_subsys, t, a); +next: t = strtok_r(NULL, ",", &r); } free(s); From 3d5d13b7262c9e8c98bd7d6708a23a1af9a09b58 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 11:32:19 +0100 Subject: [PATCH 473/503] tdt: add trace hexdump to sdt/nit parsers. --- src/dvb/dvb_tables.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index ac0226c5..11d6ce08 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -349,7 +349,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if (!tdmi) return -1; } tvhtrace("sdt", "onid %04X tsid %04X", onid, tsid); - //hexdump("sdt", ptr, len); + tvhlog_hexdump("sdt", ptr, len); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; @@ -873,6 +873,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, netname[0] = '\0'; tvhtrace("nit", "tableid 0x%02x", tableid); + tvhlog_hexdump("nit", ptr, len); /* Check NID */ if(tdmi->tdmi_adapter->tda_nitoid && From 9abf21525aade2f7bb54cc931960270237048b53 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 8 Apr 2013 19:38:50 +0100 Subject: [PATCH 474/503] dvr: attempt to stop new DVR code creating false entries --- src/dvr/dvr_db.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 4e577499..d6a1562c 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -820,22 +820,32 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) de->de_id, epg_broadcast_get_title(e, NULL), e->channel->ch_name, e->start, e->stop); + /* Ignore - already in progress */ + if (de->de_sched_state != DVR_SCHEDULED) + return; + /* Unlink the broadcast */ e->putref(e); de->de_bcast = NULL; + /* If this was craeted by autorec - just remove it, it'll get recreated */ + if (de->de_autorec) { + dvr_entry_remove(de); + /* Find match */ - RB_FOREACH(e, &e->channel->ch_epg_schedule, sched_link) { - if (dvr_entry_fuzzy_match(de, e)) { - tvhtrace("dvr", - " replacement event %s on %s @ %"PRItime_t - " to %"PRItime_t, - epg_broadcast_get_title(e, NULL), e->channel->ch_name, - e->start, e->stop); - e->getref(e); - de->de_bcast = e; - _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); - break; + } else { + RB_FOREACH(e, &e->channel->ch_epg_schedule, sched_link) { + if (dvr_entry_fuzzy_match(de, e)) { + tvhtrace("dvr", + " replacement event %s on %s @ %"PRItime_t + " to %"PRItime_t, + epg_broadcast_get_title(e, NULL), e->channel->ch_name, + e->start, e->stop); + e->getref(e); + de->de_bcast = e; + _dvr_entry_update(de, e, NULL, NULL, NULL, 0, 0, 0, 0); + break; + } } } } From 36f9bd6652ec45ff40000495024a28d6c1fda567 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 11:53:11 +0100 Subject: [PATCH 475/503] tvhlog: fix some silly errors. --- src/tvhlog.c | 5 +++-- src/tvhlog.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tvhlog.c b/src/tvhlog.c index 6e0a2332..342bbb76 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -60,7 +60,7 @@ tvhlog_init ( int level, int options, const char *path ) void tvhlog_set_subsys ( const char *subsys ) { uint32_t a; - char *t, *r, *s; + char *t, *r = NULL, *s; if (tvhlog_subsys) htsmsg_destroy(tvhlog_subsys); @@ -212,11 +212,12 @@ void _tvhlog_hexdump(const char *file, int line, int notify, int severity, const char *subsys, - const uint8_t *data, ssize_t len) + const uint8_t *data, ssize_t len, ...) { int i, c; char str[1024]; va_list args; + va_start(args, len); pthread_mutex_lock(&tvhlog_mutex); while (len > 0) { diff --git a/src/tvhlog.h b/src/tvhlog.h index 69587e0d..c36a3c44 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -44,7 +44,7 @@ void _tvhlog ( const char *file, int line, void _tvhlog_hexdump ( const char *file, int line, int notify, int severity, const char *subsys, - const uint8_t *data, ssize_t len ); + const uint8_t *data, ssize_t len, ... ); /* Options */ From da25793bcbd8fdc9a4dcd74fd51bc8e7a1e52235 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 12:05:31 +0100 Subject: [PATCH 476/503] Fix some printing errors. --- src/timeshift/timeshift_reader.c | 4 ++-- src/tvheadend.h | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index 3701ddee..f2a3b6bc 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -318,7 +318,7 @@ static int _timeshift_read ts->id, (*cur_file)->path); *fd = open((*cur_file)->path, O_RDONLY); } - tvhtrace("timeshift", "ts %d seek to %lu", ts->id, *cur_off); + tvhtrace("timeshift", "ts %d seek to %"PRIoff_t, ts->id, *cur_off); lseek(*fd, *cur_off, SEEK_SET); /* Read msg */ @@ -329,7 +329,7 @@ static int _timeshift_read tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } - tvhtrace("timeshift", "ts %d read msg %p (%ld)", + tvhtrace("timeshift", "ts %d read msg %p (%"PRIssize_t")", ts->id, *sm, r); /* Incomplete */ diff --git a/src/tvheadend.h b/src/tvheadend.h index f0dbdcd4..55cdd517 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -549,10 +549,22 @@ int rmtree ( const char *path ); char *regexp_escape ( const char *str ); /* printing */ -#if __SIZEOF_LONG__ == 8 - #define PRItime_t PRId64 +# if __WORDSIZE == 64 +#define PRIsword_t PRId64 +#define PRIuword_t PRIu64 #else - #define PRItime_t "l" PRId32 +#define PRIsword_t PRId32 +#define PRIuword_t PRIu32 +#endif +#define PRIslongword_t "ld" +#define PRIulongword_t "lu" +#define PRIsize_t PRIuword_t +#define PRIssize_t PRIsword_t +#define PRItime_t PRIslongword_t +#if _FILE_OFFSET_BITS == 64 +#define PRIoff_t PRId64 +#else +#define PRIoff_t PRIslongword_t #endif #endif /* TV_HEAD_H */ From c828849620a310aa596dc01571701b89aafbb333 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 16:30:13 +0100 Subject: [PATCH 477/503] tvhlog: add UI debug to allow run-time updates to the debugging config --- src/main.c | 3 + src/tvhlog.c | 19 ++++++ src/tvhlog.h | 1 + src/webui/extjs.c | 85 +++++++++++++++++++++++++ src/webui/static/app/tvheadend.js | 3 + src/webui/static/app/tvhlog.js | 102 ++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+) create mode 100644 src/webui/static/app/tvhlog.js diff --git a/src/main.c b/src/main.c index 02ee83a3..80a66e4a 100644 --- a/src/main.c +++ b/src/main.c @@ -131,6 +131,9 @@ const tvh_caps_t tvheadend_capabilities[] = { #endif #if ENABLE_TIMESHIFT { "timeshift", ×hift_enabled }, +#endif +#if ENABLE_TRACE + { "trace", NULL }, #endif { NULL, NULL } }; diff --git a/src/tvhlog.c b/src/tvhlog.c index 342bbb76..bbb11d03 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -56,6 +56,25 @@ tvhlog_init ( int level, int options, const char *path ) pthread_mutex_init(&tvhlog_mutex, NULL); } +/* Get subsys */ +void tvhlog_get_subsys ( char *subsys, size_t len ) +{ + size_t c = 0; + int first = 1; + htsmsg_field_t *f; + *subsys = '\0'; + if (tvhlog_subsys) { + HTSMSG_FOREACH(f, tvhlog_subsys) { + if (f->hmf_type != HMF_S64) continue; + c += snprintf(subsys+c, len-c, "%c%s%s", + f->hmf_s64 ? '+' : '-', + f->hmf_name, + first ? "" : ","); + first = 0; + } + } +} + /* Set subsys */ void tvhlog_set_subsys ( const char *subsys ) { diff --git a/src/tvhlog.h b/src/tvhlog.h index c36a3c44..bde46a50 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -34,6 +34,7 @@ extern pthread_mutex_t tvhlog_mutex; /* Initialise */ void tvhlog_init ( int level, int options, const char *path ); void tvhlog_set_subsys ( const char *subsys ); +void tvhlog_get_subsys ( char *subsys, size_t len ); void tvhlogv ( const char *file, int line, int notify, int severity, const char *subsys, const char *fmt, va_list args ); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 6e793562..fd10ef8a 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -146,6 +146,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) extjs_load(hq, "static/app/dvr.js"); extjs_load(hq, "static/app/epggrab.js"); extjs_load(hq, "static/app/config.js"); + extjs_load(hq, "static/app/tvhlog.js"); extjs_load(hq, "static/app/status.js"); /** @@ -2054,6 +2055,89 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque) return 0; } +/** + * + */ +static int +extjs_tvhlog(http_connection_t *hc, const char *remain, void *opaque) +{ + htsbuf_queue_t *hq = &hc->hc_reply; + const char *op = http_arg_get(&hc->hc_req_args, "op"); + htsmsg_t *out, *m; + + if(op == NULL) + return 400; + + pthread_mutex_lock(&global_lock); + + if(http_access_verify(hc, ACCESS_ADMIN)) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_UNAUTHORIZED; + } + + pthread_mutex_unlock(&global_lock); + + /* Basic settings */ + if(!strcmp(op, "loadSettings")) { + char str[2048]; + + /* Get config */ + pthread_mutex_lock(&tvhlog_mutex); + m = htsmsg_create_map(); + htsmsg_add_u32(m, "tvhlog_level", tvhlog_level); + htsmsg_add_u32(m, "tvhlog_trace", tvhlog_level > LOG_DEBUG); + tvhlog_get_subsys(str, sizeof(str)); + htsmsg_add_str(m, "tvhlog_subsys", str); + htsmsg_add_str(m, "tvhlog_path", tvhlog_path ?: ""); + htsmsg_add_u32(m, "tvhlog_options", tvhlog_options); + htsmsg_add_u32(m, "tvhlog_dbg_syslog", + tvhlog_options & TVHLOG_OPT_DBG_SYSLOG); + pthread_mutex_unlock(&tvhlog_mutex); + + if (!m) return HTTP_STATUS_BAD_REQUEST; + out = json_single_record(m, "config"); + + /* Save settings */ + } else if (!strcmp(op, "saveSettings") ) { + const char *str; + + pthread_mutex_lock(&tvhlog_mutex); + if ((str = http_arg_get(&hc->hc_req_args, "tvhlog_level"))) + tvhlog_level = atoi(str); + if ((str = http_arg_get(&hc->hc_req_args, "tvhlog_trace"))) + tvhlog_level = LOG_TRACE; + else + tvhlog_level = LOG_DEBUG; + if ((str = http_arg_get(&hc->hc_req_args, "tvhlog_path"))) { + free(tvhlog_path); + if (*str) + tvhlog_path = strdup(str); + else + tvhlog_path = NULL; + } + if ((str = http_arg_get(&hc->hc_req_args, "tvhlog_options"))) + tvhlog_options = atoi(str); + if ((str = http_arg_get(&hc->hc_req_args, "tvhlog_dbg_syslog"))) + tvhlog_options |= TVHLOG_OPT_DBG_SYSLOG; + else + tvhlog_options &= ~TVHLOG_OPT_DBG_SYSLOG; + tvhlog_set_subsys(http_arg_get(&hc->hc_req_args, "tvhlog_subsys")); + pthread_mutex_unlock(&tvhlog_mutex); + + out = htsmsg_create_map(); + htsmsg_add_u32(out, "success", 1); + + } else { + return HTTP_STATUS_BAD_REQUEST; + } + + htsmsg_json_serialize(out, hq, 0); + htsmsg_destroy(out); + http_output_content(hc, "text/x-json; charset=UTF-8"); + + return 0; +} + /** * Capability check */ @@ -2174,6 +2258,7 @@ extjs_start(void) #if ENABLE_TIMESHIFT http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN); #endif + http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN); #if ENABLE_LINUXDVB extjs_start_dvb(); diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 1db51311..761be214 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -320,6 +320,9 @@ function accessUpdate(o) { tabs1.push(tvheadend.conf_csa); } + /* Debug */ + tabs1.push(new tvheadend.tvhlog); + tvheadend.confpanel = new Ext.TabPanel({ activeTab : 0, autoScroll : true, diff --git a/src/webui/static/app/tvhlog.js b/src/webui/static/app/tvhlog.js new file mode 100644 index 00000000..5737405f --- /dev/null +++ b/src/webui/static/app/tvhlog.js @@ -0,0 +1,102 @@ +tvheadend.tvhlog = function() { + /* + * Basic Config + */ + var confreader = new Ext.data.JsonReader({ + root : 'config' + }, [ 'tvhlog_path', 'tvhlog_dbg_syslog', 'tvhlog_subsys', 'tvhlog_trace' ]); + + /* **************************************************************** + * Form Fields + * ***************************************************************/ + + var tvhlogLogPath = new Ext.form.TextField({ + fieldLabel : 'Debug Log Path', + name : 'tvhlog_path', + allowBlank : true, + width: 400 + }); + + var tvhlogToSyslog = new Ext.form.Checkbox({ + name: 'tvhlog_dbg_syslog', + fieldLabel: 'Debug to syslog' + }); + + var tvhlogTrace = new Ext.form.Checkbox({ + name: 'tvhlog_trace', + fieldLabel: 'Debug trace (low-level stuff)' + }); + + var tvhlogSubsys = new Ext.form.TextField({ + fieldLabel : 'Debug Subsystems', + name : 'tvhlog_subsys', + allowBlank : true, + width: 400 + }); + + /* **************************************************************** + * Form + * ***************************************************************/ + + var saveButton = new Ext.Button({ + text : "Save configuration", + tooltip : 'Save changes made to configuration below', + iconCls : 'save', + handler : saveChanges + }); + + var helpButton = new Ext.Button({ + text : 'Help', + handler : function() { + new tvheadend.help('Debug Configuration', 'config_tvhlog.html'); + } + }); + + var confpanel = new Ext.form.FormPanel({ + title : 'Debugging', + iconCls : 'wrench', + border : false, + bodyStyle : 'padding:15px', + labelAlign : 'left', + labelWidth : 200, + waitMsgTarget : true, + reader : confreader, + layout : 'form', + defaultType : 'textfield', + autoHeight : true, + items : [ tvhlogLogPath, tvhlogToSyslog, + tvhlogTrace, tvhlogSubsys ], + tbar : [ saveButton, '->', helpButton ] + }); + + /* **************************************************************** + * Load/Save + * ***************************************************************/ + + confpanel.on('render', function() { + confpanel.getForm().load({ + url : 'tvhlog', + params : { + op : 'loadSettings' + }, + success : function(form, action) { + confpanel.enable(); + } + }); + }); + + function saveChanges() { + confpanel.getForm().submit({ + url : 'tvhlog', + params : { + op : 'saveSettings' + }, + waitMsg : 'Saving Data...', + failure : function(form, action) { + Ext.Msg.alert('Save failed', action.result.errormsg); + } + }); + } + + return confpanel; +} From aa0e5b1219b7193976e36254654cfd1f7b21d30c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 16:37:53 +0100 Subject: [PATCH 478/503] init: remove TVH_TRACE option, users can do that one manually --- debian/tvheadend.default | 4 ---- debian/tvheadend.init | 1 - debian/tvheadend.upstart | 1 - 3 files changed, 6 deletions(-) diff --git a/debian/tvheadend.default b/debian/tvheadend.default index 1fda3e62..29b6dbef 100644 --- a/debian/tvheadend.default +++ b/debian/tvheadend.default @@ -47,10 +47,6 @@ TVH_HTSP_PORT="" # if set to 1 will output debug to syslog TVH_DEBUG=0 -# TVH_TRACE -# enable trace in subsystems -TVH_TRACE="" - # TVH_DELAY # if set startup will be delayed N seconds to allow hardware init TVH_DELAY="" diff --git a/debian/tvheadend.init b/debian/tvheadend.init index 5c93f8bb..b4aea9cb 100644 --- a/debian/tvheadend.init +++ b/debian/tvheadend.init @@ -38,7 +38,6 @@ ARGS="-f" [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" -[ -z "$TVH_TRACE" ] || ARGS="$ARGS --trace $TVH_TRACE" # Load the VERBOSE setting and other rcS variables [ -f /etc/default/rcS ] && . /etc/default/rcS diff --git a/debian/tvheadend.upstart b/debian/tvheadend.upstart index 745b373f..a5d70404 100644 --- a/debian/tvheadend.upstart +++ b/debian/tvheadend.upstart @@ -27,7 +27,6 @@ script [ -z "$TVH_HTTP_ROOT" ] || ARGS="$ARGS --http_root $TVH_HTTP_ROOT" [ -z "$TVH_HTSP_PORT" ] || ARGS="$ARGS --htsp_port $TVH_HTSP_PORT" [ "$TVH_DEBUG" = "1" ] && ARGS="$ARGS -s" - [ -z "$TVH_TRACE" ] || ARGS="$ARGS --trace $TVH_TRACE" [ ! -z "$TVH_DELAY" ] && sleep $TVH_DELAY From 46704009e12c40ddc54792a99b0a912f2aa3c12c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 10 Apr 2013 21:06:20 +0100 Subject: [PATCH 479/503] Issue #1692 - dvb: fix DVB v3 builds --- src/dvb/dvb_adapter.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index be8e32a0..00a53704 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -1273,13 +1273,14 @@ dvb_fe_opts(th_dvb_adapter_t *tda, const char *which) } if(!strcmp(which, "delsys")) { -#if DVB_API_VERSION >= 5 if(c & FE_CAN_QPSK) { +#if DVB_API_VERSION >= 5 fe_opts_add(a, "SYS_DVBS", SYS_DVBS); fe_opts_add(a, "SYS_DVBS2", SYS_DVBS2); - } else +#else + fe_opts_add(a, "SYS_DVBS", -1); #endif - fe_opts_add(a, "SYS_UNDEFINED", SYS_UNDEFINED); + } return a; } From 9c325854495a47b2ee8a41c6486b5e985df795a9 Mon Sep 17 00:00:00 2001 From: virtualdj Date: Thu, 6 Dec 2012 22:12:26 +0100 Subject: [PATCH 480/503] Fixes the implicit declaration of function 'llabs' warning when using older gcc versions --- src/tvhtime.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tvhtime.c b/src/tvhtime.c index 81ef8fa6..e19d6c8d 100644 --- a/src/tvhtime.c +++ b/src/tvhtime.c @@ -1,3 +1,5 @@ +#define _ISOC9X_SOURCE + #include #include #include From 9d7e51f9c5de77e666e70ca3d7f03c11b7fe9520 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 15 Apr 2013 10:15:26 +0100 Subject: [PATCH 481/503] redhat: moved to tvheadend-packaging --- contrib/redhat/tvheadend | 128 ---------------------------------- contrib/redhat/tvheadend.spec | 75 -------------------- 2 files changed, 203 deletions(-) delete mode 100755 contrib/redhat/tvheadend delete mode 100644 contrib/redhat/tvheadend.spec diff --git a/contrib/redhat/tvheadend b/contrib/redhat/tvheadend deleted file mode 100755 index fa4b9100..00000000 --- a/contrib/redhat/tvheadend +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/sh -# -# tvheadend Start/Stop the hts tvheadend daemon. -# -# chkconfig: 345 90 60 -# description: Tvheadend is a TV streaming server for Linux supporting -# DVB-S, DVB-S2, DVB-C, DVB-T, ATSC, IPTV, and Analog video -# (V4L) as input sources. It also comes with a powerful and -# easy to use web interface both used for configuration and -# day-to-day operations, such as searching the EPG and -# scheduling recordings. - -### BEGIN INIT INFO -# Provides: tvheadend -# Required-Start: $local_fs $syslog -# Required-Stop: $local_fs $syslog -# Default-Start: 345 -# Default-Stop: 90 -# Short-Description: run tvheadend daemon -# Description: Tvheadend is a TV streaming server for Linux supporting -# DVB-S, DVB-S2, DVB-C, DVB-T, ATSC, IPTV, and Analog video -# (V4L) as input sources. It also comes with a powerful and -# easy to use web interface both used for configuration and -# day-to-day operations, such as searching the EPG and -# scheduling recordings. -### END INIT INFO - -[ -f /etc/sysconfig/tvheadend ] || { - [ "$1" = "status" ] && exit 4 || exit 6 -} - -RETVAL=0 -prog="tvheadend" -exec=/usr/local/bin/tvheadend -lockfile=/var/lock/subsys/tvheadend -sysconfig=/etc/sysconfig/tvheadend - -# Source function library. -. /etc/rc.d/init.d/functions - -[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog - -start() { - [ -x $exec ] || exit 5 - [ -f $sysconfig ] || exit 6 - echo -n $"Starting $prog: " - daemon $exec $OPTIONS - retval=$? - echo - [ $retval -eq 0 ] && touch $lockfile -} - -stop() { - echo -n $"Stopping $prog: " - if [ -n "`pidfileofproc $exec`" ]; then - killproc $exec - RETVAL=3 - else - failure $"Stopping $prog" - fi - retval=$? - echo - [ $retval -eq 0 ] && rm -f $lockfile -} - -restart() { - stop - start -} - -reload() { - echo -n $"Reloading $prog: " - if [ -n "`pidfileofproc $exec`" ]; then - killproc $exec -HUP - else - failure $"Reloading $prog" - fi - retval=$? - echo -} - -force_reload() { - # new configuration takes effect after restart - restart -} - -rh_status() { - # run checks to determine if the service is running or use generic status - status -p /var/run/tvheadend.pid $prog -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - - -case "$1" in - start) - rh_status_q && exit 0 - $1 - ;; - stop) - rh_status_q || exit 0 - $1 - ;; - restart) - $1 - ;; - reload) - rh_status_q || exit 7 - $1 - ;; - force-reload) - force_reload - ;; - status) - rh_status - ;; - condrestart|try-restart) - rh_status_q || exit 0 - restart - ;; - *) - echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" - exit 2 -esac -exit $? - diff --git a/contrib/redhat/tvheadend.spec b/contrib/redhat/tvheadend.spec deleted file mode 100644 index dd2fa8c4..00000000 --- a/contrib/redhat/tvheadend.spec +++ /dev/null @@ -1,75 +0,0 @@ -Name: tvheadend -Summary: TV streaming server -Version: 2.12.cae47cf -Release: 1%{dist} -License: GPL -Group: Applications/Multimedia -URL: http://www.lonelycoder.com/tvheadend -Packager: Jouk Hettema -Source: tvheadend-%{version}.tar.bz2 -Prefix: /usr -BuildRequires: avahi-devel, openssl, glibc, zlib - -%description -Tvheadend is a TV streaming server for Linux supporting DVB-S, DVB-S2, -DVB-C, DVB-T, ATSC, IPTV, and Analog video (V4L) as input sources. - -%prep -%setup -q - -%build -%configure --release --prefix=%{prefix}/bin -%{__make} - -%install -%{__rm} -rf %{buildroot} - -mkdir -p $RPM_BUILD_ROOT/%{prefix} - -%{__install} -d -m0755 %{buildroot}%{prefix}/bin -%{__install} -d -m0755 %{buildroot}/etc/tvheadend -%{__install} -d -m0755 %{buildroot}/etc/sysconfig -%{__install} -d -m0755 %{buildroot}/etc/rc.d/init.d -%{__install} -d -m0755 %{buildroot}%{prefix}/shared/man/man1 - -%{__install} -m0755 build.Linux/tvheadend %{buildroot}%{prefix}/bin/ -%{__install} -m0755 man/tvheadend.1 %{buildroot}%{prefix}/shared/man/man1 -%{__install} -m0755 contrib/redhat/tvheadend %{buildroot}/etc/rc.d/init.d - -cat >> %{buildroot}/etc/sysconfig/tvheadend << EOF -OPTIONS="-f -u tvheadend -g tvheadend -c /etc/tvheadend -s -C" -EOF - -%pre -groupadd -f -r tvheadend >/dev/null 2>&! || : -useradd -s /sbin/nologin -c "Tvheadend" -M -r tvheadend -g tvheadend -G video >/dev/null 2>&! || : - -%preun -if [ $1 = 0 ]; then - /sbin/service tvheadend stop >/dev/null 2>&1 - /sbin/chkconfig --del tvheadend -fi - -%post -/sbin/chkconfig --add tvheadend - -if [ "$1" -ge "1" ]; then - /sbin/service tvheadend condrestart >/dev/null 2>&1 || : -fi - -%clean -%{__rm} -rf %{buildroot} - -%files -%defattr(-,root,root) -%doc debian/changelog LICENSE README -%dir %attr(-,tvheadend,root) %{prefix} -%{prefix}/bin/tvheadend -%{prefix}/shared/man/man1/tvheadend.1* -%{_sysconfdir}/rc.d/init.d/tvheadend -/etc/tvheadend -%config /etc/sysconfig/tvheadend - -%changelog -* Fri Feb 18 2011 Jouk Hettema - 2.12.cae47cf -- initial build for fedora 14 From 82a1edd132d5531823e3673fbb9c08105780e59c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 15 Apr 2013 12:13:44 +0100 Subject: [PATCH 482/503] htsp: fix bug causing imagecache file open to crash. --- src/htsp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index f0728cfb..97fa950d 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1521,7 +1521,7 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) int fd = imagecache_open(atoi(s2)); if (fd <= 0) return htsp_error("failed to open image"); - return htsp_file_open(htsp, NULL, fd); + return htsp_file_open(htsp, str, fd); } else { return htsp_error("Unknown file"); From c80088ac2396a453a8d8fcd84db259821d14e467 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 15 Apr 2013 16:23:10 +0100 Subject: [PATCH 483/503] htsp: bump version number due to imagecache fix --- src/htsp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 97fa950d..f68d016d 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -57,7 +57,7 @@ static void *htsp_server, *htsp_server_2; -#define HTSP_PROTO_VERSION 9 +#define HTSP_PROTO_VERSION 10 #define HTSP_ASYNC_OFF 0x00 #define HTSP_ASYNC_ON 0x01 From cbae62c053aba1386fcb47b65df28b37ce5c1f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rblom?= Date: Tue, 16 Apr 2013 13:41:36 +0200 Subject: [PATCH 484/503] xmltv: fixed episode/season count being off by one. --- src/epggrab/module/xmltv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index e00d002b..8c7bc389 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -184,8 +184,8 @@ static const char *xmltv_ns_get_parse_num out: - if(ap) *ap = a + 1; - if(bp) *bp = b + 1; + if(ap && a >= 0) *ap = a + 1; + if(bp && b >= 0) *bp = b; return s; } From 3581869d83831f80dd7564ea4bb2a25b68ecf087 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 16 Apr 2013 21:11:11 +0100 Subject: [PATCH 485/503] tdt: ignore other network in NIT this causes havoc with DVB-S networks. This is particularly true where the other network is only available via another orbital position (we could check for this) but it also causes problems with other transmission types so I think we'll just ignore for now. --- src/dvb/dvb_tables.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 11d6ce08..d13da580 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -875,6 +875,9 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tvhtrace("nit", "tableid 0x%02x", tableid); tvhlog_hexdump("nit", ptr, len); + /* Ignore other network */ + if(tableid != 0x40) return -1; + /* Check NID */ if(tdmi->tdmi_adapter->tda_nitoid && tdmi->tdmi_adapter->tda_nitoid != network_id) @@ -901,7 +904,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, case DVB_DESC_NETWORK_NAME: if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL)) return -1; - if(tableid == 0x40 && (!tdmi->tdmi_network || *tdmi->tdmi_network == '\0')) + if(!tdmi->tdmi_network || *tdmi->tdmi_network == '\0') dvb_mux_set_networkname(tdmi, netname); break; } From b672a9da50915d7be6e35ffedf75669067803187 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 16 Apr 2013 21:25:56 +0100 Subject: [PATCH 486/503] tdt: some changes to SDT processing to pick up unknown services --- src/dvb/dvb_tables.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index d13da580..7397561e 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -336,6 +336,11 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tsid = ptr[0] << 8 | ptr[1]; onid = ptr[5] << 8 | ptr[6]; + + tvhtrace("sdt", "onid %04X tsid %04X", onid, tsid); + tvhlog_hexdump("sdt", ptr, len); + + /* Find Transport Stream */ if (tableid == 0x42) { dvb_mux_set_tsid(tdmi, tsid, 0); dvb_mux_set_onid(tdmi, onid, 0); @@ -348,8 +353,6 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, break; if (!tdmi) return -1; } - tvhtrace("sdt", "onid %04X tsid %04X", onid, tsid); - tvhlog_hexdump("sdt", ptr, len); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; @@ -427,12 +430,14 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } } - if (!servicetype_is_tv(stype) && - !servicetype_is_radio(stype)) - continue; + if (!(t = dvb_service_find3(NULL, tdmi, NULL, 0, 0, service_id, 0, 0))) { + if (!servicetype_is_tv(stype) && + !servicetype_is_radio(stype)) + continue; - if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) - continue; + if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) + continue; + } if(t->s_servicetype != stype || t->s_scrambled != free_ca_mode) { From 34e7acc08729f7ea114141eefbe6a364758a6518 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 16 Apr 2013 21:50:52 +0100 Subject: [PATCH 487/503] services: include info from SDT (for Other services) if they already exist. Services can be added from a variety of other tables which probably indicates that they are in fact valid TV/Radio services, but we just don't know the type. This might help people to map these services manually within TVH. --- src/dvb/dvb_service.c | 5 ++++- src/service.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 31e25e64..f8f8def6 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -485,7 +485,10 @@ dvb_service_build_msg(service_t *t) htsmsg_add_u32(m, "pmt", t->s_pmt_pid); htsmsg_add_u32(m, "pcr", t->s_pcr_pid); - htsmsg_add_str(m, "type", service_servicetype_txt(t)); + snprintf(buf, sizeof(buf), "%s (0x%04X)", service_servicetype_txt(t), t->s_servicetype); + htsmsg_add_str(m, "type", buf); + htsmsg_add_str(m, "typestr", service_servicetype_txt(t)); + htsmsg_add_u32(m, "typenum", t->s_servicetype); htsmsg_add_str(m, "svcname", t->s_svcname ?: ""); htsmsg_add_str(m, "provider", t->s_provider ?: ""); diff --git a/src/service.c b/src/service.c index 2bf84aa2..7851f5c2 100644 --- a/src/service.c +++ b/src/service.c @@ -763,8 +763,8 @@ static struct strtab stypetab[] = { { "HDTV", ST_DN_HDTV }, { "SDTV", ST_SK_SDTV }, { "SDTV", ST_NE_SDTV }, - { "SDTV-AC", ST_AC_SDTV }, - { "HDTV-AC", ST_AC_HDTV }, + { "SDTV", ST_AC_SDTV }, + { "HDTV", ST_AC_HDTV }, }; const char * From ed27c4b3dd94971289bda05695e81d1d85e13eb9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 30 Mar 2013 13:01:43 +0000 Subject: [PATCH 488/503] dvb: attempt to stop bad updates to network info. --- src/dvb/dvb.h | 2 +- src/dvb/dvb_multiplex.c | 50 ++++++++++++++++++++++++++--------------- src/dvb/dvb_preconf.c | 2 +- src/dvb/dvb_tables.c | 37 ++++++++++++++++++++---------- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 3b3e8f1f..127d676f 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -435,7 +435,7 @@ th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda, uint16_t onid, uint16_t tsid, const char *network, const char *logprefix, int enabled, int initialscan, const char *identifier, - dvb_satconf_t *satconf, int create); + dvb_satconf_t *satconf, int create, th_dvb_mux_instance_t *src); void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index f9985fd0..ce0f1909 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -155,7 +155,7 @@ th_dvb_mux_instance_t * dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, uint16_t onid, uint16_t tsid, const char *network, const char *source, int enabled, int initialscan, const char *identifier, - dvb_satconf_t *satconf, int create) + dvb_satconf_t *satconf, int create, th_dvb_mux_instance_t *src) { th_dvb_mux_instance_t *tdmi, *c; char buf[200]; @@ -177,7 +177,35 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, int save = 0; char buf2[1024]; buf2[0] = 0; + int master = 0; + if (!src) + master = 1; + else if (src->tdmi_network_id == tdmi->tdmi_network_id) + master = 1; + /* Network ID */ + if(tsid != 0xFFFF && tdmi->tdmi_transport_stream_id != tsid) { + if (tdmi->tdmi_transport_stream_id == 0xFFFF || master) { + tdmi->tdmi_transport_stream_id = tsid; + save = 1; + } + } + if(onid && tdmi->tdmi_network_id != onid) { + if (!tdmi->tdmi_network_id || master) { + tdmi->tdmi_network_id = onid; + save = 1; + } + } + if(network && *network && strcmp(tdmi->tdmi_network ?: "", network)) { + if (!tdmi->tdmi_network || master) { + free(tdmi->tdmi_network); + tdmi->tdmi_network = strdup(network); + save = 1; + } + } + + /* Tuning Info */ + // TODO: same protection here? if(tdmi->tdmi_adapter->tda_autodiscovery && tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { #if DVB_API_VERSION >= 5 @@ -201,20 +229,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, save = 1; } - if(tsid != 0xFFFF && tdmi->tdmi_transport_stream_id != tsid) { - tdmi->tdmi_transport_stream_id = tsid; - save = 1; - } - if(onid && tdmi->tdmi_network_id != onid) { - tdmi->tdmi_network_id = onid; - save = 1; - } - if(network && *network && strcmp(tdmi->tdmi_network ?: "", network)) { - free(tdmi->tdmi_network); - tdmi->tdmi_network = strdup(network); - save = 1; - } - /* HACK - load old transports and remove old mux config */ if(identifier) { save = 1; @@ -803,7 +817,7 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) tdmi = dvb_mux_create(tda, &dmc, onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled, initscan, - identifier, NULL, 1); + identifier, NULL, 1, NULL); if(tdmi != NULL) { if((s = htsmsg_get_str(m, "status")) != NULL) @@ -1203,7 +1217,7 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, } dmc.dmc_polarisation = polarisation; - tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL, 1); + tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL, 1, NULL); if(tdmi == NULL) return "Mux already exist"; @@ -1230,7 +1244,7 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, tdmi_src->tdmi_transport_stream_id, tdmi_src->tdmi_network, "copy operation", tdmi_src->tdmi_enabled, - 1, NULL, satconf, 1); + 1, NULL, satconf, 1, tdmi_src); if(tdmi_dst == NULL) return -1; // Already exist diff --git a/src/dvb/dvb_preconf.c b/src/dvb/dvb_preconf.c index d1805242..bf9e8e34 100644 --- a/src/dvb/dvb_preconf.c +++ b/src/dvb/dvb_preconf.c @@ -98,7 +98,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net, dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); - dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL, 1); + dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL, 1, NULL); } } diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 7397561e..07671dd2 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -448,17 +448,30 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if (chname && (strcmp(t->s_provider ?: "", provider) || strcmp(t->s_svcname ?: "", chname))) { - free(t->s_provider); - t->s_provider = strdup(provider); + int save2 = 0; + int master = 0; + if (t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_network_id && + t->s_dvb_mux_instance->tdmi_network_id == tdmi->tdmi_network_id) + master = 1; + + if (!t->s_provider || master) { + free(t->s_provider); + t->s_provider = strdup(provider); + save2 = 1; + } - free(t->s_svcname); - t->s_svcname = strdup(chname); + if (!t->s_svcname || master) { + free(t->s_svcname); + t->s_svcname = strdup(chname); + save2 = 1; + } - pthread_mutex_lock(&t->s_stream_mutex); - service_make_nicename(t); - pthread_mutex_unlock(&t->s_stream_mutex); - - save = 1; + if (save2) { + pthread_mutex_lock(&t->s_stream_mutex); + service_make_nicename(t); + pthread_mutex_unlock(&t->s_stream_mutex); + save = 1; + } } if (*crid && strcmp(t->s_default_authority ?: "", crid)) { @@ -692,7 +705,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, NULL, - tdmi->tdmi_adapter->tda_autodiscovery); + tdmi->tdmi_adapter->tda_autodiscovery, tdmi); return 0; } @@ -778,7 +791,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf, - tdmi->tdmi_adapter->tda_autodiscovery); + tdmi->tdmi_adapter->tda_autodiscovery, tdmi); return 0; } @@ -817,7 +830,7 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, NULL, - tdmi->tdmi_adapter->tda_autodiscovery); + tdmi->tdmi_adapter->tda_autodiscovery, tdmi); return 0; } From f442a28b775757ddbeb07bd03146660ab444891c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 16 Apr 2013 21:04:38 +0100 Subject: [PATCH 489/503] tvhlog: some efficiency improvements --- src/tvhlog.c | 64 +++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/tvhlog.c b/src/tvhlog.c index bbb11d03..381a8ec7 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -28,7 +28,6 @@ int tvhlog_level; int tvhlog_options; char *tvhlog_path; -int tvhlog_path_fail; htsmsg_t *tvhlog_subsys; pthread_mutex_t tvhlog_mutex; @@ -120,9 +119,11 @@ void tvhlogv ( const char *file, int line, struct timeval now; struct tm tm; char t[128], buf[2048], buf2[2048]; - uint32_t a; size_t l; int s; + int options; + char *path = NULL; + int skip = 0; /* Map down */ if (severity > LOG_DEBUG) @@ -130,35 +131,46 @@ void tvhlogv ( const char *file, int line, else s = severity; - /* Check debug enabled */ + /* Check debug enabled (and cache config) */ + pthread_mutex_lock(&tvhlog_mutex); if (severity >= LOG_DEBUG) { if (!tvhlog_subsys) - return; - if (severity > tvhlog_level) - return; - a = htsmsg_get_u32_or_default(tvhlog_subsys, "all", 0); - if (!htsmsg_get_u32_or_default(tvhlog_subsys, subsys, a)) - return; + skip = 1; + else if (severity > tvhlog_level) + skip = 1; + else { + uint32_t a = htsmsg_get_u32_or_default(tvhlog_subsys, "all", 0); + if (!htsmsg_get_u32_or_default(tvhlog_subsys, subsys, a)) + skip = 1; + } } + if (!skip) { + if (tvhlog_path) + path = strdup(tvhlog_path); + options = tvhlog_options; + } + pthread_mutex_unlock(&tvhlog_mutex); + if (skip) + return; /* Get time */ gettimeofday(&now, NULL); localtime_r(&now.tv_sec, &tm); l = strftime(t, sizeof(t), "%b %d %H:%M:%S", &tm); - if (tvhlog_options & TVHLOG_OPT_MILLIS) { + if (options & TVHLOG_OPT_MILLIS) { int ms = now.tv_usec / 1000; snprintf(t+l, sizeof(t)-l, ".%03d", ms); } /* Basic message */ l = snprintf(buf, sizeof(buf), "%s: ", subsys); - if (tvhlog_options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) + if (options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) l += snprintf(buf + l, sizeof(buf) - l, "(%s:%d) ", file, line); l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); /* Syslog */ - if (tvhlog_options & TVHLOG_OPT_SYSLOG) { - if (tvhlog_options & TVHLOG_OPT_DBG_SYSLOG || severity < LOG_DEBUG) { + if (options & TVHLOG_OPT_SYSLOG) { + if (options & TVHLOG_OPT_DBG_SYSLOG || severity < LOG_DEBUG) { syslog(s, "%s", buf); } } @@ -174,13 +186,13 @@ void tvhlogv ( const char *file, int line, } /* Console */ - if (tvhlog_options & TVHLOG_OPT_STDERR) { - if (tvhlog_options & TVHLOG_OPT_DBG_STDERR || severity < LOG_DEBUG) { + if (options & TVHLOG_OPT_STDERR) { + if (options & TVHLOG_OPT_DBG_STDERR || severity < LOG_DEBUG) { const char *leveltxt = logtxtmeta[severity][0]; const char *sgr = logtxtmeta[severity][1]; const char *sgroff; - if (tvhlog_options & TVHLOG_OPT_DECORATE) + if (options & TVHLOG_OPT_DECORATE) sgroff = "\033[0m"; else { sgr = ""; @@ -191,20 +203,16 @@ void tvhlogv ( const char *file, int line, } /* File */ - if (tvhlog_path) { - if (tvhlog_options & TVHLOG_OPT_DBG_FILE || severity < LOG_DEBUG) { + if (path) { + if (options & TVHLOG_OPT_DBG_FILE || severity < LOG_DEBUG) { const char *leveltxt = logtxtmeta[severity][0]; - FILE *fp = fopen(tvhlog_path, "a"); + FILE *fp = fopen(path, "a"); if (fp) { - tvhlog_path_fail = 0; fprintf(fp, "%s [%7s]:%s\n", t, leveltxt, buf); fclose(fp); - } else { - if (!tvhlog_path_fail) - syslog(LOG_WARNING, "failed to write log file %s", tvhlog_path); - tvhlog_path_fail = 1; } } + free(path); } } @@ -216,11 +224,11 @@ void _tvhlog ( const char *file, int line, const char *subsys, const char *fmt, ... ) { va_list args; - pthread_mutex_lock(&tvhlog_mutex); + //pthread_mutex_lock(&tvhlog_mutex); va_start(args, fmt); tvhlogv(file, line, notify, severity, subsys, fmt, args); va_end(args); - pthread_mutex_unlock(&tvhlog_mutex); + //pthread_mutex_unlock(&tvhlog_mutex); } /* @@ -238,7 +246,7 @@ _tvhlog_hexdump(const char *file, int line, va_list args; va_start(args, len); - pthread_mutex_lock(&tvhlog_mutex); + //pthread_mutex_lock(&tvhlog_mutex); while (len > 0) { c = 0; for (i = 0; i < HEXDUMP_WIDTH; i++) { @@ -262,6 +270,6 @@ _tvhlog_hexdump(const char *file, int line, len -= HEXDUMP_WIDTH; data += HEXDUMP_WIDTH; } - pthread_mutex_unlock(&tvhlog_mutex); + //pthread_mutex_unlock(&tvhlog_mutex); } From 99fe1c73a77896e3665ad861d620f7c19b757841 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 17 Apr 2013 13:14:44 +0100 Subject: [PATCH 490/503] tvhlog: fix some problems with hexdump code --- src/tvhlog.c | 14 ++++++++------ src/tvhlog.h | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/tvhlog.c b/src/tvhlog.c index 381a8ec7..5aad2f80 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -114,7 +114,7 @@ next: /* Log */ void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args ) + const char *subsys, const char *fmt, va_list args, int noargs ) { struct timeval now; struct tm tm; @@ -166,7 +166,10 @@ void tvhlogv ( const char *file, int line, l = snprintf(buf, sizeof(buf), "%s: ", subsys); if (options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) l += snprintf(buf + l, sizeof(buf) - l, "(%s:%d) ", file, line); - l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); + if (noargs) + l += snprintf(buf + l, sizeof(buf) - l, "%s", fmt); + else + l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); /* Syslog */ if (options & TVHLOG_OPT_SYSLOG) { @@ -226,7 +229,7 @@ void _tvhlog ( const char *file, int line, va_list args; //pthread_mutex_lock(&tvhlog_mutex); va_start(args, fmt); - tvhlogv(file, line, notify, severity, subsys, fmt, args); + tvhlogv(file, line, notify, severity, subsys, fmt, args, 0); va_end(args); //pthread_mutex_unlock(&tvhlog_mutex); } @@ -239,12 +242,11 @@ void _tvhlog_hexdump(const char *file, int line, int notify, int severity, const char *subsys, - const uint8_t *data, ssize_t len, ...) + const uint8_t *data, ssize_t len ) { int i, c; char str[1024]; va_list args; - va_start(args, len); //pthread_mutex_lock(&tvhlog_mutex); while (len > 0) { @@ -266,7 +268,7 @@ _tvhlog_hexdump(const char *file, int line, c++; } str[c] = '\0'; - tvhlogv(file, line, notify, severity, subsys, str, args); + tvhlogv(file, line, notify, severity, subsys, str, args, 1); len -= HEXDUMP_WIDTH; data += HEXDUMP_WIDTH; } diff --git a/src/tvhlog.h b/src/tvhlog.h index bde46a50..45a39cb2 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -37,7 +37,7 @@ void tvhlog_set_subsys ( const char *subsys ); void tvhlog_get_subsys ( char *subsys, size_t len ); void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args ); + const char *subsys, const char *fmt, va_list args, int noargs ); void _tvhlog ( const char *file, int line, int notify, int severity, const char *subsys, const char *fmt, ... ) @@ -45,7 +45,7 @@ void _tvhlog ( const char *file, int line, void _tvhlog_hexdump ( const char *file, int line, int notify, int severity, const char *subsys, - const uint8_t *data, ssize_t len, ... ); + const uint8_t *data, ssize_t len ); /* Options */ From 1ed10b2cf2b7e3d6bad3d4a3824dbe543175776f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 17 Apr 2013 13:15:18 +0100 Subject: [PATCH 491/503] dvb: add some additional tracing into NIT/SDT parsing --- src/dvb/dvb_tables.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 07671dd2..6b03fb41 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -718,8 +718,10 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint16_t tsid, uint16_t onid, const char *netname) { int freq, symrate; - // uint16_t orbital_pos; struct dvb_mux_conf dmc; +#if ENABLE_TRACE + uint16_t orbital_pos; +#endif if(len < 11) return -1; @@ -733,10 +735,15 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dmc.dmc_fe_params.frequency = freq * 10; tvhtrace("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); - if(!freq) + if(!freq) { + tvhlog(LOG_ERR, "nit", "invalid frequency (%d)", freq); return -1; + } - // orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]); +#if ENABLE_TRACE + orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]); +#endif + tvhtrace("nit", " orbital pos %d", orbital_pos); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + @@ -783,7 +790,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } if (dmc.dmc_fe_delsys == SYS_DVBS && dmc.dmc_fe_rolloff != ROLLOFF_35) { - printf ("error descriptor\n"); + tvhlog(LOG_ERR, "nit", "error descriptor"); return -1; } @@ -922,7 +929,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, case DVB_DESC_NETWORK_NAME: if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL)) return -1; - if(!tdmi->tdmi_network || *tdmi->tdmi_network == '\0') + if((tableid == 0x40) && (!tdmi->tdmi_network || *tdmi->tdmi_network == '\0')) dvb_mux_set_networkname(tdmi, netname); break; } From 1a95003b24fa3768a6cb519bda151c620cd9cfe7 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 18 Apr 2013 12:03:24 +0100 Subject: [PATCH 492/503] tvhlog: correct stupid error in va_list processing --- src/tvhlog.c | 13 ++++++------- src/tvhlog.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tvhlog.c b/src/tvhlog.c index 5aad2f80..13bd4486 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -114,7 +114,7 @@ next: /* Log */ void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args, int noargs ) + const char *subsys, const char *fmt, va_list args ) { struct timeval now; struct tm tm; @@ -166,10 +166,10 @@ void tvhlogv ( const char *file, int line, l = snprintf(buf, sizeof(buf), "%s: ", subsys); if (options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) l += snprintf(buf + l, sizeof(buf) - l, "(%s:%d) ", file, line); - if (noargs) - l += snprintf(buf + l, sizeof(buf) - l, "%s", fmt); - else + if (args) l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); + else + l += snprintf(buf + l, sizeof(buf) - l, "%s", fmt); /* Syslog */ if (options & TVHLOG_OPT_SYSLOG) { @@ -229,7 +229,7 @@ void _tvhlog ( const char *file, int line, va_list args; //pthread_mutex_lock(&tvhlog_mutex); va_start(args, fmt); - tvhlogv(file, line, notify, severity, subsys, fmt, args, 0); + tvhlogv(file, line, notify, severity, subsys, fmt, args); va_end(args); //pthread_mutex_unlock(&tvhlog_mutex); } @@ -246,7 +246,6 @@ _tvhlog_hexdump(const char *file, int line, { int i, c; char str[1024]; - va_list args; //pthread_mutex_lock(&tvhlog_mutex); while (len > 0) { @@ -268,7 +267,7 @@ _tvhlog_hexdump(const char *file, int line, c++; } str[c] = '\0'; - tvhlogv(file, line, notify, severity, subsys, str, args, 1); + tvhlogv(file, line, notify, severity, subsys, str, NULL); len -= HEXDUMP_WIDTH; data += HEXDUMP_WIDTH; } diff --git a/src/tvhlog.h b/src/tvhlog.h index 45a39cb2..e23e5ee6 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -37,7 +37,7 @@ void tvhlog_set_subsys ( const char *subsys ); void tvhlog_get_subsys ( char *subsys, size_t len ); void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args, int noargs ); + const char *subsys, const char *fmt, va_list args ); void _tvhlog ( const char *file, int line, int notify, int severity, const char *subsys, const char *fmt, ... ) From 89b18c05ca4a351db0f4ba8c423e9ec29759cd94 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 18 Apr 2013 13:02:22 +0100 Subject: [PATCH 493/503] docs: update documentation with new domain name and paypal link --- README | 6 +++--- README.md | 6 +++--- debian/control | 4 ++-- docs/html/overview.html | 10 +--------- docs/html/sysreq.html | 12 ++---------- man/tvheadend.1 | 4 ++-- src/main.c | 2 +- src/webui/extjs.c | 8 ++++---- support/changelog | 4 ++-- 9 files changed, 20 insertions(+), 36 deletions(-) diff --git a/README b/README index 56a138ad..dd1cc6ef 100644 --- a/README +++ b/README @@ -28,6 +28,6 @@ Further information ------------------- For more information about building, including generating packages please visit: -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Building -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Packaging -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Git +> https://tvheadend.org/projects/tvheadend/wiki/Building +> https://tvheadend.org/projects/tvheadend/wiki/Packaging +> https://tvheadend.org/projects/tvheadend/wiki/Git diff --git a/README.md b/README.md index 8899c76a..5f2c0675 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,6 @@ Further information ------------------- For more information about building, including generating packages please visit: -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Building -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Packaging -> https://www.lonelycoder.com/redmine/projects/tvheadend/wiki/Git +> https://tvheadend.org/projects/tvheadend/wiki/Building +> https://tvheadend.org/projects/tvheadend/wiki/Packaging +> https://tvheadend.org/projects/tvheadend/wiki/Git diff --git a/debian/control b/debian/control index 7c0de44a..29435c2a 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,7 @@ Source: tvheadend Section: video Priority: extra -Maintainer: Andreas Öman +Maintainer: Andreas Öman Build-Depends: debhelper (>= 7.0.50), pkg-config, libavahi-client-dev, libssl-dev, zlib1g-dev, wget, bzip2, libcurl4-gnutls-dev, git-core Standards-Version: 3.7.3 @@ -11,7 +11,7 @@ Depends: ${shlibs:Depends}, libavahi-client3, zlib1g Recommends: xmltv-util Enhances: showtime Replaces: hts-tvheadend -Homepage: http://www.lonelycoder.com/tvheadend +Homepage: https://tvheadend.org Description: Tvheadend Tvheadend is a TV streaming server for Linux supporting DVB, ATSC, IPTV, and Analog video (V4L) as input sources. Can be used as a backend to Showtime, XBMC and various other clients. diff --git a/docs/html/overview.html b/docs/html/overview.html index 735f95c9..36de92be 100644 --- a/docs/html/overview.html +++ b/docs/html/overview.html @@ -1,16 +1,8 @@

- -

-

HTS Tvheadend 3.2

+

Tvheadend

© 2006 - 2013, Andreas Öman, et al.

- -Tvheadend is part of the HTS project hosted at -http://www.lonelycoder.com/hts -

-It functions primarily as a TV streaming back end for the Showtime Mediaplayer but -can be used standalone for other purposes, such as a Digital Video Recorder.

diff --git a/docs/html/sysreq.html b/docs/html/sysreq.html index bf365f79..6aab8579 100644 --- a/docs/html/sysreq.html +++ b/docs/html/sysreq.html @@ -1,17 +1,9 @@

If you want to build Tvheadend from source, please visit -this page. +this page. Please note: that wiki development site only reflects the work -in HEAD (-current). It should not deviate much should you want to +in master. It should not deviate much should you want to build a released version from source, but you never know.

- -Tvheadend is part of the HTS project and is hosted at -https://www.lonelycoder.com/redmine/projects/tvheadend. - -

-It functions primarily as a TV backend for the Showtime Media player, but -can be used standalone for other purposes.

diff --git a/man/tvheadend.1 b/man/tvheadend.1 index d48655b8..c8648bf7 100644 --- a/man/tvheadend.1 +++ b/man/tvheadend.1 @@ -132,8 +132,8 @@ If daemonizing, tvheadend will writes it pid in /var/run/tvheadend.pid .B Tvheadend and this man page is maintained by Andreas Oeman .PP -(andreas a lonelycoder d com) +(andreas a tvheadend d org) .PP You may also visit #hts at irc.freenode.net .SH "SEE ALSO" -.BR http://www.lonelycoder.com/hts/ +.BR https://tvheadend.org diff --git a/src/main.c b/src/main.c index 80a66e4a..8e2d7ec8 100644 --- a/src/main.c +++ b/src/main.c @@ -319,7 +319,7 @@ show_usage } printf("\n"); printf("For more information please visit the Tvheadend website:\n"); - printf(" http://www.lonelycoder.com/tvheadend/\n"); + printf(" https://tvheadend.org\n"); printf("\n"); exit(0); } diff --git a/src/webui/extjs.c b/src/webui/extjs.c index fd10ef8a..c7a6dfcb 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -198,10 +198,10 @@ page_about(http_connection_t *hc, const char *remain, void *opaque) "
" "HTS Tvheadend %s" "

" - "© 2006 - 2012 Andreas \303\226man, et al.

" + "© 2006 - 2013 Andreas \303\226man, et al.

" "
" - "" - "http://www.lonelycoder.com/tvheadend

" + "" + "https://tvheadend.org

" "Based on software from " "ExtJS. " "Icons from " @@ -215,7 +215,7 @@ page_about(http_connection_t *hc, const char *remain, void *opaque) "All proceeds are used to support server infrastructure and buy test " "equipment." "
" - "" + "" "", tvheadend_version, tvheadend_version); diff --git a/support/changelog b/support/changelog index dabd2eae..33f1c919 100755 --- a/support/changelog +++ b/support/changelog @@ -10,7 +10,7 @@ VER=$3 # Defaults [ -z "$CHANGELOG" ] && CHANGELOG=$(dirname "$0")/../debian/changelog -[ -z "$DEBEMAIL" ] && DEBEMAIL="andreas@lonelycoder.com" +[ -z "$DEBEMAIL" ] && DEBEMAIL="andreas@tvheadend.org" [ -z "$DEBFULLNAME" ] && DEBFULLNAME="Andreas Öman" [ -z "$VER" ] && VER=$("$(dirname "$0")"/version) [ ! -z "$DIST" ] && VER=${VER}~${DIST} @@ -21,6 +21,6 @@ NOW=$(date -R) echo >"${CHANGELOG}" "tvheadend (${VER}) ${DIST}; urgency=low" echo >>"${CHANGELOG}" echo >>"${CHANGELOG}" " * The full changelog can be found at " -echo >>"${CHANGELOG}" " http://www.lonelycoder.com/tvheadend/download" +echo >>"${CHANGELOG}" " https://tvheadend.org/projects/tvheadend/wiki/Releases" echo >>"${CHANGELOG}" echo >>"${CHANGELOG}" " -- ${DEBFULLNAME} <${DEBEMAIL}> ${NOW}" From d4883d9d6b3eb8ed1b0e85aeb4d297028c91e830 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 18 Apr 2013 13:25:14 +0100 Subject: [PATCH 494/503] docs: some minor documentation updates --- docs/html/config_misc.html | 28 ++++++++++++++++++++++++++++ docs/html/dvrlog.html | 10 ++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/html/config_misc.html b/docs/html/config_misc.html index c5709839..da07d690 100644 --- a/docs/html/config_misc.html +++ b/docs/html/config_misc.html @@ -44,7 +44,35 @@
How frequently it will re-try fetching an image that has failed to be fetched. + +
Ignore invalid SSL certificates +
Ignore invalid/unverifiable (expired, self-certified, etc.) certificates + +

+ Time Update - TVH now has a built-in capability to update the systme time. + However you should bare in mind that DVB time is not highly accurate and is + prone to both jitter and variation between different transponders. +
+ Where possible its probably still better to use an internet based NTP source + to synchronise the system clock. +

+ +
+
Update time +
Enable system time updates, this will only work if the user + running TVH has rights to update the system clock (normally only root). + +
Enable NTP driver +
This will create an NTP driver (using shmem interface) that you can feed + into ntpd. This can be run without root priviledges, but generally the + performance is not that great. + +
Update tolerance (milliseconds) +
Only update the system clock (doesn't affect NTP driver) if the delta + between the system clock and DVB time is greater than this. This can help + stop horrible oscillations on the system clock. +
diff --git a/docs/html/dvrlog.html b/docs/html/dvrlog.html index 1b2de7c3..d29b53aa 100644 --- a/docs/html/dvrlog.html +++ b/docs/html/dvrlog.html @@ -1,6 +1,12 @@
-The DVR log displays a paged grid containing all scheduled, current -and completed recordings. The list is sorted based on start time. +The DVR log is split into a series of paged grids: + +
    +
  • Upcoming Recordings - stuff scheduled to be recorded in the future +
  • Finished Recordings - stuff that has succesfully finished recording +
  • Failed Recordings - stuff that failed to record +
+

Use the bottom toolbar (not displayed in this manual) to navigate between pages in the grid. From c83b0cba2f0c8092b6a96828aef327ec8bb4c3b4 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 18 Apr 2013 15:21:59 +0100 Subject: [PATCH 495/503] dvb: remove the create only tv/radio services, this was a bad idea! --- src/dvb/dvb_tables.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 6b03fb41..2cce820b 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -430,14 +430,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, } } - if (!(t = dvb_service_find3(NULL, tdmi, NULL, 0, 0, service_id, 0, 0))) { - if (!servicetype_is_tv(stype) && - !servicetype_is_radio(stype)) - continue; - - if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) - continue; - } + if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) + continue; if(t->s_servicetype != stype || t->s_scrambled != free_ca_mode) { From 341618477ec6dba15f8aaad11cff76a01e458d59 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 18 Apr 2013 15:29:34 +0100 Subject: [PATCH 496/503] Fix #1682 - support: version script outputs something even if .git is missing --- support/version | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/support/version b/support/version index f3b37916..ba9bfedb 100755 --- a/support/version +++ b/support/version @@ -15,8 +15,10 @@ if [ -d ".git" ]; then VER=$(cd "$(dirname "$0")/.."; git describe --match "v*" 2> /dev/null)-unknown fi VER=$(echo $VER | sed "s/^v//" | sed "s/-\([0-9]*\)-\(g[0-9a-f]*\)/.\1~\2/") -else +elif [ -f "$(dirname "$0")/../debian/changelog" ]; then VER=$(head -1 "$(dirname "$0")/../debian/changelog" | awk '{ print $2 }' | tr -d '()' | cut -d '-' -f 1) +else + VER="0.0.0~unknown" fi # Output From 143128c7088042fa13652268e020ee94514bc547 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 19 Apr 2013 12:46:17 +0100 Subject: [PATCH 497/503] Fix #1703 - tvhlog: took too many liberties with previous va_list fix --- src/tvhlog.c | 10 +++------- src/tvhlog.h | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tvhlog.c b/src/tvhlog.c index 13bd4486..f55288ec 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -114,7 +114,7 @@ next: /* Log */ void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args ) + const char *subsys, const char *fmt, va_list *args ) { struct timeval now; struct tm tm; @@ -167,7 +167,7 @@ void tvhlogv ( const char *file, int line, if (options & TVHLOG_OPT_FILELINE && severity >= LOG_DEBUG) l += snprintf(buf + l, sizeof(buf) - l, "(%s:%d) ", file, line); if (args) - l += vsnprintf(buf + l, sizeof(buf) - l, fmt, args); + l += vsnprintf(buf + l, sizeof(buf) - l, fmt, *args); else l += snprintf(buf + l, sizeof(buf) - l, "%s", fmt); @@ -227,11 +227,9 @@ void _tvhlog ( const char *file, int line, const char *subsys, const char *fmt, ... ) { va_list args; - //pthread_mutex_lock(&tvhlog_mutex); va_start(args, fmt); - tvhlogv(file, line, notify, severity, subsys, fmt, args); + tvhlogv(file, line, notify, severity, subsys, fmt, &args); va_end(args); - //pthread_mutex_unlock(&tvhlog_mutex); } /* @@ -247,7 +245,6 @@ _tvhlog_hexdump(const char *file, int line, int i, c; char str[1024]; - //pthread_mutex_lock(&tvhlog_mutex); while (len > 0) { c = 0; for (i = 0; i < HEXDUMP_WIDTH; i++) { @@ -271,6 +268,5 @@ _tvhlog_hexdump(const char *file, int line, len -= HEXDUMP_WIDTH; data += HEXDUMP_WIDTH; } - //pthread_mutex_unlock(&tvhlog_mutex); } diff --git a/src/tvhlog.h b/src/tvhlog.h index e23e5ee6..cc4b3011 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -37,7 +37,7 @@ void tvhlog_set_subsys ( const char *subsys ); void tvhlog_get_subsys ( char *subsys, size_t len ); void tvhlogv ( const char *file, int line, int notify, int severity, - const char *subsys, const char *fmt, va_list args ); + const char *subsys, const char *fmt, va_list *args ); void _tvhlog ( const char *file, int line, int notify, int severity, const char *subsys, const char *fmt, ... ) From a2ccbb2de6eb50f7506f6da018625778cc6b4f8f Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 22 Apr 2013 10:06:58 +0100 Subject: [PATCH 498/503] support: remove natty and add raring to the build list --- support/apt-update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/apt-update b/support/apt-update index 0d192779..32279462 100755 --- a/support/apt-update +++ b/support/apt-update @@ -18,7 +18,7 @@ DIR=$(cd $(dirname "$0"); pwd) # Configuration TVH_ROOT=$(cd "$(dirname "$0")"/..; pwd) -[ -z "$TVH_DIST" ] && TVH_DIST="wheezy lucid natty oneiric precise quantal" +[ -z "$TVH_DIST" ] && TVH_DIST="wheezy lucid oneiric precise quantal raring" [ -z "$TVH_ARCH" ] && TVH_ARCH="i386 amd64" # Options From 84c1c05d73232d4d0e34bbd587892bb019f0abb5 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 10 May 2013 15:10:54 +0100 Subject: [PATCH 499/503] dvb: fix mistake in NIT processing. This was stopping some DVB-C networks from being properly scanned. --- src/dvb/dvb_tables.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 2cce820b..037fbbbb 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -894,13 +894,13 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, tvhtrace("nit", "tableid 0x%02x", tableid); tvhlog_hexdump("nit", ptr, len); - /* Ignore other network */ - if(tableid != 0x40) return -1; - - /* Check NID */ - if(tdmi->tdmi_adapter->tda_nitoid && - tdmi->tdmi_adapter->tda_nitoid != network_id) - return -1; + /* Specific NID requested */ + if(tdmi->tdmi_adapter->tda_nitoid) { + if (tdmi->tdmi_adapter->tda_nitoid != network_id) + return -1; + } else if (tableid != 0x40) { + return -1; + } /* Ignore non-current */ if((ptr[2] & 1) == 0) From 60bdb16c9d3a84ac2eed29aef3ae38000447775c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rblom?= Date: Thu, 9 May 2013 21:51:27 +0200 Subject: [PATCH 500/503] Added initial support for transcoding. --- Makefile | 3 +- configure | 4 + src/htsp_server.c | 69 +- src/libav.c | 49 ++ src/libav.h | 2 +- src/main.c | 4 + src/plumbing/transcoding.c | 1336 ++++++++++++++++++++++++++++++++++++ src/plumbing/transcoding.h | 42 ++ src/psi.c | 12 +- src/tvheadend.h | 3 +- src/webui/webui.c | 59 ++ 11 files changed, 1578 insertions(+), 5 deletions(-) create mode 100644 src/plumbing/transcoding.c create mode 100644 src/plumbing/transcoding.h diff --git a/Makefile b/Makefile index 00ee4147..b3a6a7ae 100644 --- a/Makefile +++ b/Makefile @@ -189,7 +189,8 @@ SRCS-$(CONFIG_AVAHI) += src/avahi.c # libav SRCS-$(CONFIG_LIBAV) += src/libav.c \ - src/muxer/muxer_libav.c + src/muxer/muxer_libav.c \ + src/plumbing/transcoding.c \ # CWC SRCS-${CONFIG_CWC} += src/cwc.c \ diff --git a/configure b/configure index 198e4d1b..fcec9de4 100755 --- a/configure +++ b/configure @@ -137,6 +137,10 @@ if enabled_or_auto libav; then has_libav=false fi + if $has_libav && ! check_pkg libswscale ">=0.13.0"; then + has_libav=false + fi + if $has_libav; then enable libav elif enabled libav; then diff --git a/src/htsp_server.c b/src/htsp_server.c index f68d016d..2a670a69 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -46,7 +46,9 @@ #if ENABLE_TIMESHIFT #include "timeshift.h" #endif - +#if ENABLE_LIBAV +#include "plumbing/transcoding.h" +#endif #include #include "settings.h" #include @@ -177,6 +179,10 @@ typedef struct htsp_subscription { streaming_target_t *hs_tshift; #endif +#if ENABLE_LIBAV +streaming_target_t *hs_transcoder; +#endif + htsp_msg_q_t hs_q; time_t hs_last_report; /* Last queue status report sent */ @@ -282,13 +288,23 @@ htsp_subscription_destroy(htsp_connection_t *htsp, htsp_subscription_t *hs) { LIST_REMOVE(hs, hs_link); subscription_unsubscribe(hs->hs_s); + if(hs->hs_tsfix != NULL) tsfix_destroy(hs->hs_tsfix); + +#if ENABLE_LIBAV + if(hs->hs_transcoder != NULL) + transcoder_destroy(hs->hs_transcoder); +#endif + htsp_flush_queue(htsp, &hs->hs_q); + #if ENABLE_TIMESHIFT if(hs->hs_tshift) timeshift_destroy(hs->hs_tshift); #endif + + free(hs); } @@ -1328,6 +1344,32 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) normts = 1; } #endif + +#if ENABLE_LIBAV + if (transcoding_enabled) { + transcoder_props_t props; + + props.tp_vcodec = streaming_component_txt2type(htsmsg_get_str(in, "videoCodec")); + props.tp_acodec = streaming_component_txt2type(htsmsg_get_str(in, "audioCodec")); + props.tp_scodec = streaming_component_txt2type(htsmsg_get_str(in, "subtitleCodec")); + + props.tp_resolution = htsmsg_get_u32_or_default(in, "maxResolution", 0); + props.tp_channels = htsmsg_get_u32_or_default(in, "channels", 0); + props.tp_bandwidth = htsmsg_get_u32_or_default(in, "bandwidth", 0); + + if ((str = htsmsg_get_str(in, "language"))) + strncpy(props.tp_language, str, 3); + + if(props.tp_vcodec != SCT_UNKNOWN || + props.tp_acodec != SCT_UNKNOWN || + props.tp_scodec != SCT_UNKNOWN) { + st = hs->hs_transcoder = transcoder_create(st); + transcoder_set_properties(st, &props); + normts = 1; + } + } +#endif + if(normts) st = hs->hs_tsfix = tsfix_create(st); @@ -1640,6 +1682,28 @@ htsp_method_file_seek(htsp_connection_t *htsp, htsmsg_t *in) return rep; } + +#if ENABLE_LIBAV +/** + * + */ +static htsmsg_t * +htsp_method_getCodecs(htsp_connection_t *htsp, htsmsg_t *in) +{ + htsmsg_t *out, *l; + + l = htsmsg_create_list(); + transcoder_get_capabilities(l); + + out = htsmsg_create_map(); + + htsmsg_add_msg(out, "encoders", l); + + return out; +} +#endif + + /** * HTSP methods */ @@ -1669,6 +1733,9 @@ struct { { "subscriptionSkip", htsp_method_skip, ACCESS_STREAMING}, { "subscriptionSpeed", htsp_method_speed, ACCESS_STREAMING}, { "subscriptionLive", htsp_method_live, ACCESS_STREAMING}, +#if ENABLE_LIBAV + { "getCodecs", htsp_method_getCodecs, ACCESS_STREAMING}, +#endif { "fileOpen", htsp_method_file_open, ACCESS_RECORDER}, { "fileRead", htsp_method_file_read, ACCESS_RECORDER}, { "fileClose", htsp_method_file_close, ACCESS_RECORDER}, diff --git a/src/libav.c b/src/libav.c index e867eea4..7934c52e 100644 --- a/src/libav.c +++ b/src/libav.c @@ -91,6 +91,55 @@ streaming_component_type2codec_id(streaming_component_type_t type) return codec_id; } + +/** + * Translate a libavcodec id to a component type + */ +streaming_component_type_t +codec_id2streaming_component_type(enum CodecID id) +{ + streaming_component_type_t type = CODEC_ID_NONE; + + switch(id) { + case CODEC_ID_H264: + type = SCT_H264; + break; + case CODEC_ID_MPEG2VIDEO: + type = SCT_MPEG2VIDEO; + break; + case CODEC_ID_AC3: + type = SCT_AC3; + break; + case CODEC_ID_EAC3: + type = SCT_EAC3; + break; + case CODEC_ID_AAC: + type = SCT_AAC; + break; + case CODEC_ID_MP2: + type = SCT_MPEG2AUDIO; + break; + case CODEC_ID_DVB_SUBTITLE: + type = SCT_DVBSUB; + break; + case CODEC_ID_TEXT: + type = SCT_TEXTSUB; + break; + case CODEC_ID_DVB_TELETEXT: + type = SCT_TELETEXT; + break; + case CODEC_ID_NONE: + type = SCT_NONE; + break; + default: + type = SCT_UNKNOWN; + break; + } + + return type; +} + + /** * */ diff --git a/src/libav.h b/src/libav.h index c8a6ed7a..98856f90 100644 --- a/src/libav.h +++ b/src/libav.h @@ -24,7 +24,7 @@ #include "tvheadend.h" enum CodecID streaming_component_type2codec_id(streaming_component_type_t type); - +streaming_component_type_t codec_id2streaming_component_type(enum CodecID id); void libav_init(void); #endif diff --git a/src/main.c b/src/main.c index 8e2d7ec8..8a2323f8 100644 --- a/src/main.c +++ b/src/main.c @@ -64,6 +64,7 @@ #include "timeshift.h" #if ENABLE_LIBAV #include "libav.h" +#include "plumbing/transcoding.h" #endif /* Command line option struct */ @@ -126,6 +127,9 @@ const tvh_caps_t tvheadend_capabilities[] = { #if ENABLE_LINUXDVB { "linuxdvb", NULL }, #endif +#if ENABLE_LIBAV + { "transcoding", &transcoding_enabled }, +#endif #if ENABLE_IMAGECACHE { "imagecache", &imagecache_enabled }, #endif diff --git a/src/plumbing/transcoding.c b/src/plumbing/transcoding.c new file mode 100644 index 00000000..4cb48f67 --- /dev/null +++ b/src/plumbing/transcoding.c @@ -0,0 +1,1336 @@ +/** + * Transcoding + * Copyright (C) 2013 John Törnblom + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "streaming.h" +#include "service.h" +#include "packet.h" +#include "transcoding.h" +#include "libav.h" + +LIST_HEAD(transcoder_stream_list, transcoder_stream); + +typedef struct transcoder_stream { + int ts_index; + streaming_component_type_t ts_type; + streaming_target_t *ts_target; + LIST_ENTRY(transcoder_stream) ts_link; + + void (*ts_handle_pkt) (struct transcoder_stream *, th_pkt_t *); + void (*ts_destroy) (struct transcoder_stream *); +} transcoder_stream_t; + + +typedef struct audio_stream { + transcoder_stream_t; + + AVCodecContext *aud_ictx; + AVCodec *aud_icodec; + + AVCodecContext *aud_octx; + AVCodec *aud_ocodec; + + uint8_t *aud_dec_sample; + uint32_t aud_dec_size; + uint32_t aud_dec_offset; + + uint8_t *aud_enc_sample; + uint32_t aud_enc_size; + + uint64_t aud_dec_pts; + uint64_t aud_enc_pts; + + int8_t aud_channels; + int32_t aud_bitrate; +} audio_stream_t; + + +typedef struct video_stream { + transcoder_stream_t; + + AVCodecContext *vid_ictx; + AVCodec *vid_icodec; + + AVCodecContext *vid_octx; + AVCodec *vid_ocodec; + + AVFrame *vid_dec_frame; + struct SwsContext *vid_scaler; + AVFrame *vid_enc_frame; + + int16_t vid_width; + int16_t vid_height; +} video_stream_t; + + +typedef struct subtitle_stream { + transcoder_stream_t; + + AVCodecContext *sub_ictx; + AVCodec *sub_icodec; + + AVCodecContext *sub_octx; + AVCodec *sub_ocodec; +} subtitle_stream_t; + + + +typedef struct transcoder { + streaming_target_t t_input; // must be first + streaming_target_t *t_output; + + transcoder_props_t t_props; + struct transcoder_stream_list t_stream_list; +} transcoder_t; + + + +#define WORKING_ENCODER(x) (x == CODEC_ID_H264 || x == CODEC_ID_MPEG2VIDEO || \ + x == CODEC_ID_AAC || x == CODEC_ID_MP2) + + +uint32_t transcoding_enabled = 0; + +/** + * + */ +static AVCodec * +transcoder_get_decoder(streaming_component_type_t ty) +{ + enum CodecID codec_id; + AVCodec *codec; + + codec_id = streaming_component_type2codec_id(ty); + if (codec_id == CODEC_ID_NONE) { + tvhlog(LOG_ERR, "transcode", "Unsupported input codec %s", + streaming_component_type2txt(ty)); + return NULL; + } + + codec = avcodec_find_decoder(codec_id); + if (!codec) { + tvhlog(LOG_ERR, "transcode", "Unable to find %s decoder", + streaming_component_type2txt(ty)); + return NULL; + } + + tvhlog(LOG_DEBUG, "transcode", "Using decoder %s", codec->name); + + return codec; +} + + +/** + * + */ +static AVCodec * +transcoder_get_encoder(streaming_component_type_t ty) +{ + enum CodecID codec_id; + AVCodec *codec; + + codec_id = streaming_component_type2codec_id(ty); + if (codec_id == CODEC_ID_NONE) { + tvhlog(LOG_ERR, "transcode", "Unable to find %s codec", + streaming_component_type2txt(ty)); + return NULL; + } + + if (!WORKING_ENCODER(codec_id)) { + tvhlog(LOG_WARNING, "transcode", "Unsupported output codec %s", + streaming_component_type2txt(ty)); + return NULL; + } + + codec = avcodec_find_encoder(codec_id); + if (!codec) { + tvhlog(LOG_ERR, "transcode", "Unable to find %s encoder", + streaming_component_type2txt(ty)); + return NULL; + } + + tvhlog(LOG_DEBUG, "transcode", "Using encoder %s", codec->name); + + return codec; +} + + +/** + * + */ +static void +transcoder_stream_packet(transcoder_stream_t *ts, th_pkt_t *pkt) +{ + streaming_message_t *sm; + + sm = streaming_msg_create_pkt(pkt); + streaming_target_deliver2(ts->ts_target, sm); +} + + +/** + * + */ +static void +transcoder_stream_subtitle(transcoder_stream_t *ts, th_pkt_t *pkt) +{ + //streaming_message_t *sm; + AVCodec *icodec; + AVCodecContext *ictx; + AVPacket packet; + AVSubtitle sub; + int length, got_subtitle; + + subtitle_stream_t *ss = (subtitle_stream_t*)ts; + + ictx = ss->sub_ictx; + //octx = ss->sub_octx; + + icodec = ss->sub_icodec; + //ocodec = ss->sub_ocodec; + + if (ictx->codec_id == CODEC_ID_NONE) { + ictx->codec_id = icodec->id; + + if (avcodec_open(ictx, icodec) < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to open %s decoder", icodec->name); + ts->ts_index = 0; + goto cleanup; + } + } + + av_init_packet(&packet); + packet.data = pktbuf_ptr(pkt->pkt_payload); + packet.size = pktbuf_len(pkt->pkt_payload); + packet.pts = pkt->pkt_pts; + packet.dts = pkt->pkt_dts; + packet.duration = pkt->pkt_duration; + + length = avcodec_decode_subtitle2(ictx, &sub, &got_subtitle, &packet); + if (length <= 0) { + tvhlog(LOG_ERR, "transcode", "Unable to decode subtitle (%d)", length); + ts->ts_index = 0; + goto cleanup; + } + + if (!got_subtitle) + goto cleanup; + + //TODO: encoding + + cleanup: + av_free_packet(&packet); + avsubtitle_free(&sub); +} + + +/** + * + */ +static void +transcoder_stream_audio(transcoder_stream_t *ts, th_pkt_t *pkt) +{ + AVCodec *icodec, *ocodec; + AVCodecContext *ictx, *octx; + AVPacket packet; + int length, len, i; + uint32_t frame_bytes; + short *samples; + streaming_message_t *sm; + th_pkt_t *n; + audio_stream_t *as = (audio_stream_t*)ts; + + ictx = as->aud_ictx; + octx = as->aud_octx; + + icodec = as->aud_icodec; + ocodec = as->aud_ocodec; + + if (ictx->codec_id == CODEC_ID_NONE) { + ictx->codec_id = icodec->id; + + if (avcodec_open(ictx, icodec) < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to open %s decoder", icodec->name); + ts->ts_index = 0; + goto cleanup; + } + + as->aud_dec_pts = pkt->pkt_pts; + } + + if (pkt->pkt_pts > as->aud_dec_pts) { + tvhlog(LOG_WARNING, "transcode", "Detected framedrop in audio"); + as->aud_enc_pts += (pkt->pkt_pts - as->aud_dec_pts); + } + + pkt = pkt_merge_header(pkt); + + av_init_packet(&packet); + packet.data = pktbuf_ptr(pkt->pkt_payload); + packet.size = pktbuf_len(pkt->pkt_payload); + packet.pts = pkt->pkt_pts; + packet.dts = pkt->pkt_dts; + packet.duration = pkt->pkt_duration; + + if ((len = as->aud_dec_size - as->aud_dec_offset) <= 0) { + tvhlog(LOG_ERR, "transcode", "Decoder buffer overflow"); + ts->ts_index = 0; + goto cleanup; + } + + samples = (short*)(as->aud_dec_sample + as->aud_dec_offset); + if ((length = avcodec_decode_audio3(ictx, samples, &len, &packet)) <= 0) { + tvhlog(LOG_ERR, "transcode", "Unable to decode audio (%d)", length); + ts->ts_index = 0; + goto cleanup; + } + + as->aud_dec_pts += pkt->pkt_duration; + as->aud_dec_offset += len; + + + octx->sample_rate = ictx->sample_rate; + octx->sample_fmt = ictx->sample_fmt; + + octx->time_base.den = 90000; + octx->time_base.num = 1; + + octx->channels = as->aud_channels ? as->aud_channels : ictx->channels; + octx->bit_rate = as->aud_bitrate ? as->aud_bitrate : ictx->bit_rate; + + octx->channels = MIN(octx->channels, ictx->channels); + octx->bit_rate = MIN(octx->bit_rate, ictx->bit_rate); + + switch (octx->channels) { + case 1: + octx->channel_layout = AV_CH_LAYOUT_MONO; + break; + + case 2: + octx->channel_layout = AV_CH_LAYOUT_STEREO; + break; + + case 3: + octx->channel_layout = AV_CH_LAYOUT_SURROUND; + break; + + case 4: + octx->channel_layout = AV_CH_LAYOUT_QUAD; + break; + + case 5: + octx->channel_layout = AV_CH_LAYOUT_5POINT0; + break; + + case 6: + octx->channel_layout = AV_CH_LAYOUT_5POINT1; + break; + + case 7: + octx->channel_layout = AV_CH_LAYOUT_6POINT1; + break; + + case 8: + octx->channel_layout = AV_CH_LAYOUT_7POINT1; + break; + + default: + break; + } + + switch (ts->ts_type) { + case SCT_MPEG2AUDIO: + octx->channels = MIN(octx->channels, 2); + if (octx->channels == 1) + octx->channel_layout = AV_CH_LAYOUT_MONO; + else + octx->channel_layout = AV_CH_LAYOUT_STEREO; + + break; + + case SCT_AAC: + octx->global_quality = 4*FF_QP2LAMBDA; + octx->flags |= CODEC_FLAG_QSCALE; + break; + + default: + break; + } + + if (octx->codec_id == CODEC_ID_NONE) { + octx->codec_id = ocodec->id; + + if (avcodec_open(octx, ocodec) < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to open %s encoder", ocodec->name); + ts->ts_index = 0; + goto cleanup; + } + } + + frame_bytes = av_get_bytes_per_sample(octx->sample_fmt) * + octx->frame_size * + octx->channels; + + len = as->aud_dec_offset; + + for (i = 0; i <= (len - frame_bytes); i += frame_bytes) { + length = avcodec_encode_audio(octx, + as->aud_enc_sample, + as->aud_enc_size, + (short *)(as->aud_dec_sample + i)); + if (length < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to encode audio (%d)", length); + ts->ts_index = 0; + goto cleanup; + + } else if (length) { + n = pkt_alloc(as->aud_enc_sample, length, as->aud_enc_pts, as->aud_enc_pts); + n->pkt_componentindex = ts->ts_index; + n->pkt_frametype = pkt->pkt_frametype; + n->pkt_channels = octx->channels; + n->pkt_sri = pkt->pkt_sri; + + if (octx->coded_frame && octx->coded_frame->pts != AV_NOPTS_VALUE) + n->pkt_duration = octx->coded_frame->pts - as->aud_enc_pts; + else + n->pkt_duration = frame_bytes*90000 / (2 * octx->channels * octx->sample_rate); + + as->aud_enc_pts += n->pkt_duration; + + if (octx->extradata_size) + n->pkt_header = pktbuf_alloc(octx->extradata, octx->extradata_size); + + sm = streaming_msg_create_pkt(n); + streaming_target_deliver2(ts->ts_target, sm); + pkt_ref_dec(n); + } + + as->aud_dec_offset -= frame_bytes; + } + + if (as->aud_dec_offset) + memmove(as->aud_dec_sample, as->aud_dec_sample + len - as->aud_dec_offset, + as->aud_dec_offset); + + cleanup: + av_free_packet(&packet); +} + + +/** + * + */ +static void +transcoder_stream_video(transcoder_stream_t *ts, th_pkt_t *pkt) +{ + AVCodec *icodec, *ocodec; + AVCodecContext *ictx, *octx; + AVDictionary *opts; + AVPacket packet; + AVPicture deint_pic; + uint8_t *buf, *out, *deint; + int length, len, got_picture; + streaming_message_t *sm; + th_pkt_t *n; + video_stream_t *vs = (video_stream_t*)ts; + + ictx = vs->vid_ictx; + octx = vs->vid_octx; + + icodec = vs->vid_icodec; + ocodec = vs->vid_ocodec; + + buf = out = deint = NULL; + opts = NULL; + + if (ictx->codec_id == CODEC_ID_NONE) { + ictx->codec_id = icodec->id; + + if (avcodec_open(ictx, icodec) < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to open %s decoder", icodec->name); + ts->ts_index = 0; + goto cleanup; + } + } + + pkt = pkt_merge_header(pkt); + + av_init_packet(&packet); + packet.data = pktbuf_ptr(pkt->pkt_payload); + packet.size = pktbuf_len(pkt->pkt_payload); + packet.pts = pkt->pkt_pts; + packet.dts = pkt->pkt_dts; + packet.duration = pkt->pkt_duration; + + vs->vid_enc_frame->pts = packet.pts; + vs->vid_enc_frame->pkt_dts = packet.dts; + vs->vid_enc_frame->pkt_pts = packet.pts; + + vs->vid_dec_frame->pts = packet.pts; + vs->vid_dec_frame->pkt_dts = packet.dts; + vs->vid_dec_frame->pkt_pts = packet.pts; + + ictx->reordered_opaque = packet.pts; + + length = avcodec_decode_video2(ictx, vs->vid_dec_frame, &got_picture, &packet); + if (length <= 0) { + tvhlog(LOG_ERR, "transcode", "Unable to decode video (%d)", length); + ts->ts_index = 0; + goto cleanup; + } + + if (!got_picture) + goto cleanup; + + octx->sample_aspect_ratio.num = ictx->sample_aspect_ratio.num; + octx->sample_aspect_ratio.den = ictx->sample_aspect_ratio.den; + + vs->vid_enc_frame->sample_aspect_ratio.num = vs->vid_dec_frame->sample_aspect_ratio.num; + vs->vid_enc_frame->sample_aspect_ratio.den = vs->vid_dec_frame->sample_aspect_ratio.den; + + if(octx->codec_id == CODEC_ID_NONE) { + // Common settings + octx->width = vs->vid_width ? vs->vid_width : ictx->width; + octx->height = vs->vid_height ? vs->vid_height : ictx->height; + octx->gop_size = 25; + octx->time_base.den = 25; + octx->time_base.num = 1; + octx->has_b_frames = ictx->has_b_frames; + + switch (ts->ts_type) { + case SCT_MPEG2VIDEO: + octx->codec_id = CODEC_ID_MPEG2VIDEO; + octx->pix_fmt = PIX_FMT_YUV420P; + octx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + octx->qmin = 1; + octx->qmax = FF_LAMBDA_MAX; + + octx->bit_rate = 2 * octx->width * octx->height; + octx->rc_max_rate = 4 * octx->bit_rate; + octx->rc_buffer_size = 2 * octx->rc_max_rate; + break; + + case SCT_H264: + octx->codec_id = CODEC_ID_H264; + octx->pix_fmt = PIX_FMT_YUV420P; + octx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + // Qscale difference between I-frames and P-frames. + // Note: -i_qfactor is handled a little differently than --ipratio. + // Recommended: -i_qfactor 0.71 + octx->i_quant_factor = 0.71; + + // QP curve compression: 0.0 => CBR, 1.0 => CQP. + // Recommended default: -qcomp 0.60 + octx->qcompress = 0.6; + + // Minimum quantizer. Doesn't need to be changed. + // Recommended default: -qmin 10 + octx->qmin = 10; + + // Maximum quantizer. Doesn't need to be changed. + // Recommended default: -qmax 51 + octx->qmax = 30; + + av_dict_set(&opts, "preset", "medium", 0); + av_dict_set(&opts, "profile", "baseline", 0); + + octx->bit_rate = 2 * octx->width * octx->height; + octx->rc_buffer_size = 8 * 1024 * 224; + octx->rc_max_rate = 2 * octx->rc_buffer_size; + break; + + default: + break; + } + + octx->codec_id = ocodec->id; + + if (avcodec_open2(octx, ocodec, &opts) < 0) { + tvhlog(LOG_ERR, "transcode", "Unable to open %s encoder", ocodec->name); + ts->ts_index = 0; + goto cleanup; + } + } + + len = avpicture_get_size(ictx->pix_fmt, ictx->width, ictx->height); + deint = av_malloc(len); + + avpicture_fill(&deint_pic, + deint, + ictx->pix_fmt, + ictx->width, + ictx->height); + + if (avpicture_deinterlace(&deint_pic, + (AVPicture *)vs->vid_dec_frame, + ictx->pix_fmt, + ictx->width, + ictx->height) < 0) { + tvhlog(LOG_ERR, "transcode", "Cannot deinterlace frame"); + ts->ts_index = 0; + goto cleanup; + } + + len = avpicture_get_size(octx->pix_fmt, octx->width, octx->height); + buf = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE); + memset(buf, 0, len); + + avpicture_fill((AVPicture *)vs->vid_enc_frame, + buf, + octx->pix_fmt, + octx->width, + octx->height); + + vs->vid_scaler = sws_getCachedContext(vs->vid_scaler, + ictx->width, + ictx->height, + ictx->pix_fmt, + octx->width, + octx->height, + octx->pix_fmt, + 1, + NULL, + NULL, + NULL); + + if (sws_scale(vs->vid_scaler, + (const uint8_t * const*)deint_pic.data, + deint_pic.linesize, + 0, + ictx->height, + vs->vid_enc_frame->data, + vs->vid_enc_frame->linesize) < 0) { + tvhlog(LOG_ERR, "transcode", "Cannot scale frame"); + ts->ts_index = 0; + goto cleanup; + } + + + len = avpicture_get_size(octx->pix_fmt, ictx->width, ictx->height); + out = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE); + memset(out, 0, len); + + vs->vid_enc_frame->pkt_pts = vs->vid_dec_frame->pkt_pts; + vs->vid_enc_frame->pkt_dts = vs->vid_dec_frame->pkt_dts; + + if (vs->vid_dec_frame->reordered_opaque != AV_NOPTS_VALUE) + vs->vid_enc_frame->pts = vs->vid_dec_frame->reordered_opaque; + + else if (ictx->coded_frame && ictx->coded_frame->pts != AV_NOPTS_VALUE) + vs->vid_enc_frame->pts = vs->vid_dec_frame->pts; + + length = avcodec_encode_video(octx, out, len, vs->vid_enc_frame); + if (length <= 0) { + if (length) { + tvhlog(LOG_ERR, "transcode", "Unable to encode video (%d)", length); + ts->ts_index = 0; + } + + goto cleanup; + } + + if (!octx->coded_frame) + goto cleanup; + + n = pkt_alloc(out, length, octx->coded_frame->pkt_pts, octx->coded_frame->pkt_dts); + + switch (octx->coded_frame->pict_type) { + case AV_PICTURE_TYPE_I: + n->pkt_frametype = PKT_I_FRAME; + break; + + case AV_PICTURE_TYPE_P: + n->pkt_frametype = PKT_P_FRAME; + break; + + case AV_PICTURE_TYPE_B: + n->pkt_frametype = PKT_B_FRAME; + break; + + default: + break; + } + + n->pkt_duration = pkt->pkt_duration; + n->pkt_commercial = pkt->pkt_commercial; + n->pkt_componentindex = pkt->pkt_componentindex; + n->pkt_field = pkt->pkt_field; + n->pkt_aspect_num = pkt->pkt_aspect_num; + n->pkt_aspect_den = pkt->pkt_aspect_den; + + if(octx->coded_frame && octx->coded_frame->pts != AV_NOPTS_VALUE) { + if(n->pkt_dts != PTS_UNSET) + n->pkt_dts -= n->pkt_pts; + + n->pkt_pts = octx->coded_frame->pts; + + if(n->pkt_dts != PTS_UNSET) + n->pkt_dts += n->pkt_pts; + } + + if (octx->extradata_size) + n->pkt_header = pktbuf_alloc(octx->extradata, octx->extradata_size); + + sm = streaming_msg_create_pkt(n); + streaming_target_deliver2(ts->ts_target, sm); + pkt_ref_dec(n); + + cleanup: + av_free_packet(&packet); + + if(buf) + av_free(buf); + + if(out) + av_free(out); + + if(deint) + av_free(deint); + + if(opts) + av_dict_free(&opts); +} + + +/** + * + */ +static void +transcoder_packet(transcoder_t *t, th_pkt_t *pkt) +{ + transcoder_stream_t *ts; + + LIST_FOREACH(ts, &t->t_stream_list, ts_link) { + if (pkt->pkt_componentindex != ts->ts_index) + continue; + + ts->ts_handle_pkt(ts, pkt); + break; + } +} + + +/** + * + */ +static void +transcoder_destroy_stream(transcoder_stream_t *ts) +{ + free(ts); +} + + +/** + * + */ +static int +transcoder_init_stream(transcoder_t *t, streaming_start_component_t *ssc) +{ + transcoder_stream_t *ts = calloc(1, sizeof(transcoder_stream_t)); + + ts->ts_index = ssc->ssc_index; + ts->ts_type = ssc->ssc_type; + ts->ts_target = t->t_output; + ts->ts_handle_pkt = transcoder_stream_packet; + ts->ts_destroy = transcoder_destroy_stream; + + LIST_INSERT_HEAD(&t->t_stream_list, ts, ts_link); + + if(ssc->ssc_gh) + pktbuf_ref_inc(ssc->ssc_gh); + + tvhlog(LOG_INFO, "transcode", "%d:%s ==> Passthrough", + ssc->ssc_index, + streaming_component_type2txt(ssc->ssc_type)); + + return 1; +} + + +/** + * + */ +static void +transcoder_destroy_subtitle(transcoder_stream_t *ts) +{ + subtitle_stream_t *ss = (subtitle_stream_t*)ts; + + if(ss->sub_ictx) { + avcodec_close(ss->sub_ictx); + av_free(ss->sub_ictx); + } + + if(ss->sub_octx) { + avcodec_close(ss->sub_octx); + av_free(ss->sub_octx); + } + + free(ts); +} + + +/** + * + */ +static int +transcoder_init_subtitle(transcoder_t *t, streaming_start_component_t *ssc) +{ + subtitle_stream_t *ss; + AVCodec *icodec, *ocodec; + transcoder_props_t *tp = &t->t_props; + + if (tp->tp_scodec == SCT_NONE) + return 0; + + else if (tp->tp_scodec == SCT_UNKNOWN) + return transcoder_init_stream(t, ssc); + + else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) + return transcoder_init_stream(t, ssc); + + else if (!(ocodec = transcoder_get_encoder(tp->tp_scodec))) + return transcoder_init_stream(t, ssc); + + if (tp->tp_scodec == ssc->ssc_type) + return transcoder_init_stream(t, ssc); + + ss = calloc(1, sizeof(subtitle_stream_t)); + + ss->ts_index = ssc->ssc_index; + ss->ts_type = tp->tp_scodec; + ss->ts_target = t->t_output; + ss->ts_handle_pkt = transcoder_stream_subtitle; + ss->ts_destroy = transcoder_destroy_subtitle; + + ss->sub_icodec = icodec; + ss->sub_ocodec = ocodec; + + ss->sub_ictx = avcodec_alloc_context(); + ss->sub_octx = avcodec_alloc_context(); + + ss->sub_ictx->codec_type = AVMEDIA_TYPE_SUBTITLE; + ss->sub_octx->codec_type = AVMEDIA_TYPE_SUBTITLE; + + avcodec_get_context_defaults3(ss->sub_ictx, icodec); + avcodec_get_context_defaults3(ss->sub_octx, ocodec); + + LIST_INSERT_HEAD(&t->t_stream_list, (transcoder_stream_t*)ss, ts_link); + + tvhlog(LOG_INFO, "transcode", "%d:%s ==> %s", + ssc->ssc_index, + streaming_component_type2txt(ssc->ssc_type), + streaming_component_type2txt(ss->ts_type)); + + ssc->ssc_type = tp->tp_scodec; + ssc->ssc_gh = NULL; + + return 1; +} + + +/** + * + */ +static void +transcoder_destroy_audio(transcoder_stream_t *ts) +{ + audio_stream_t *as = (audio_stream_t*)ts; + + if(as->aud_ictx) { + avcodec_close(as->aud_ictx); + av_free(as->aud_ictx); + } + + if(as->aud_octx) { + avcodec_close(as->aud_octx); + av_free(as->aud_octx); + } + + if(as->aud_dec_sample) + av_free(as->aud_dec_sample); + + if(as->aud_enc_sample) + av_free(as->aud_enc_sample); + + free(ts); +} + + +/** + * + */ +static int +transcoder_init_audio(transcoder_t *t, streaming_start_component_t *ssc) +{ + audio_stream_t *as; + transcoder_stream_t *ts; + AVCodec *icodec, *ocodec; + transcoder_props_t *tp = &t->t_props; + + if (tp->tp_acodec == SCT_NONE) + return 0; + + else if (tp->tp_acodec == SCT_UNKNOWN) + return transcoder_init_stream(t, ssc); + + else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) + return transcoder_init_stream(t, ssc); + + else if (!(ocodec = transcoder_get_encoder(tp->tp_acodec))) + return transcoder_init_stream(t, ssc); + + LIST_FOREACH(ts, &t->t_stream_list, ts_link) + if (SCT_ISAUDIO(ts->ts_type)) + return 0; + + if (tp->tp_acodec == ssc->ssc_type) + return transcoder_init_stream(t, ssc); + + as = calloc(1, sizeof(audio_stream_t)); + + as->ts_index = ssc->ssc_index; + as->ts_type = tp->tp_acodec; + as->ts_target = t->t_output; + as->ts_handle_pkt = transcoder_stream_audio; + as->ts_destroy = transcoder_destroy_audio; + + as->aud_icodec = icodec; + as->aud_ocodec = ocodec; + + as->aud_ictx = avcodec_alloc_context(); + as->aud_octx = avcodec_alloc_context(); + + as->aud_ictx->codec_type = AVMEDIA_TYPE_AUDIO; + as->aud_octx->codec_type = AVMEDIA_TYPE_AUDIO; + + as->aud_ictx->thread_count = sysconf(_SC_NPROCESSORS_ONLN); + as->aud_octx->thread_count = sysconf(_SC_NPROCESSORS_ONLN); + + avcodec_get_context_defaults3(as->aud_ictx, icodec); + avcodec_get_context_defaults3(as->aud_octx, ocodec); + + as->aud_ictx->codec_type = AVMEDIA_TYPE_AUDIO; + as->aud_octx->codec_type = AVMEDIA_TYPE_AUDIO; + + as->aud_dec_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*2; + as->aud_enc_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*2; + + as->aud_dec_sample = av_malloc(as->aud_dec_size + FF_INPUT_BUFFER_PADDING_SIZE); + as->aud_enc_sample = av_malloc(as->aud_enc_size + FF_INPUT_BUFFER_PADDING_SIZE); + + memset(as->aud_dec_sample, 0, as->aud_dec_size + FF_INPUT_BUFFER_PADDING_SIZE); + memset(as->aud_enc_sample, 0, as->aud_enc_size + FF_INPUT_BUFFER_PADDING_SIZE); + + LIST_INSERT_HEAD(&t->t_stream_list, (transcoder_stream_t*)as, ts_link); + + tvhlog(LOG_INFO, "transcode", "%d:%s ==> %s", + ssc->ssc_index, + streaming_component_type2txt(ssc->ssc_type), + streaming_component_type2txt(as->ts_type)); + + ssc->ssc_type = tp->tp_acodec; + ssc->ssc_gh = NULL; + + // resampling not implemented yet + if(tp->tp_channels > 0) + as->aud_channels = 0; //tp->tp_channels; + else + as->aud_channels = 0; + + as->aud_bitrate = as->aud_channels * 64000; + return 1; +} + + +/** + * + */ +static void +transcoder_destroy_video(transcoder_stream_t *ts) +{ + video_stream_t *vs = (video_stream_t*)ts; + + if(vs->vid_ictx) { + avcodec_close(vs->vid_ictx); + av_free(vs->vid_ictx); + } + + if(vs->vid_octx) { + avcodec_close(vs->vid_octx); + av_free(vs->vid_octx); + } + + if(vs->vid_dec_frame) + av_free(vs->vid_dec_frame); + + if(vs->vid_scaler) + sws_freeContext(vs->vid_scaler); + + if(vs->vid_enc_frame) + av_free(vs->vid_enc_frame); + + free(ts); +} + + +/** + * + */ +static int +transcoder_init_video(transcoder_t *t, streaming_start_component_t *ssc) +{ + video_stream_t *vs; + AVCodec *icodec, *ocodec; + double aspect; + transcoder_props_t *tp = &t->t_props; + + if (tp->tp_vcodec == SCT_NONE) + return 0; + + else if (tp->tp_vcodec == SCT_UNKNOWN) + return transcoder_init_stream(t, ssc); + + else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) + return transcoder_init_stream(t, ssc); + + else if (!(ocodec = transcoder_get_encoder(tp->tp_vcodec))) + return transcoder_init_stream(t, ssc); + + vs = calloc(1, sizeof(video_stream_t)); + + vs->ts_index = ssc->ssc_index; + vs->ts_type = tp->tp_vcodec; + vs->ts_target = t->t_output; + vs->ts_handle_pkt = transcoder_stream_video; + vs->ts_destroy = transcoder_destroy_video; + + vs->vid_icodec = icodec; + vs->vid_ocodec = ocodec; + + vs->vid_ictx = avcodec_alloc_context(); + vs->vid_octx = avcodec_alloc_context(); + + vs->vid_ictx->thread_count = sysconf(_SC_NPROCESSORS_ONLN); + vs->vid_octx->thread_count = sysconf(_SC_NPROCESSORS_ONLN); + + avcodec_get_context_defaults3(vs->vid_ictx, icodec); + avcodec_get_context_defaults3(vs->vid_octx, ocodec); + + vs->vid_dec_frame = avcodec_alloc_frame(); + vs->vid_enc_frame = avcodec_alloc_frame(); + + avcodec_get_frame_defaults(vs->vid_dec_frame); + avcodec_get_frame_defaults(vs->vid_enc_frame); + + vs->vid_ictx->codec_type = AVMEDIA_TYPE_VIDEO; + vs->vid_octx->codec_type = AVMEDIA_TYPE_VIDEO; + + LIST_INSERT_HEAD(&t->t_stream_list, (transcoder_stream_t*)vs, ts_link); + + aspect = (double)ssc->ssc_width / ssc->ssc_height; + + vs->vid_height = MIN(tp->tp_resolution, ssc->ssc_height); + if (vs->vid_height&1) // Must be even + vs->vid_height++; + + vs->vid_width = vs->vid_height * aspect; + if (vs->vid_width&1) // Must be even + vs->vid_width++; + + tvhlog(LOG_INFO, "transcode", "%d:%s %dx%d ==> %s %dx%d", + ssc->ssc_index, + streaming_component_type2txt(ssc->ssc_type), + ssc->ssc_width, + ssc->ssc_height, + streaming_component_type2txt(vs->ts_type), + vs->vid_width, + vs->vid_height); + + ssc->ssc_type = tp->tp_vcodec; + ssc->ssc_width = vs->vid_width; + ssc->ssc_height = vs->vid_height; + ssc->ssc_gh = NULL; + + return 1; +} + + +/** + * Figure out how many streams we will use. + */ +static int +transcoder_calc_stream_count(transcoder_t *t, streaming_start_t *ss) { + int i = 0; + int video = 0; + int audio = 0; + int subtitle = 0; + streaming_start_component_t *ssc = NULL; + + for (i = 0; i < ss->ss_num_components; i++) { + ssc = &ss->ss_components[i]; + + if (ssc->ssc_disabled) + continue; + + if (SCT_ISVIDEO(ssc->ssc_type)) { + if (t->t_props.tp_vcodec == SCT_NONE) + video = 0; + else if (t->t_props.tp_vcodec == SCT_UNKNOWN) + video++; + else + video = 1; + + } else if (SCT_ISAUDIO(ssc->ssc_type)) { + if (t->t_props.tp_acodec == SCT_NONE) + audio = 0; + else if (t->t_props.tp_acodec == SCT_UNKNOWN) + audio++; + else + audio = 1; + + } else if (SCT_ISSUBTITLE(ssc->ssc_type)) { + if (t->t_props.tp_scodec == SCT_NONE) + subtitle = 0; + else if (t->t_props.tp_scodec == SCT_UNKNOWN) + subtitle++; + else + subtitle = 1; + } + } + + return (video + audio + subtitle); +} + + +/** + * + */ +static streaming_start_t * +transcoder_start(transcoder_t *t, streaming_start_t *src) +{ + int i, j, n, rc; + streaming_start_t *ss; + + + n = transcoder_calc_stream_count(t, src); + ss = calloc(1, (sizeof(streaming_start_t) + + sizeof(streaming_start_component_t) * n)); + + ss->ss_refcount = 1; + ss->ss_num_components = n; + ss->ss_pcr_pid = src->ss_pcr_pid; + ss->ss_pmt_pid = src->ss_pmt_pid; + service_source_info_copy(&ss->ss_si, &src->ss_si); + + + for (i = j = 0; i < src->ss_num_components && j < n; i++) { + streaming_start_component_t *ssc_src = &src->ss_components[i]; + streaming_start_component_t *ssc = &ss->ss_components[j]; + + if (ssc_src->ssc_disabled) + continue; + + ssc->ssc_index = ssc_src->ssc_index; + ssc->ssc_type = ssc_src->ssc_type; + ssc->ssc_composition_id = ssc_src->ssc_composition_id; + ssc->ssc_ancillary_id = ssc_src->ssc_ancillary_id; + ssc->ssc_pid = ssc_src->ssc_pid; + ssc->ssc_width = ssc_src->ssc_width; + ssc->ssc_height = ssc_src->ssc_height; + ssc->ssc_aspect_num = ssc_src->ssc_aspect_num; + ssc->ssc_aspect_den = ssc_src->ssc_aspect_den; + ssc->ssc_sri = ssc_src->ssc_sri; + ssc->ssc_channels = ssc_src->ssc_channels; + ssc->ssc_disabled = ssc_src->ssc_disabled; + ssc->ssc_frameduration = ssc_src->ssc_frameduration; + ssc->ssc_gh = ssc_src->ssc_gh; + + memcpy(ssc->ssc_lang, ssc_src->ssc_lang, 4); + + if (SCT_ISVIDEO(ssc->ssc_type)) + rc = transcoder_init_video(t, ssc); + + else if (SCT_ISAUDIO(ssc->ssc_type)) + rc = transcoder_init_audio(t, ssc); + + else if (SCT_ISSUBTITLE(ssc->ssc_type)) + rc = transcoder_init_subtitle(t, ssc); + else + rc = 0; + + if(!rc) + tvhlog(LOG_INFO, "transcode", "%d:%s ==> Filtered", + ssc->ssc_index, + streaming_component_type2txt(ssc->ssc_type)); + else + j++; + } + + return ss; +} + + +/** + * + */ +static void +transcoder_stop(transcoder_t *t) +{ + transcoder_stream_t *ts; + + while ((ts = LIST_FIRST(&t->t_stream_list))) { + LIST_REMOVE(ts, ts_link); + + if (ts->ts_destroy) + ts->ts_destroy(ts); + } +} + + +/** + * + */ +static void +transcoder_input(void *opaque, streaming_message_t *sm) +{ + transcoder_t *t; + streaming_start_t *ss; + th_pkt_t *pkt; + + t = opaque; + + switch (sm->sm_type) { + case SMT_PACKET: + pkt = sm->sm_data; + transcoder_packet(t, pkt); + pkt_ref_dec(pkt); + break; + + case SMT_START: + ss = transcoder_start(t, sm->sm_data); + streaming_start_unref(sm->sm_data); + sm->sm_data = ss; + + streaming_target_deliver2(t->t_output, sm); + break; + + case SMT_STOP: + transcoder_stop(t); + // Fallthrough + + case SMT_SPEED: + case SMT_SKIP: + case SMT_TIMESHIFT_STATUS: + case SMT_EXIT: + case SMT_SERVICE_STATUS: + case SMT_SIGNAL_STATUS: + case SMT_NOSTART: + case SMT_MPEGTS: + streaming_target_deliver2(t->t_output, sm); + break; + } +} + + +/** + * + */ +streaming_target_t * +transcoder_create(streaming_target_t *output) +{ + transcoder_t *t = calloc(1, sizeof(transcoder_t)); + + t->t_output = output; + + streaming_target_init(&t->t_input, transcoder_input, t, 0); + + return &t->t_input; +} + + +/** + * + */ +void +transcoder_set_properties(streaming_target_t *st, + transcoder_props_t *props) +{ + transcoder_t *t = (transcoder_t *)st; + transcoder_props_t *tp = &t->t_props; + + tp->tp_vcodec = props->tp_vcodec; + tp->tp_acodec = props->tp_acodec; + tp->tp_scodec = props->tp_scodec; + tp->tp_channels = props->tp_channels; + tp->tp_bandwidth = props->tp_bandwidth; + tp->tp_resolution = props->tp_resolution; + + memcpy(tp->tp_language, props->tp_language, 4); +} + + +/** + * + */ +void +transcoder_destroy(streaming_target_t *st) +{ + transcoder_t *t = (transcoder_t *)st; + + transcoder_stop(t); + free(t); +} + + +/** + * + */ +void +transcoder_get_capabilities(htsmsg_t *array) +{ + AVCodec *p = NULL; + const char *name; + streaming_component_type_t sct; + + while ((p = av_codec_next(p))) { + + if (!p->encode && !p->encode2) + continue; + + if (!WORKING_ENCODER(p->id)) + continue; + + sct = codec_id2streaming_component_type(p->id); + if (sct == SCT_NONE) + continue; + + name = streaming_component_type2txt(sct); + htsmsg_add_str(array, NULL, name); + } +} + + + diff --git a/src/plumbing/transcoding.h b/src/plumbing/transcoding.h new file mode 100644 index 00000000..a08bea01 --- /dev/null +++ b/src/plumbing/transcoding.h @@ -0,0 +1,42 @@ +/** + * Transcoding + * Copyright (C) 2013 John Törnblom + * + * 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 . + */ +#pragma once + +#include "tvheadend.h" +#include "htsmsg.h" + +typedef struct transcoder_prop { + streaming_component_type_t tp_vcodec; + streaming_component_type_t tp_acodec; + streaming_component_type_t tp_scodec; + + int8_t tp_channels; + int32_t tp_bandwidth; + char tp_language[4]; + int32_t tp_resolution; +} transcoder_props_t; + +extern uint32_t transcoding_enabled; + +streaming_target_t *transcoder_create (streaming_target_t *output); +void transcoder_destroy(streaming_target_t *tr); + +void transcoder_get_capabilities(htsmsg_t *array); +void transcoder_set_properties (streaming_target_t *tr, + transcoder_props_t *prop); + diff --git a/src/psi.c b/src/psi.c index b191037b..d86519c8 100644 --- a/src/psi.c +++ b/src/psi.c @@ -908,6 +908,8 @@ psi_caid2name(uint16_t caid) * */ static struct strtab streamtypetab[] = { + { "NONE", SCT_NONE }, + { "UNKNOWN", SCT_UNKNOWN }, { "MPEG2VIDEO", SCT_MPEG2VIDEO }, { "MPEG2AUDIO", SCT_MPEG2AUDIO }, { "H264", SCT_H264 }, @@ -920,7 +922,7 @@ static struct strtab streamtypetab[] = { { "MPEGTS", SCT_MPEGTS }, { "TEXTSUB", SCT_TEXTSUB }, { "EAC3", SCT_EAC3 }, - { "AAC", SCT_MP4A }, + { "AAC", SCT_MP4A }, }; @@ -933,6 +935,14 @@ streaming_component_type2txt(streaming_component_type_t s) return val2str(s, streamtypetab) ?: "INVALID"; } +/** + * + */ +streaming_component_type_t +streaming_component_txt2type(const char *str) +{ + return str ? str2val(str, streamtypetab) : SCT_UNKNOWN; +} /** * Store service settings into message diff --git a/src/tvheadend.h b/src/tvheadend.h index 55cdd517..4bc951dc 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -184,6 +184,7 @@ int get_device_connection(const char *dev); * Stream component types */ typedef enum { + SCT_NONE = -1, SCT_UNKNOWN = 0, SCT_MPEG2VIDEO = 1, SCT_MPEG2AUDIO, @@ -423,7 +424,7 @@ typedef struct sbuf { } sbuf_t; - +streaming_component_type_t streaming_component_txt2type(const char *str); const char *streaming_component_type2txt(streaming_component_type_t s); static inline unsigned int tvh_strhash(const char *s, unsigned int mod) diff --git a/src/webui/webui.c b/src/webui/webui.c index bc7d5989..997b8829 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -40,6 +40,7 @@ #include "psi.h" #include "plumbing/tsfix.h" #include "plumbing/globalheaders.h" +#include "plumbing/transcoding.h" #include "epg.h" #include "muxer.h" #include "dvb/dvb.h" @@ -548,6 +549,47 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque) } +#if ENABLE_LIBAV +static int +http_get_transcoder_properties(struct http_arg_list *args, + transcoder_props_t *props) +{ + int transcode; + const char *s; + + memset(props, 0, sizeof(transcoder_props_t)); + + if ((s = http_arg_get(args, "transcode"))) + transcode = atoi(s); + else + transcode = 0; + + if ((s = http_arg_get(args, "resolution"))) + props->tp_resolution = atoi(s); + + if ((s = http_arg_get(args, "channels"))) + props->tp_channels = atoi(s); + + if ((s = http_arg_get(args, "bandwidth"))) + props->tp_bandwidth = atoi(s); + + if ((s = http_arg_get(args, "language"))) + strncpy(props->tp_language, s, 3); + + if ((s = http_arg_get(args, "vcodec"))) + props->tp_vcodec = streaming_component_txt2type(s); + + if ((s = http_arg_get(args, "acodec"))) + props->tp_acodec = streaming_component_txt2type(s); + + if ((s = http_arg_get(args, "scodec"))) + props->tp_scodec = streaming_component_txt2type(s); + + return transcode && transcoding_enabled; +} +#endif + + /** * Subscribes to a service and starts the streaming loop */ @@ -660,6 +702,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) streaming_target_t *gh; streaming_target_t *tsfix; streaming_target_t *st; +#if ENABLE_LIBAV + streaming_target_t *tr = NULL; +#endif dvr_config_t *cfg; int priority = 100; int flags; @@ -689,6 +734,14 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) } else { streaming_queue_init2(&sq, 0, qsize); gh = globalheaders_create(&sq.sq_st); +#if ENABLE_LIBAV + transcoder_props_t props; + if(http_get_transcoder_properties(&hc->hc_req_args, &props)) { + tr = transcoder_create(gh); + transcoder_set_properties(tr, &props); + tsfix = tsfix_create(tr); + } else +#endif tsfix = tsfix_create(gh); st = tsfix; flags = 0; @@ -710,6 +763,12 @@ http_stream_channel(http_connection_t *hc, channel_t *ch) if(gh) globalheaders_destroy(gh); + +#if ENABLE_LIBAV + if(tr) + transcoder_destroy(tr); +#endif + if(tsfix) tsfix_destroy(tsfix); From 4b86d2fd200fc2a65dab0da6381f1337b892ac63 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Mon, 13 May 2013 09:27:01 +0100 Subject: [PATCH 501/503] Parse the audio_type field of the iso639_language_descriptor - this indicates if the audio stream contains audio description for the hard of hearing. Use this value when regenerating the PMT for the passthrough muxer, and also include it in the HTSP stream description messages. --- src/htsp_server.c | 1 + src/psi.c | 30 +++++++++++++++++++----------- src/service.c | 1 + src/service.h | 2 ++ src/streaming.h | 1 + 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index 2a670a69..eeb15ace 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2421,6 +2421,7 @@ htsp_subscription_start(htsp_subscription_t *hs, const streaming_start_t *ss) if (SCT_ISAUDIO(ssc->ssc_type)) { + htsmsg_add_u32(c, "audio_type", ssc->ssc_audio_type); if (ssc->ssc_channels) htsmsg_add_u32(c, "channels", ssc->ssc_channels); if (ssc->ssc_sri) diff --git a/src/psi.c b/src/psi.c index d86519c8..dc8ec979 100644 --- a/src/psi.c +++ b/src/psi.c @@ -172,16 +172,17 @@ psi_build_pat(service_t *t, uint8_t *buf, int maxlen, int pmtpid) #define PMT_UPDATE_PCR 0x1 #define PMT_UPDATE_NEW_STREAM 0x2 #define PMT_UPDATE_LANGUAGE 0x4 -#define PMT_UPDATE_FRAME_DURATION 0x8 -#define PMT_UPDATE_COMPOSITION_ID 0x10 -#define PMT_UPDATE_ANCILLARY_ID 0x20 -#define PMT_UPDATE_STREAM_DELETED 0x40 -#define PMT_UPDATE_NEW_CA_STREAM 0x80 -#define PMT_UPDATE_NEW_CAID 0x100 -#define PMT_UPDATE_CA_PROVIDER_CHANGE 0x200 -#define PMT_UPDATE_PARENT_PID 0x400 -#define PMT_UPDATE_CAID_DELETED 0x800 -#define PMT_REORDERED 0x1000 +#define PMT_UPDATE_AUDIO_TYPE 0x8 +#define PMT_UPDATE_FRAME_DURATION 0x10 +#define PMT_UPDATE_COMPOSITION_ID 0x20 +#define PMT_UPDATE_ANCILLARY_ID 0x40 +#define PMT_UPDATE_STREAM_DELETED 0x80 +#define PMT_UPDATE_NEW_CA_STREAM 0x100 +#define PMT_UPDATE_NEW_CAID 0x200 +#define PMT_UPDATE_CA_PROVIDER_CHANGE 0x400 +#define PMT_UPDATE_PARENT_PID 0x800 +#define PMT_UPDATE_CAID_DELETED 0x1000 +#define PMT_REORDERED 0x2000 /** * Add a CA descriptor @@ -397,6 +398,7 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, int position = 0; int tt_position = 1000; const char *lang = NULL; + uint8_t audio_type = 0; caid_t *c, *cn; @@ -529,6 +531,7 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, case DVB_DESC_LANGUAGE: lang = lang_code_get2((const char*)ptr, 3); + audio_type = ptr[3]; break; case DVB_DESC_TELETEXT: @@ -601,6 +604,11 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, memcpy(st->es_lang, lang, 4); } + if(st->es_audio_type != audio_type) { + update |= PMT_UPDATE_AUDIO_TYPE; + st->es_audio_type = audio_type; + } + if(composition_id != -1 && st->es_composition_id != composition_id) { st->es_composition_id = composition_id; update |= PMT_UPDATE_COMPOSITION_ID; @@ -756,7 +764,7 @@ psi_build_pmt(const streaming_start_t *ss, uint8_t *buf0, int maxlen, buf[0] = DVB_DESC_LANGUAGE; buf[1] = 4; memcpy(&buf[2],ssc->ssc_lang,3); - buf[5] = 0; /* Main audio */ + buf[5] = ssc->ssc_audio_type; dlen = 6; break; case SCT_DVBSUB: diff --git a/src/service.c b/src/service.c index 7851f5c2..c6666b5f 100644 --- a/src/service.c +++ b/src/service.c @@ -908,6 +908,7 @@ service_build_stream_start(service_t *t) ssc->ssc_type = st->es_type; memcpy(ssc->ssc_lang, st->es_lang, 4); + ssc->ssc_audio_type = st->es_audio_type; ssc->ssc_composition_id = st->es_composition_id; ssc->ssc_ancillary_id = st->es_ancillary_id; ssc->ssc_pid = st->es_pid; diff --git a/src/service.h b/src/service.h index d2f8b03b..8b96156e 100644 --- a/src/service.h +++ b/src/service.h @@ -82,6 +82,8 @@ typedef struct elementary_stream { uint16_t es_aspect_den; char es_lang[4]; /* ISO 639 2B 3-letter language code */ + uint8_t es_audio_type; /* Audio type */ + uint16_t es_composition_id; uint16_t es_ancillary_id; diff --git a/src/streaming.h b/src/streaming.h index 75dc7857..855be286 100644 --- a/src/streaming.h +++ b/src/streaming.h @@ -27,6 +27,7 @@ typedef struct streaming_start_component { int ssc_index; int ssc_type; char ssc_lang[4]; + uint8_t ssc_audio_type; uint16_t ssc_composition_id; uint16_t ssc_ancillary_id; uint16_t ssc_pid; From 477fa0a846780b4c6f594bc93354ed7fe2878ba9 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Mon, 13 May 2013 10:39:35 +0100 Subject: [PATCH 502/503] Add saving/loading of audio_type, and also display it in the webui --- src/psi.c | 22 ++++++++++++++++++++++ src/psi.h | 1 + src/webui/extjs.c | 8 +++++++- src/webui/static/app/tvadapters.js | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/psi.c b/src/psi.c index dc8ec979..378cc4c4 100644 --- a/src/psi.c +++ b/src/psi.c @@ -912,6 +912,20 @@ psi_caid2name(uint16_t caid) return buf; } +const char * +psi_audio_type2desc(uint8_t audio_type) +{ + /* From ISO 13818-1 - ISO 639 language descriptor */ + switch(audio_type) { + case 0: return ""; /* "Undefined" in the standard, but used for normal audio */ + case 1: return "Clean effects"; + case 2: return "Hearing impaired"; + case 3: return "Visually impaired commentary"; + } + + return "Reserved"; +} + /** * */ @@ -977,6 +991,9 @@ psi_save_service_settings(htsmsg_t *m, service_t *t) if(st->es_lang[0]) htsmsg_add_str(sub, "language", st->es_lang); + if (SCT_ISAUDIO(st->es_type)) + htsmsg_add_u32(sub, "audio_type", st->es_audio_type); + if(st->es_type == SCT_CA) { caid_t *c; @@ -1127,6 +1144,11 @@ psi_load_service_settings(htsmsg_t *m, service_t *t) if((v = htsmsg_get_str(c, "language")) != NULL) strncpy(st->es_lang, lang_code_get(v), 3); + if (SCT_ISAUDIO(type)) { + if(!htsmsg_get_u32(c, "audio_type", &u32)) + st->es_audio_type = u32; + } + if(!htsmsg_get_u32(c, "position", &u32)) st->es_position = u32; diff --git a/src/psi.h b/src/psi.h index 34c9736b..5dfc98b5 100644 --- a/src/psi.h +++ b/src/psi.h @@ -46,6 +46,7 @@ int psi_build_pmt(const streaming_start_t *ss, uint8_t *buf, int maxlen, int version, int pcrpid); const char *psi_caid2name(uint16_t caid); +const char *psi_audio_type2desc(uint8_t audio_type); void psi_load_service_settings(htsmsg_t *m, struct service *t); void psi_save_service_settings(htsmsg_t *m, struct service *t); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index c7a6dfcb..dd2b07d1 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1634,7 +1634,13 @@ extjs_servicedetails(http_connection_t *hc, case SCT_MP4A: case SCT_AAC: case SCT_MPEG2AUDIO: - htsmsg_add_str(c, "details", st->es_lang); + if (st->es_audio_type) { + snprintf(buf, sizeof(buf), "%s (%s)", st->es_lang, + psi_audio_type2desc(st->es_audio_type)); + htsmsg_add_str(c, "details", buf); + } else { + htsmsg_add_str(c, "details", st->es_lang); + } break; case SCT_DVBSUB: diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index 3661fabd..8da843f1 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -99,7 +99,7 @@ tvheadend.showTransportDetails = function(data) { win = new Ext.Window({ title : 'Service details for ' + data.title, layout : 'fit', - width : 400, + width : 450, height : 400, plain : true, bodyStyle : 'padding: 5px', From 29f715f685b502d332359dccd4047a222fc679e4 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Mon, 13 May 2013 11:23:26 +0100 Subject: [PATCH 503/503] Bump HTSP protocol version to 11 for the recent transcoding changes and the addition of the audio_type field --- src/htsp_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/htsp_server.c b/src/htsp_server.c index eeb15ace..88fe3bdd 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -59,7 +59,7 @@ static void *htsp_server, *htsp_server_2; -#define HTSP_PROTO_VERSION 10 +#define HTSP_PROTO_VERSION 11 #define HTSP_ASYNC_OFF 0x00 #define HTSP_ASYNC_ON 0x01