From 0edd429da6e9aea9476f7568c51585e35410ebb8 Mon Sep 17 00:00:00 2001
From: jdembski
Date: Wed, 5 Jan 2011 14:20:52 +0100
Subject: [PATCH 01/72] Store the epg data on the harddisk on exit and re-load
on startup
---
src/epg.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/epg.h | 5 +++
src/main.c | 2 ++
3 files changed, 106 insertions(+)
diff --git a/src/epg.c b/src/epg.c
index c5f05ba3..30e00dee 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -25,6 +25,7 @@
#include "tvheadend.h"
#include "channels.h"
+#include "settings.h"
#include "epg.h"
#include "dvr/dvr.h"
#include "htsp.h"
@@ -533,6 +534,104 @@ epg_content_group_find_by_name(const char *name)
void
epg_init(void)
{
+ event_t *e;
+ int injected = 0;
+ int created = 0;
+ uint32_t id = 0;
+
+ htsmsg_t *l, *c;
+ htsmsg_field_t *f;
+
+ if((l = hts_settings_load("epg")) == NULL)
+ return;
+
+ HTSMSG_FOREACH(f, l) {
+ if((c = htsmsg_get_map_by_field(f)) == NULL)
+ continue;
+
+ e = epg_event_create_by_msg(c, &created);
+ htsmsg_get_u32(c, "id", &id);
+
+ hts_settings_remove("epg/%d", id);
+
+ if(created)
+ injected++;
+ }
+ htsmsg_destroy(l);
+
+ tvhlog(LOG_INFO, "epg", "Injected %d epg events.", injected);
+}
+
+/*
+ *
+ */
+event_t *
+epg_event_create_by_msg(htsmsg_t *c, int *created)
+{
+ channel_t *ch;
+ event_t *e = NULL;
+ uint32_t ch_id = 0;
+ uint32_t e_start = 0;
+ uint32_t e_stop = 0;
+ int e_dvb_id = 0;
+
+ if (created != NULL)
+ *created = 0;
+
+
+
+ // Now create the event
+ htsmsg_get_u32(c, "ch_id", &ch_id);
+ ch = channel_find_by_identifier(ch_id);
+ if (ch == NULL)
+ tvhlog(LOG_DEBUG, "epg", "Cannot find this channel, skipping...");
+ else {
+ htsmsg_get_u32(c, "start", &e_start);
+ htsmsg_get_u32(c, "stop", &e_stop);
+ htsmsg_get_s32(c, "dvb_id", &e_dvb_id);
+
+ e = epg_event_create(ch, e_start, e_stop, e_dvb_id, created);
+
+ int changed = 0;
+
+ changed |= epg_event_set_title(e, htsmsg_get_str(c, "title"));
+ changed |= epg_event_set_desc(e, htsmsg_get_str(c, "desc"));
+
+ if(changed)
+ epg_event_updated(e);
+ }
+
+ return e;
+}
+
+/*
+ * Save the epg on disk
+ */
+void
+epg_save(void)
+{
+ event_t *e;
+ int saved = 0;
+
+ channel_t *ch;
+
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
+ RB_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_u32(m, "id", saved);
+ htsmsg_add_u32(m, "start", e->e_start);
+ htsmsg_add_u32(m, "stop", e->e_stop);
+ htsmsg_add_str(m, "title", e->e_title);
+ htsmsg_add_str(m, "desc", e->e_desc);
+ htsmsg_add_u32(m, "ch_id", ch->ch_id);
+ htsmsg_add_u32(m, "dvb_id", e->e_dvb_id);
+
+ hts_settings_save(m, "epg/%d", saved);
+
+ saved++;
+ }
+ }
+ tvhlog(LOG_DEBUG, "epg", "Wrote epg data for %d events", saved);
}
diff --git a/src/epg.h b/src/epg.h
index 017d7097..0046dbf4 100644
--- a/src/epg.h
+++ b/src/epg.h
@@ -20,6 +20,7 @@
#define EPG_H
#include "channels.h"
+#include "settings.h"
@@ -68,6 +69,8 @@ typedef struct event {
*/
void epg_init(void);
+void epg_save(void);
+
/**
* All the epg_event_set_ function return 1 if it actually changed
* the EPG records. otherwise it returns 0.
@@ -105,6 +108,8 @@ void epg_event_updated(event_t *e);
event_t *epg_event_create(channel_t *ch, time_t start, time_t stop,
int dvb_id, int *created);
+event_t *epg_event_create_by_msg(htsmsg_t *c, int *created);
+
event_t *epg_event_find_by_time(channel_t *ch, time_t t);
event_t *epg_event_find_by_id(int eventid);
diff --git a/src/main.c b/src/main.c
index 71afd566..d2ee3a4a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -433,6 +433,8 @@ main(int argc, char **argv)
mainloop();
+ epg_save();
+
tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend");
if(forkaway)
From 114ac7c3fd96a7d50ef358a5687391d96dc7b40a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 02:18:33 +0800
Subject: [PATCH 02/72] Provide a link to the http stream when the browser is
missing a vlc-compatible plugin.
---
src/webui/static/app/tvheadend.js | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index ccb8feb6..a2047b09 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -45,6 +45,10 @@ tvheadend.VLC = function(url) {
vlc.style.display = 'none';
}
+ var missingPlugin = document.createElement('div');
+ missingPlugin.style.display = 'none';
+ missingPlugin.style.padding = '5px';
+
var selectChannel = new Ext.form.ComboBox({
loadingText: 'Loading...',
width: 200,
@@ -59,8 +63,20 @@ tvheadend.VLC = function(url) {
selectChannel.on('select', function(c, r) {
var url = 'stream/channelid/' + r.data.chid;
var chName = r.data.name;
+ if (!chName.length) {
+ chName = 'the channel';
+ }
+
+ if(!vlc.playlist || vlc.playlist == 'undefined') {
+ var chUrl = '' + chName + '';
+ missingPlugin.innerHTML = 'You are missing a plugin for your browser.
';
+ missingPlugin.innerHTML += 'You can still watch ' + chUrl + ' using an external player.
';
+ missingPlugin.style.display = 'block';
+ return;
+ }
+
vlc.style.display = 'block';
-
+
if(vlc.playlist && vlc.playlist.isPlaying) {
vlc.playlist.stop();
}
@@ -150,7 +166,7 @@ tvheadend.VLC = function(url) {
disabled: true
},
],
- items: [vlc]
+ items: [vlc, missingPlugin]
});
win.on('render', function() {
From 8850be81c1e56eab94e225b533d59bfecab1fc9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 16:42:34 +0100
Subject: [PATCH 03/72] Added http-streaming of services. Tested and working
with mplayer. VLC does'nt start the playback since it waits for a pmt (that
is sent upon a START command that never comes).
---
src/webui/static/app/dvb.js | 2 +-
src/webui/webui.c | 45 ++++++++++++++++++++++++++++++++++---
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js
index d0c257b0..c82611a3 100644
--- a/src/webui/static/app/dvb.js
+++ b/src/webui/static/app/dvb.js
@@ -371,7 +371,7 @@ tvheadend.dvb_services = function(adapterId) {
dataIndex: 'id',
width: 50,
renderer: function(value, metadata, record, row, col, store) {
- url = makeRTSPprefix() + 'service/' + value
+ url = 'stream/service/' + value
return 'Play'
}
},
diff --git a/src/webui/webui.c b/src/webui/webui.c
index eda7f4bd..6316eaf1 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -284,6 +284,39 @@ http_stream_playlist(http_connection_t *hc)
pthread_mutex_unlock(&global_lock);
}
+/**
+ * Subscribes to a service and starts the streaming loop
+ */
+static int
+http_stream_service(http_connection_t *hc, service_t *service)
+{
+ streaming_queue_t sq;
+ th_subscription_t *s;
+
+ pthread_mutex_lock(&global_lock);
+
+ streaming_queue_init(&sq, ~SMT_TO_MASK(SUBSCRIPTION_RAW_MPEGTS));
+
+ s = subscription_create_from_service(service,
+ "HTTP", &sq.sq_st,
+ SUBSCRIPTION_RAW_MPEGTS);
+
+
+ pthread_mutex_unlock(&global_lock);
+
+ //We won't get a START command, send http-header here.
+ http_output_content(hc, "video/mp2t");
+
+ http_stream_run(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
*/
@@ -319,12 +352,14 @@ 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/
*/
static int
http_stream(http_connection_t *hc, const char *remain, void *opaque)
{
char *components[2];
channel_t *ch = NULL;
+ service_t *service = NULL;
if(http_access_verify(hc, ACCESS_STREAMING)) {
http_error(hc, HTTP_STATUS_UNAUTHORIZED);
@@ -351,16 +386,20 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
ch = channel_find_by_identifier(atoi(components[1]));
} else if(!strcmp(components[0], "channel")) {
ch = channel_find_by_name(components[1], 0, 0);
+ } else if(!strcmp(components[0], "service")) {
+ service = service_find_by_identifier(components[1]);
}
pthread_mutex_unlock(&global_lock);
- if(ch == NULL) {
+ if(ch != NULL) {
+ return http_stream_channel(hc, ch);
+ } else if(service != NULL) {
+ return http_stream_service(hc, service);
+ } else {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return HTTP_STATUS_BAD_REQUEST;
}
-
- return http_stream_channel(hc, ch);
}
From 3b69376f4007acf85384b77e5c526f859823ebce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 16:43:24 +0100
Subject: [PATCH 04/72] Check if the PAT is sent okey to the http-client.
---
src/webui/webui.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 6316eaf1..f5f9ef56 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -219,6 +219,10 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq)
pat_ts[4] = 0x00;
run = (write(hc->hc_fd, pat_ts, 188) == 188);
+ if(!run) {
+ break;
+ }
+
//Send PMT
memset(pmt_ts, 0xff, 188);
psi_build_pmt(ss, pmt_ts+5, 183, pcrpid);
From 48ee984082ed8726bc40466c64a5cc366e31e3cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Thu, 6 Jan 2011 23:01:50 +0100
Subject: [PATCH 05/72] Must escape % in printf formated strings
---
src/webui/extjs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index 58b9cc81..7b5041cb 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -152,7 +152,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
"\tpadding:0;\n"
"\tborder:0 none;\n"
"\toverflow:hidden;\n"
- "\theight:100%;\n"
+ "\theight:100%%;\n"
"}\n"
"#systemlog {\n"
"\tfont:normal 12px courier; font-weight: bold;\n"
From 29f5e48468c0cf5c622005edec690de5844990ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Fri, 7 Jan 2011 10:14:19 +0100
Subject: [PATCH 06/72] FIX: Crash due to NULL pointer access
---
src/epg.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/epg.c b/src/epg.c
index 30e00dee..bdbcd91a 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -617,18 +617,23 @@ epg_save(void)
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
- htsmsg_t *m = htsmsg_create_map();
- htsmsg_add_u32(m, "id", saved);
- htsmsg_add_u32(m, "start", e->e_start);
- htsmsg_add_u32(m, "stop", e->e_stop);
- htsmsg_add_str(m, "title", e->e_title);
- htsmsg_add_str(m, "desc", e->e_desc);
- htsmsg_add_u32(m, "ch_id", ch->ch_id);
- htsmsg_add_u32(m, "dvb_id", e->e_dvb_id);
+ if((e->e_start) && (e->e_stop)) {
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_u32(m, "id", saved);
+ htsmsg_add_u32(m, "start", e->e_start);
+ htsmsg_add_u32(m, "stop", e->e_stop);
+ if (e->e_title != NULL)
+ htsmsg_add_str(m, "title", e->e_title);
- hts_settings_save(m, "epg/%d", saved);
+ if (e->e_desc != NULL)
+ htsmsg_add_str(m, "desc", e->e_desc);
+ htsmsg_add_u32(m, "ch_id", ch->ch_id);
+ htsmsg_add_s32(m, "dvb_id", e->e_dvb_id);
- saved++;
+ hts_settings_save(m, "epg/%d", saved);
+
+ saved++;
+ }
}
}
tvhlog(LOG_DEBUG, "epg", "Wrote epg data for %d events", saved);
From f10ba2a2209d19efe921ab7ba168ed9bceb32d9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Fri, 7 Jan 2011 10:23:48 +0100
Subject: [PATCH 07/72] FIX: Crash due to NULL pointer access on epg load
---
src/epg.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/epg.c b/src/epg.c
index bdbcd91a..336609b2 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -568,18 +568,17 @@ epg_init(void)
event_t *
epg_event_create_by_msg(htsmsg_t *c, int *created)
{
- channel_t *ch;
+ channel_t *ch;
event_t *e = NULL;
uint32_t ch_id = 0;
uint32_t e_start = 0;
uint32_t e_stop = 0;
int e_dvb_id = 0;
+ const char *e_title, *e_desc;
if (created != NULL)
*created = 0;
-
-
// Now create the event
htsmsg_get_u32(c, "ch_id", &ch_id);
ch = channel_find_by_identifier(ch_id);
@@ -593,9 +592,14 @@ epg_event_create_by_msg(htsmsg_t *c, int *created)
e = epg_event_create(ch, e_start, e_stop, e_dvb_id, created);
int changed = 0;
-
- changed |= epg_event_set_title(e, htsmsg_get_str(c, "title"));
- changed |= epg_event_set_desc(e, htsmsg_get_str(c, "desc"));
+
+ e_title = htsmsg_get_str(c, "title");
+ if (e_title != NULL)
+ changed |= epg_event_set_title(e, e_title);
+
+ e_desc = htsmsg_get_str(c, "title");
+ if (e_desc != NULL)
+ changed |= epg_event_set_title(e, e_desc);
if(changed)
epg_event_updated(e);
From 5482182c80111c6da29628aff0d3007cd0054d2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Fri, 7 Jan 2011 12:21:16 +0100
Subject: [PATCH 08/72] Feature: Add the possibility to update timers via HTSP
---
src/dvr/dvr.h | 2 ++
src/dvr/dvr_db.c | 20 ++++++++++++++++++++
src/htsp.c | 40 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 62 insertions(+)
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index 888fc986..ed22ea28 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -242,6 +242,8 @@ dvr_entry_t *dvr_entry_create(const char *dvr_config_name,
epg_episode_t *ee, uint8_t content_type,
dvr_prio_t pri);
+dvr_entry_t *dvr_entry_update(dvr_entry_t *de, const char* de_title, int de_start, int de_stop);
+
void dvr_init(void);
void dvr_autorec_init(void);
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index c7933c62..e777066f 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -589,6 +589,26 @@ dvr_timer_expire(void *aux)
}
+/**
+ *
+ */
+dvr_entry_t *
+dvr_entry_update(dvr_entry_t *de, const char* de_title, int de_start, int de_stop)
+{
+
+ de->de_title = strdup(de_title);
+ de->de_start = de_start;
+ de->de_stop = de_stop;
+
+ dvr_entry_save(de);
+ htsp_dvr_entry_update(de);
+ dvr_entry_notify(de);
+
+
+ tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": Updated Timer", de->de_title, de->de_channel->ch_name);
+
+ return de;
+}
/**
*
diff --git a/src/htsp.c b/src/htsp.c
index 9a49ad21..b71f4c3e 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -534,6 +534,45 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
return out;
}
+/**
+ * update a Dvrentry
+ */
+static htsmsg_t *
+htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
+{
+ htsmsg_t *out;
+ uint32_t dvrEntryId;
+ dvr_entry_t *de;
+ uint32_t start;
+ uint32_t stop;
+ const char *title = NULL;
+
+ if(htsmsg_get_u32(in, "id", &dvrEntryId))
+ return htsp_error("Missing argument 'id'");
+
+ if(htsmsg_get_u32(in, "start", &start))
+ return htsp_error("Missing argument 'start'");
+
+ if(htsmsg_get_u32(in, "stop", &stop))
+ return htsp_error("Missing argument 'stop'");
+
+ title = htsmsg_get_str(in, "title");
+ if (title == NULL)
+ return htsp_error("Missing argument 'title'");
+
+
+ if( (de = dvr_entry_find_by_id(dvrEntryId)) == NULL)
+ return htsp_error("id not found");
+
+ de = dvr_entry_update(de, title, start, stop);
+
+ //create response
+ out = htsmsg_create_map();
+ htsmsg_add_u32(out, "success", 1);
+
+ return out;
+}
+
/**
* delete a Dvrentry
*/
@@ -925,6 +964,7 @@ struct {
{ "unsubscribe", htsp_method_unsubscribe, ACCESS_STREAMING},
{ "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING},
{ "addDvrEntry", htsp_method_addDvrEntry, ACCESS_RECORDER},
+ { "updateDvrEntry", htsp_method_updateDvrEntry, ACCESS_RECORDER},
{ "deleteDvrEntry", htsp_method_deleteDvrEntry, ACCESS_RECORDER},
{ "epgQuery", htsp_method_epgQuery, ACCESS_STREAMING},
From cc414cc6bc19e12368e3103bae8e2fd5ec99e583 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Fri, 7 Jan 2011 14:30:17 +0100
Subject: [PATCH 09/72] Change the dvr_entry_update function so that it will
update the entry even if only one of start, stop or title has been send via
htsp
---
src/htsp.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/src/htsp.c b/src/htsp.c
index b71f4c3e..4429e6fb 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -549,20 +549,19 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
if(htsmsg_get_u32(in, "id", &dvrEntryId))
return htsp_error("Missing argument 'id'");
+
+ if( (de = dvr_entry_find_by_id(dvrEntryId)) == NULL)
+ return htsp_error("id not found");
if(htsmsg_get_u32(in, "start", &start))
- return htsp_error("Missing argument 'start'");
+ start = de->de_start;
if(htsmsg_get_u32(in, "stop", &stop))
- return htsp_error("Missing argument 'stop'");
+ stop = de->de_stop;
title = htsmsg_get_str(in, "title");
if (title == NULL)
- return htsp_error("Missing argument 'title'");
-
-
- if( (de = dvr_entry_find_by_id(dvrEntryId)) == NULL)
- return htsp_error("id not found");
+ title = de->de_title;
de = dvr_entry_update(de, title, start, stop);
From ad2ab1ee04ea04e396d65272b7e99b16b9c8c356 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sat, 8 Jan 2011 13:36:48 +0100
Subject: [PATCH 10/72] Add hts_settings_open_file()
---
src/settings.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/settings.h | 2 ++
2 files changed, 56 insertions(+)
diff --git a/src/settings.c b/src/settings.c
index 1c5128b3..ca3c7270 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -284,3 +284,57 @@ hts_settings_remove(const char *pathfmt, ...)
return;
unlink(fullpath);
}
+
+
+/**
+ *
+ */
+int
+hts_settings_open_file(int for_write, const char *pathfmt, ...)
+{
+ char path[256];
+ char fullpath[256];
+ int x, l;
+ va_list ap;
+ struct stat st;
+ char *n;
+
+ if(settingspath == NULL)
+ return -1;
+
+ va_start(ap, pathfmt);
+ vsnprintf(path, sizeof(path), pathfmt, ap);
+ va_end(ap);
+
+ n = path;
+
+ while(*n) {
+ if(*n == ':' || *n == '?' || *n == '*' || *n > 127 || *n < 32)
+ *n = '_';
+ n++;
+ }
+
+ l = strlen(path);
+
+ for(x = 0; x < l; x++) {
+ if(path[x] == '/') {
+ /* It's a directory here */
+
+ path[x] = 0;
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", settingspath, path);
+
+ if(stat(fullpath, &st) && mkdir(fullpath, 0700)) {
+ tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s",
+ fullpath, strerror(errno));
+ return -1;
+ }
+ path[x] = '/';
+ }
+ }
+
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", settingspath, path);
+
+ int flags = for_write ? O_CREAT | O_TRUNC | O_WRONLY : O_RDONLY;
+
+ return tvh_open(fullpath, flags, 0700);
+}
diff --git a/src/settings.h b/src/settings.h
index 6be8cffe..1713d9d2 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -32,4 +32,6 @@ void hts_settings_remove(const char *pathfmt, ...);
const char *hts_settings_get_root(void);
+int hts_settings_open_file(int for_write, const char *pathfmt, ...);
+
#endif /* HTSSETTINGS_H__ */
From 2948456f41caf65093df5e3c49aaacc278feb8a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sat, 8 Jan 2011 13:37:29 +0100
Subject: [PATCH 11/72] =?UTF-8?q?Store=20EPG=20on=20disk=20(using=20a=20bi?=
=?UTF-8?q?nary=20format)=20Based=20on=20work=20by=20J=C3=B6rg=20Dembski?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/epg.c | 229 +++++++++++++++++++++++++++++++++++-------------------
src/epg.h | 8 +-
2 files changed, 152 insertions(+), 85 deletions(-)
diff --git a/src/epg.c b/src/epg.c
index 336609b2..24bf59d2 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+#include
+#include
#include
#include
#include
@@ -29,6 +31,7 @@
#include "epg.h"
#include "dvr/dvr.h"
#include "htsp.h"
+#include "htsmsg_binary.h"
#define EPG_MAX_AGE 86400
@@ -528,119 +531,187 @@ epg_content_group_find_by_name(const char *name)
}
-/*
+/**
*
*/
-void
-epg_init(void)
-{
- event_t *e;
- int injected = 0;
- int created = 0;
- uint32_t id = 0;
-
- htsmsg_t *l, *c;
- htsmsg_field_t *f;
-
- if((l = hts_settings_load("epg")) == NULL)
- return;
-
- HTSMSG_FOREACH(f, l) {
- if((c = htsmsg_get_map_by_field(f)) == NULL)
- continue;
-
- e = epg_event_create_by_msg(c, &created);
- htsmsg_get_u32(c, "id", &id);
-
- hts_settings_remove("epg/%d", id);
-
- if(created)
- injected++;
- }
- htsmsg_destroy(l);
-
- tvhlog(LOG_INFO, "epg", "Injected %d epg events.", injected);
-}
-
-/*
- *
- */
-event_t *
-epg_event_create_by_msg(htsmsg_t *c, int *created)
+static int
+epg_event_create_by_msg(htsmsg_t *c, time_t now)
{
channel_t *ch;
event_t *e = NULL;
uint32_t ch_id = 0;
uint32_t e_start = 0;
uint32_t e_stop = 0;
- int e_dvb_id = 0;
- const char *e_title, *e_desc;
+ int e_dvb_id = 0, v;
+ const char *s;
- if (created != NULL)
- *created = 0;
+ // Now create the event
+ if(htsmsg_get_u32(c, "ch_id", &ch_id))
+ return 0;
- // Now create the event
- htsmsg_get_u32(c, "ch_id", &ch_id);
- ch = channel_find_by_identifier(ch_id);
- if (ch == NULL)
- tvhlog(LOG_DEBUG, "epg", "Cannot find this channel, skipping...");
- else {
- htsmsg_get_u32(c, "start", &e_start);
- htsmsg_get_u32(c, "stop", &e_stop);
- htsmsg_get_s32(c, "dvb_id", &e_dvb_id);
+ if((ch = channel_find_by_identifier(ch_id)) == NULL)
+ return 0;
- e = epg_event_create(ch, e_start, e_stop, e_dvb_id, created);
+ if(htsmsg_get_u32(c, "start", &e_start))
+ return 0;
- int changed = 0;
+ if(htsmsg_get_u32(c, "stop", &e_stop))
+ return 0;
- e_title = htsmsg_get_str(c, "title");
- if (e_title != NULL)
- changed |= epg_event_set_title(e, e_title);
+ if(e_stop < now)
+ return 0;
- e_desc = htsmsg_get_str(c, "title");
- if (e_desc != NULL)
- changed |= epg_event_set_title(e, e_desc);
+ if(htsmsg_get_s32(c, "dvb_id", &e_dvb_id))
+ e_dvb_id = -1;
- if(changed)
- epg_event_updated(e);
- }
+ e = epg_event_create(ch, e_start, e_stop, e_dvb_id, NULL);
- return e;
+ if((s = htsmsg_get_str(c, "title")) != NULL)
+ epg_event_set_title(e, s);
+
+ if((s = htsmsg_get_str(c, "desc")) != NULL)
+ epg_event_set_desc(e, s);
+
+ if(!htsmsg_get_s32(c, "season", &v))
+ e->e_episode.ee_season = v;
+
+ if(!htsmsg_get_s32(c, "episode", &v))
+ e->e_episode.ee_episode = v;
+
+ if(!htsmsg_get_s32(c, "part", &v))
+ e->e_episode.ee_part = v;
+
+ if((s = htsmsg_get_str(c, "epname")) != NULL)
+ tvh_str_set(&e->e_episode.ee_onscreen, s);
+
+ return 1;
}
-/*
+
+/**
+ *
+ */
+static void
+epg_load(void)
+{
+ struct stat st;
+ int fd = hts_settings_open_file(0, "epgdb");
+ time_t now;
+ int created = 0;
+
+ time(&now);
+
+ if(fd == -1)
+ return;
+
+ if(fstat(fd, &st)) {
+ close(fd);
+ return;
+ }
+ uint8_t *mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if(mem == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+ const uint8_t *rp = mem;
+ size_t remain = st.st_size;
+
+ while(remain > 4) {
+ int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3];
+ remain -= 4;
+ rp += 4;
+
+ if(msglen > remain) {
+ tvhlog(LOG_ERR, "EPG", "Malformed EPG database, skipping some data");
+ break;
+ }
+ htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL);
+
+ created += epg_event_create_by_msg(m, now);
+
+ htsmsg_destroy(m);
+ rp += msglen;
+ remain -= msglen;
+ }
+
+ munmap(mem, st.st_size);
+ close(fd);
+ tvhlog(LOG_NOTICE, "EPG", "Injected %d event from disk database", created);
+}
+
+/**
+ *
+ */
+void
+epg_init(void)
+{
+ epg_load();
+}
+
+
+/**
* Save the epg on disk
*/
void
epg_save(void)
{
event_t *e;
- int saved = 0;
-
+ int num_saved = 0;
+ size_t msglen;
+ void *msgdata;
channel_t *ch;
+ int fd = hts_settings_open_file(1, "epgdb");
+
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
- if((e->e_start) && (e->e_stop)) {
- htsmsg_t *m = htsmsg_create_map();
- htsmsg_add_u32(m, "id", saved);
- htsmsg_add_u32(m, "start", e->e_start);
- htsmsg_add_u32(m, "stop", e->e_stop);
- if (e->e_title != NULL)
- htsmsg_add_str(m, "title", e->e_title);
+ if(!e->e_start || !e->e_stop)
+ continue;
- if (e->e_desc != NULL)
- htsmsg_add_str(m, "desc", e->e_desc);
- htsmsg_add_u32(m, "ch_id", ch->ch_id);
- htsmsg_add_s32(m, "dvb_id", e->e_dvb_id);
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_u32(m, "ch_id", ch->ch_id);
+ htsmsg_add_u32(m, "start", e->e_start);
+ htsmsg_add_u32(m, "stop", e->e_stop);
+ if(e->e_title != NULL)
+ htsmsg_add_str(m, "title", e->e_title);
+ if(e->e_desc != NULL)
+ htsmsg_add_str(m, "desc", e->e_desc);
+ if(e->e_dvb_id)
+ htsmsg_add_u32(m, "dvb_id", e->e_dvb_id);
- hts_settings_save(m, "epg/%d", saved);
+ if(e->e_episode.ee_season)
+ htsmsg_add_u32(m, "season", e->e_episode.ee_season);
- saved++;
+ if(e->e_episode.ee_episode)
+ htsmsg_add_u32(m, "episode", e->e_episode.ee_episode);
+
+ if(e->e_episode.ee_part)
+ htsmsg_add_u32(m, "part", e->e_episode.ee_part);
+
+ if(e->e_episode.ee_onscreen)
+ htsmsg_add_str(m, "epname", e->e_episode.ee_onscreen);
+
+
+ int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000);
+ htsmsg_destroy(m);
+
+ if(!r) {
+ ssize_t written = write(fd, msgdata, msglen);
+ int err = errno;
+ free(msgdata);
+ if(written != msglen) {
+ tvhlog(LOG_DEBUG, "epg", "Failed to store EPG on disk -- %s",
+ strerror(err));
+ close(fd);
+ hts_settings_remove("epgdb");
+ return;
+ }
}
+ num_saved++;
}
}
- tvhlog(LOG_DEBUG, "epg", "Wrote epg data for %d events", saved);
+ close(fd);
+ tvhlog(LOG_DEBUG, "EPG", "Stored EPG data for %d events on disk", num_saved);
}
diff --git a/src/epg.h b/src/epg.h
index 0046dbf4..ebd7b0c9 100644
--- a/src/epg.h
+++ b/src/epg.h
@@ -82,11 +82,9 @@ void epg_save(void);
* can combine multiple set()'s into one update
*
*/
-int epg_event_set_title(event_t *e, const char *title)
- __attribute__ ((warn_unused_result));
+int epg_event_set_title(event_t *e, const char *title);
-int epg_event_set_desc(event_t *e, const char *desc)
- __attribute__ ((warn_unused_result));
+int epg_event_set_desc(event_t *e, const char *desc);
int epg_event_set_ext_desc(event_t *e, int ext_dn, const char *desc)
__attribute__ ((warn_unused_result));
@@ -108,8 +106,6 @@ void epg_event_updated(event_t *e);
event_t *epg_event_create(channel_t *ch, time_t start, time_t stop,
int dvb_id, int *created);
-event_t *epg_event_create_by_msg(htsmsg_t *c, int *created);
-
event_t *epg_event_find_by_time(channel_t *ch, time_t t);
event_t *epg_event_find_by_id(int eventid);
From 2dc06edcf76320f04900b43dcb1956fbde989b48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sun, 9 Jan 2011 12:55:52 +0100
Subject: [PATCH 12/72] Add provider parsing for DRECrypt
---
src/psi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/psi.c b/src/psi.c
index 42231b7c..5c792a58 100644
--- a/src/psi.c
+++ b/src/psi.c
@@ -306,6 +306,9 @@ psi_desc_ca(service_t *t, const uint8_t *buffer, int size)
i += nanolen;
}
break;
+ case 0x4a00://DRECrypt
+ provid = size < 4 ? 0 : buffer[4];
+ break;
default:
provid = 0;
break;
From cc42203b53967414e4d0badd99f5d443c587b73f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sun, 9 Jan 2011 22:23:05 +0100
Subject: [PATCH 13/72] Make IPTV PAT parser skip over services with service_id
set to 0
Ticket #318
---
src/iptv_input.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/iptv_input.c b/src/iptv_input.c
index 70bdb6f4..8a4b2bd7 100644
--- a/src/iptv_input.c
+++ b/src/iptv_input.c
@@ -60,13 +60,18 @@ iptv_got_pat(const uint8_t *ptr, size_t len, void *aux)
len -= 8;
ptr += 8;
- if(len < 4)
- return;
+ while(len >= 4) {
- prognum = ptr[0] << 8 | ptr[1];
- pmt = (ptr[2] & 0x1f) << 8 | ptr[3];
+ prognum = ptr[0] << 8 | ptr[1];
+ pmt = (ptr[2] & 0x1f) << 8 | ptr[3];
- t->s_pmt_pid = pmt;
+ if(prognum != 0) {
+ t->s_pmt_pid = pmt;
+ return;
+ }
+ ptr += 4;
+ len -= 4;
+ }
}
From eeb20b9b1ca33be1c74506afd87df85817c8628f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Thu, 13 Jan 2011 09:40:04 +0100
Subject: [PATCH 14/72] Add extra start / stop time information to status.xml
page (redmine ticket #351)
---
src/webui/simpleui.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c
index 197825af..1c791092 100644
--- a/src/webui/simpleui.c
+++ b/src/webui/simpleui.c
@@ -383,7 +383,7 @@ page_status(http_connection_t *hc,
if (DVR_SCHEDULED == de->de_sched_state)
{
- timelefttemp = (int) (de->de_start - now) / 60; // output minutes
+ timelefttemp = (int) ((de->de_start - now) / 60) - de->de_start_extra; // output minutes
if (timelefttemp < timeleft)
timeleft = timelefttemp;
}
@@ -398,19 +398,23 @@ page_status(http_connection_t *hc,
"%02d/%02d/%02d"
""
"%d"
+ "%d"
""
""
"%02d/%02d/%02d"
""
"%d"
+ "%d"
""
"%s",
a.tm_year + 1900, a.tm_mon, a.tm_mday,
a.tm_hour, a.tm_min,
de->de_start,
+ de->de_start_extra,
b.tm_year+1900, b.tm_mon, b.tm_mday,
b.tm_hour, b.tm_min,
de->de_stop,
+ de->de_stop_extra,
de->de_title);
rstatus = val2str(de->de_sched_state, recstatustxt);
From 3fa3a9c1548ab9f08b28294bc8ea417bf554b4d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Dembski?=
Date: Thu, 13 Jan 2011 09:43:02 +0100
Subject: [PATCH 15/72] Formatting
---
src/webui/simpleui.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c
index 1c791092..8e322f6d 100644
--- a/src/webui/simpleui.c
+++ b/src/webui/simpleui.c
@@ -398,23 +398,23 @@ page_status(http_connection_t *hc,
"%02d/%02d/%02d"
""
"%d"
- "%d"
+ "%d"
""
""
"%02d/%02d/%02d"
""
"%d"
- "%d"
+ "%d"
""
"%s",
a.tm_year + 1900, a.tm_mon, a.tm_mday,
a.tm_hour, a.tm_min,
de->de_start,
- de->de_start_extra,
+ de->de_start_extra,
b.tm_year+1900, b.tm_mon, b.tm_mday,
b.tm_hour, b.tm_min,
de->de_stop,
- de->de_stop_extra,
+ de->de_stop_extra,
de->de_title);
rstatus = val2str(de->de_sched_state, recstatustxt);
From a7bbf812119faa26cbe2bb3b2e74f7474eacb233 Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Wed, 19 Jan 2011 09:31:52 +0100
Subject: [PATCH 16/72] updated .gitignore to ignore Eclipse project files
---
.gitignore | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.gitignore b/.gitignore
index e98183de..bba2cfd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
build.*
config.default
+
+.cproject
+.project
+.settings
From a137baf845594a66e82f82b994311ac241b71017 Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Wed, 19 Jan 2011 09:32:49 +0100
Subject: [PATCH 17/72] fix typo in cwc
---
src/cwc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/cwc.c b/src/cwc.c
index b47563bd..8eb098fe 100644
--- a/src/cwc.c
+++ b/src/cwc.c
@@ -269,7 +269,7 @@ typedef struct cwc {
*/
static void cwc_service_destroy(th_descrambler_t *td);
-static void cwc_detecs_card_type(cwc_t *cwc);
+static void cwc_detect_card_type(cwc_t *cwc);
void cwc_emm_conax(cwc_t *cwc, uint8_t *data, int len);
void cwc_emm_irdeto(cwc_t *cwc, uint8_t *data, int len);
void cwc_emm_dre(cwc_t *cwc, uint8_t *data, int len);
@@ -578,7 +578,7 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len)
cwc->cwc_ua[0], cwc->cwc_ua[1], cwc->cwc_ua[2], cwc->cwc_ua[3], cwc->cwc_ua[4], cwc->cwc_ua[5], cwc->cwc_ua[6], cwc->cwc_ua[7],
nprov);
- cwc_detecs_card_type(cwc);
+ cwc_detect_card_type(cwc);
msg += 15;
plen -= 12;
@@ -644,7 +644,7 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len)
* based on the equivalent in sasc-ng
*/
static void
-cwc_detecs_card_type(cwc_t *cwc)
+cwc_detect_card_type(cwc_t *cwc)
{
uint8_t c_sys = cwc->cwc_caid >> 8;
From 5b640b2ed062f26fb48f6e7bcad93086f822b78d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 25 Jan 2011 22:48:01 +0100
Subject: [PATCH 18/72] Keep CWC in a separate lock domain to avoid waiting for
global_lock
---
src/access.c | 1 +
src/capmt.c | 1 +
src/channels.c | 1 +
src/cwc.c | 43 ++++++++++++++++++++++---------------------
src/dtable.h | 2 ++
src/dvb/dvb_satconf.c | 1 +
src/dvr/dvr_autorec.c | 1 +
src/webui/extjs.c | 8 ++++----
8 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/src/access.c b/src/access.c
index 6de0d728..e04f7ad5 100644
--- a/src/access.c
+++ b/src/access.c
@@ -409,6 +409,7 @@ static const dtable_class_t access_dtc = {
.dtc_record_delete = access_record_delete,
.dtc_read_access = ACCESS_ADMIN,
.dtc_write_access = ACCESS_ADMIN,
+ .dtc_mutex = &global_lock,
};
/**
diff --git a/src/capmt.c b/src/capmt.c
index 0a0cefec..18cdf2da 100644
--- a/src/capmt.c
+++ b/src/capmt.c
@@ -841,6 +841,7 @@ static const dtable_class_t capmt_dtc = {
.dtc_record_delete = capmt_entry_delete,
.dtc_read_access = ACCESS_ADMIN,
.dtc_write_access = ACCESS_ADMIN,
+ .dtc_mutex = &global_lock,
};
/**
diff --git a/src/channels.c b/src/channels.c
index 8dd409f9..f13e0ed4 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -858,6 +858,7 @@ static const dtable_class_t channel_tags_dtc = {
.dtc_record_delete = channel_tag_record_delete,
.dtc_read_access = ACCESS_ADMIN,
.dtc_write_access = ACCESS_ADMIN,
+ .dtc_mutex = &global_lock,
};
diff --git a/src/cwc.c b/src/cwc.c
index 8eb098fe..d3dd460a 100644
--- a/src/cwc.c
+++ b/src/cwc.c
@@ -95,6 +95,7 @@ TAILQ_HEAD(cwc_message_queue, cwc_message);
LIST_HEAD(ecm_section_list, ecm_section);
static struct cwc_queue cwcs;
static pthread_cond_t cwc_config_changed;
+static pthread_mutex_t cwc_mutex;
static char *crypt_md5(const char *pw, const char *salt);
/**
@@ -202,7 +203,7 @@ typedef struct cwc {
int cwc_writer_running;
struct cwc_message_queue cwc_writeq;
- TAILQ_ENTRY(cwc) cwc_link; /* Linkage protected via global_lock */
+ TAILQ_ENTRY(cwc) cwc_link; /* Linkage protected via cwc_mutex */
struct cwc_service_list cwc_services;
@@ -770,18 +771,16 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg,
"Obtained key for for service \"%s\" in %lld ms, from %s",
t->s_svcname, delay, ct->cs_cwc->cwc_hostname);
- pthread_mutex_lock(&t->s_stream_mutex);
ct->cs_keystate = CS_RESOLVED;
memcpy(ct->cs_cw, msg + 3, 16);
ct->cs_pending_cw_update = 1;
- pthread_mutex_unlock(&t->s_stream_mutex);
}
}
/**
* Handle running reply
- * global_lock is held
+ * cwc_mutex is held
*/
static int
cwc_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
@@ -836,9 +835,9 @@ cwc_read(cwc_t *cwc, void *buf, size_t len, int timeout)
{
int r;
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_unlock(&cwc_mutex);
r = tcp_read_timeout(cwc->cwc_fd, buf, len, timeout);
- pthread_mutex_lock(&global_lock);
+ pthread_mutex_lock(&cwc_mutex);
if(cwc_must_break(cwc))
return ECONNABORTED;
@@ -1031,23 +1030,23 @@ cwc_thread(void *aux)
struct timespec ts;
int attempts = 0;
- pthread_mutex_lock(&global_lock);
+ pthread_mutex_lock(&cwc_mutex);
while(cwc->cwc_running) {
while(cwc->cwc_running && cwc->cwc_enabled == 0)
- pthread_cond_wait(&cwc->cwc_cond, &global_lock);
+ pthread_cond_wait(&cwc->cwc_cond, &cwc_mutex);
snprintf(hostname, sizeof(hostname), "%s", cwc->cwc_hostname);
port = cwc->cwc_port;
tvhlog(LOG_INFO, "cwc", "Attemping to connect to %s:%d", hostname, port);
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_unlock(&cwc_mutex);
fd = tcp_connect(hostname, port, errbuf, sizeof(errbuf), 10);
- pthread_mutex_lock(&global_lock);
+ pthread_mutex_lock(&cwc_mutex);
if(fd == -1) {
attempts++;
@@ -1092,7 +1091,7 @@ cwc_thread(void *aux)
"%s: Automatic connection attempt in in %d seconds",
cwc->cwc_hostname, d);
- pthread_cond_timedwait(&cwc_config_changed, &global_lock, &ts);
+ pthread_cond_timedwait(&cwc_config_changed, &cwc_mutex, &ts);
}
tvhlog(LOG_INFO, "cwc", "%s destroyed", cwc->cwc_hostname);
@@ -1110,7 +1109,7 @@ cwc_thread(void *aux)
free((void *)cwc->cwc_hostname);
free(cwc);
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_unlock(&cwc_mutex);
return NULL;
}
@@ -1164,7 +1163,7 @@ cwc_emm(uint8_t *data, int len)
{
cwc_t *cwc;
- lock_assert(&global_lock);
+ pthread_mutex_lock(&cwc_mutex);
TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
if(cwc->cwc_forward_emm && cwc->cwc_writer_running) {
@@ -1189,6 +1188,7 @@ cwc_emm(uint8_t *data, int len)
}
}
}
+ pthread_mutex_unlock(&cwc_mutex);
}
@@ -1447,7 +1447,7 @@ cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int mlen)
}
/**
- *
+ * t->s_streaming_mutex is held
*/
static void
cwc_table_input(struct th_descrambler *td, struct service *t,
@@ -1667,7 +1667,7 @@ cwc_descramble(th_descrambler_t *td, service_t *t, struct elementary_stream *st,
}
/**
- * global_lock is held
+ * cwc_mutex is held
* s_stream_mutex is held
*/
static void
@@ -1715,7 +1715,7 @@ cwc_find_stream_by_caid(service_t *t, int caid)
/**
* Check if our CAID's matches, and if so, link
*
- * global_lock is held
+ * global_lock is held. Not that we care about that, but either way, it is.
*/
void
cwc_service_start(service_t *t)
@@ -1724,7 +1724,7 @@ cwc_service_start(service_t *t)
cwc_service_t *ct;
th_descrambler_t *td;
- lock_assert(&global_lock);
+ pthread_mutex_lock(&cwc_mutex);
TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
if(cwc->cwc_caid == 0)
continue;
@@ -1753,6 +1753,7 @@ cwc_service_start(service_t *t)
service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);
}
+ pthread_mutex_unlock(&cwc_mutex);
}
@@ -1762,10 +1763,11 @@ cwc_service_start(service_t *t)
static void
cwc_destroy(cwc_t *cwc)
{
- lock_assert(&global_lock);
+ 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);
}
@@ -1890,8 +1892,6 @@ cwc_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
if((cwc = cwc_entry_find(id, maycreate)) == NULL)
return NULL;
- lock_assert(&global_lock);
-
if((s = htsmsg_get_str(values, "username")) != NULL) {
free(cwc->cwc_username);
cwc->cwc_username = strdup(s);
@@ -2025,6 +2025,7 @@ static const dtable_class_t cwc_dtc = {
.dtc_record_delete = cwc_entry_delete,
.dtc_read_access = ACCESS_ADMIN,
.dtc_write_access = ACCESS_ADMIN,
+ .dtc_mutex = &cwc_mutex,
};
@@ -2038,7 +2039,7 @@ cwc_init(void)
dtable_t *dt;
TAILQ_INIT(&cwcs);
-
+ pthread_mutex_init(&cwc_mutex, NULL);
pthread_cond_init(&cwc_config_changed, NULL);
dt = dtable_create(&cwc_dtc, "cwc", NULL);
diff --git a/src/dtable.h b/src/dtable.h
index fd0b422d..5a480ac2 100644
--- a/src/dtable.h
+++ b/src/dtable.h
@@ -40,6 +40,8 @@ typedef struct dtable_class {
int dtc_read_access;
int dtc_write_access;
+ pthread_mutex_t *dtc_mutex;
+
} dtable_class_t;
diff --git a/src/dvb/dvb_satconf.c b/src/dvb/dvb_satconf.c
index 964754ea..6a41e35c 100644
--- a/src/dvb/dvb_satconf.c
+++ b/src/dvb/dvb_satconf.c
@@ -218,6 +218,7 @@ static const dtable_class_t satconf_dtc = {
.dtc_record_delete = satconf_entry_delete,
.dtc_read_access = ACCESS_ADMIN,
.dtc_write_access = ACCESS_ADMIN,
+ .dtc_mutex = &global_lock,
};
diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c
index 91f18fa5..bd3e148b 100644
--- a/src/dvr/dvr_autorec.c
+++ b/src/dvr/dvr_autorec.c
@@ -403,6 +403,7 @@ static const dtable_class_t autorec_dtc = {
.dtc_record_delete = autorec_record_delete,
.dtc_read_access = ACCESS_RECORDER,
.dtc_write_access = ACCESS_RECORDER,
+ .dtc_mutex = &global_lock,
};
/**
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index 7b5041cb..d6c1caaf 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -230,7 +230,7 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
- pthread_mutex_lock(&global_lock);
+ pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
if(!strcmp(op, "create")) {
if(http_access_verify(hc, dt->dt_dtc->dtc_write_access))
@@ -264,15 +264,15 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
} else {
bad:
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
return HTTP_STATUS_BAD_REQUEST;
noaccess:
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
return HTTP_STATUS_BAD_REQUEST;
}
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
if(in != NULL)
htsmsg_destroy(in);
From 6fa99fbd59281f7f15cd22b3d26c57c144a94f16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 25 Jan 2011 23:47:35 +0100
Subject: [PATCH 19/72] Replace deprecated DES_key_sched() with
DES_set_key_unchecked()
---
src/cwc.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/cwc.c b/src/cwc.c
index d3dd460a..c42ec3f0 100644
--- a/src/cwc.c
+++ b/src/cwc.c
@@ -413,8 +413,8 @@ des_make_login_key(cwc_t *cwc, uint8_t *k)
des14[i] = cwc->cwc_confedkey[i] ^ k[i];
des_key_spread(des14, spread);
- DES_key_sched((DES_cblock *)spread, &cwc->cwc_k1);
- DES_key_sched((DES_cblock *)(spread+8), &cwc->cwc_k2);
+ DES_set_key_unchecked((DES_cblock *)spread, &cwc->cwc_k1);
+ DES_set_key_unchecked((DES_cblock *)(spread+8), &cwc->cwc_k2);
}
/**
@@ -432,8 +432,8 @@ des_make_session_key(cwc_t *cwc)
des14[i % 14] ^= k2[i];
des_key_spread(des14, spread);
- DES_key_sched((DES_cblock *)spread, &cwc->cwc_k1);
- DES_key_sched((DES_cblock *)(spread+8), &cwc->cwc_k2);
+ DES_set_key_unchecked((DES_cblock *)spread, &cwc->cwc_k1);
+ DES_set_key_unchecked((DES_cblock *)(spread+8), &cwc->cwc_k2);
}
/**
From 901c71347fffaafff644475caaa31f9e5fa3313a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 27 Jan 2011 16:05:26 +0100
Subject: [PATCH 20/72] Include radio stations in the service probe.
---
src/service.c | 8 ++++++++
src/service.h | 2 ++
src/serviceprobe.c | 15 ++++++++++-----
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/src/service.c b/src/service.c
index 68832e48..94c0f345 100644
--- a/src/service.c
+++ b/src/service.c
@@ -720,6 +720,14 @@ service_is_tv(service_t *t)
t->s_servicetype == ST_AC_HDTV;
}
+/**
+ *
+ */
+int
+service_is_radio(service_t *t)
+{
+ return t->s_servicetype == ST_RADIO;
+}
/**
*
diff --git a/src/service.h b/src/service.h
index b2a687f4..4042eff6 100644
--- a/src/service.h
+++ b/src/service.h
@@ -516,6 +516,8 @@ const char *service_servicetype_txt(service_t *t);
int service_is_tv(service_t *t);
+int service_is_radio(service_t *t);
+
void service_destroy(service_t *t);
void service_remove_subscriber(service_t *t, struct th_subscription *s,
diff --git a/src/serviceprobe.c b/src/serviceprobe.c
index 5b144396..826f7965 100644
--- a/src/serviceprobe.c
+++ b/src/serviceprobe.c
@@ -44,8 +44,8 @@ static pthread_cond_t serviceprobe_cond;
void
serviceprobe_enqueue(service_t *t)
{
- if(!service_is_tv(t))
- return; /* Don't even consider non-tv channels */
+ if(!service_is_tv(t) && !service_is_radio(t))
+ return; /* Don't even consider non-tv/non-radio channels */
if(t->s_sp_onqueue)
return;
@@ -170,9 +170,11 @@ serviceprobe_thread(void *aux)
tvhlog(LOG_INFO, "serviceprobe", "%20s: mapped to channel \"%s\"",
t->s_svcname, t->s_svcname);
- channel_tag_map(ch, channel_tag_find_by_name("TV channels", 1), 1);
- tvhlog(LOG_INFO, "serviceprobe", "%20s: joined tag \"%s\"",
- t->s_svcname, "TV channels");
+ if(service_is_tv(t)) {
+ channel_tag_map(ch, channel_tag_find_by_name("TV channels", 1), 1);
+ tvhlog(LOG_INFO, "serviceprobe", "%20s: joined tag \"%s\"",
+ t->s_svcname, "TV channels");
+ }
switch(t->s_servicetype) {
case ST_SDTV:
@@ -183,6 +185,9 @@ serviceprobe_thread(void *aux)
case ST_AC_HDTV:
str = "HDTV";
break;
+ case ST_RADIO:
+ str = "Radio";
+ break;
default:
str = NULL;
}
From 3ceb49c6d562264b16f8063507ab06e25fb78398 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 1 Feb 2011 20:29:33 +0100
Subject: [PATCH 21/72] Replace obvious mistake pthread_mutex_lock() ->
pthread_mutex_unlock()
---
src/webui/extjs.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index d6c1caaf..e6111064 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -264,15 +264,15 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
} else {
bad:
- pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
+ pthread_mutex_unlock(dt->dt_dtc->dtc_mutex);
return HTTP_STATUS_BAD_REQUEST;
noaccess:
- pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
+ pthread_mutex_unlock(dt->dt_dtc->dtc_mutex);
return HTTP_STATUS_BAD_REQUEST;
}
- pthread_mutex_lock(dt->dt_dtc->dtc_mutex);
+ pthread_mutex_unlock(dt->dt_dtc->dtc_mutex);
if(in != NULL)
htsmsg_destroy(in);
From 22b89fd780f4f80943cc7497706d781a8536df56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 17:47:06 +0100
Subject: [PATCH 22/72] Replaced the rtsp playlist with a http one (.m3u rather
than .pls). Yoy can now also request a playlist containing only one channel.
---
src/webui/webui.c | 90 +++++++++++++++++++++++++++++------------------
1 file changed, 55 insertions(+), 35 deletions(-)
diff --git a/src/webui/webui.c b/src/webui/webui.c
index f5f9ef56..f9a93612 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -121,35 +121,6 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
-/**
- * Playlist (.pls format) for the rtsp streams
- */
-static int
-page_rtsp_playlist(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- channel_t *ch = NULL;
- int i = 0;
- const char *host = http_arg_get(&hc->hc_args, "Host");
-
- htsbuf_qprintf(hq, "[playlist]\n");
-
- scopedgloballock();
-
- RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
- i++;
- htsbuf_qprintf(hq, "File%d=rtsp://%s/channelid/%d\n", i, host, ch->ch_id);
- htsbuf_qprintf(hq, "Title%d=%s\n", i, ch->ch_name);
- htsbuf_qprintf(hq, "Length%d=%d\n\n", i, -1);
- }
-
- htsbuf_qprintf(hq, "NumberOfEntries=%d\n\n", i);
- htsbuf_qprintf(hq, "Version=2");
-
- http_output_content(hc, "audio/x-scpls; charset=UTF-8");
- return 0;
-}
-
/**
* HTTP stream loop
*/
@@ -266,10 +237,10 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq)
}
/**
- * Playlist with http streams (.m3u format)
+ * Output playlist with http streams (.m3u format)
*/
static void
-http_stream_playlist(http_connection_t *hc)
+http_stream_playlist(http_connection_t *hc, channel_t *channel)
{
htsbuf_queue_t *hq = &hc->hc_reply;
channel_t *ch = NULL;
@@ -279,8 +250,10 @@ http_stream_playlist(http_connection_t *hc)
htsbuf_qprintf(hq, "#EXTM3U\n");
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
- htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name);
- htsbuf_qprintf(hq, "http://%s/stream/channelid/%d\n", host, ch->ch_id);
+ if (channel == NULL || ch == channel) {
+ htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name);
+ htsbuf_qprintf(hq, "http://%s/stream/channelid/%d\n", host, ch->ch_id);
+ }
}
http_output_content(hc, "application/x-mpegURL");
@@ -288,6 +261,53 @@ http_stream_playlist(http_connection_t *hc)
pthread_mutex_unlock(&global_lock);
}
+/**
+ * Handle requests for playlists
+ */
+static int
+page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
+{
+ char *components[2];
+ channel_t *ch = NULL;
+
+ if(http_access_verify(hc, ACCESS_STREAMING)) {
+ http_error(hc, HTTP_STATUS_UNAUTHORIZED);
+ return HTTP_STATUS_UNAUTHORIZED;
+ }
+
+ hc->hc_keep_alive = 0;
+
+ if(remain == NULL) {
+ http_stream_playlist(hc, NULL);
+ return 0;
+ }
+
+ if(http_tokenize((char *)remain, components, 2, '/') != 2) {
+ http_error(hc, HTTP_STATUS_BAD_REQUEST);
+ return HTTP_STATUS_BAD_REQUEST;
+ }
+
+ http_deescape(components[1]);
+
+ pthread_mutex_lock(&global_lock);
+
+ if(!strcmp(components[0], "channelid")) {
+ ch = channel_find_by_identifier(atoi(components[1]));
+ } else if(!strcmp(components[0], "channel")) {
+ ch = channel_find_by_name(components[1], 0, 0);
+ }
+
+ pthread_mutex_unlock(&global_lock);
+
+ if(ch == NULL) {
+ http_error(hc, HTTP_STATUS_BAD_REQUEST);
+ return HTTP_STATUS_BAD_REQUEST;
+ }
+
+ http_stream_playlist(hc, ch);
+ return 0;
+}
+
/**
* Subscribes to a service and starts the streaming loop
*/
@@ -373,7 +393,7 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
hc->hc_keep_alive = 0;
if(remain == NULL) {
- http_stream_playlist(hc);
+ http_stream_playlist(hc, NULL);
return 0;
}
@@ -589,7 +609,7 @@ webui_init(const char *contentpath)
http_path_add("/dvrfile", NULL, page_dvrfile, ACCESS_WEB_INTERFACE);
http_path_add("/favicon.ico", NULL, favicon, ACCESS_WEB_INTERFACE);
- http_path_add("/channels.pls", NULL, page_rtsp_playlist, ACCESS_WEB_INTERFACE);
+ http_path_add("/playlist", NULL, page_http_playlist, ACCESS_WEB_INTERFACE);
http_path_add("/state", NULL, page_statedump, ACCESS_ADMIN);
From 2412ac4643b4dd163a2739b10711c7b81104a1f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 17:53:13 +0100
Subject: [PATCH 23/72] send 400 error when the streaming url is invalid
instead of the playlist.
---
src/webui/webui.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/webui/webui.c b/src/webui/webui.c
index f9a93612..c2060b92 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -393,8 +393,8 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
hc->hc_keep_alive = 0;
if(remain == NULL) {
- http_stream_playlist(hc, NULL);
- return 0;
+ http_error(hc, HTTP_STATUS_BAD_REQUEST);
+ return HTTP_STATUS_BAD_REQUEST;
}
if(http_tokenize((char *)remain, components, 2, '/') != 2) {
From d8b23f47201a7effc2d2dcd60f06e2210f21f0c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 18:41:51 +0100
Subject: [PATCH 24/72] Eexpose a playlist url (.m3u) when the browser is
missing a vlc-compatible plugin
---
src/webui/static/app/tvheadend.js | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index a2047b09..688f7203 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -62,15 +62,21 @@ tvheadend.VLC = function(url) {
selectChannel.on('select', function(c, r) {
var url = 'stream/channelid/' + r.data.chid;
+ var playlist = 'playlist/channelid/' + r.data.chid;
var chName = r.data.name;
if (!chName.length) {
chName = 'the channel';
}
if(!vlc.playlist || vlc.playlist == 'undefined') {
- var chUrl = '' + chName + '';
- missingPlugin.innerHTML = 'You are missing a plugin for your browser.
';
- missingPlugin.innerHTML += 'You can still watch ' + chUrl + ' using an external player.
';
+ var innerHTML = '';
+ innerHTML += 'You are missing a plugin for your browser.'
+ innerHTML += 'You can still watch ' + chName;
+ innerHTML += ' using an external player.
';
+ innerHTML += 'M3U Playlist
';
+ innerHTML += 'Direct URL
';
+
+ missingPlugin.innerHTML = innerHTML;
missingPlugin.style.display = 'block';
return;
}
From d107748b91bce66268f1993415d3d208b8f5ca99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 18:42:24 +0100
Subject: [PATCH 25/72] cosmetics
---
src/webui/static/app/tvheadend.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index 688f7203..bc5af085 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -63,9 +63,10 @@ tvheadend.VLC = function(url) {
selectChannel.on('select', function(c, r) {
var url = 'stream/channelid/' + r.data.chid;
var playlist = 'playlist/channelid/' + r.data.chid;
+
var chName = r.data.name;
if (!chName.length) {
- chName = 'the channel';
+ chName = 'the stream';
}
if(!vlc.playlist || vlc.playlist == 'undefined') {
From 3c06b89ad37dcd60f387f79079398f0fa10fb96a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 18:43:25 +0100
Subject: [PATCH 26/72] Expose a link to the url passed to tvheadend.VLC if the
browser doesn't have a VLC-compatrible browser
---
src/webui/static/app/tvheadend.js | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index bc5af085..d8bf786c 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -182,6 +182,15 @@ tvheadend.VLC = function(url) {
win.getTopToolbar().add(new Ext.Toolbar.Spacer());
win.getTopToolbar().add(new Ext.Toolbar.Spacer());
win.getTopToolbar().add(sliderLabel);
+
+ if(url && (!vlc.playlist || vlc.playlist == 'undefined')) {
+ missingPlugin.style.display = 'none';
+
+ var chUrl = 'the stream';
+ missingPlugin.innerHTML = 'You are missing a plugin for your browser.
';
+ missingPlugin.innerHTML += 'You can still watch ' + chUrl + ' using an external player.
';
+ missingPlugin.style.display = 'block';
+ }
});
win.show();
From 0885b19df79e5203f07454f486b1ae775630448d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 6 Jan 2011 18:47:24 +0100
Subject: [PATCH 27/72] Fixed small typo causing the missingPlugin div not too
be displayed
---
src/webui/static/app/tvheadend.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index d8bf786c..2cd955fa 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -184,7 +184,7 @@ tvheadend.VLC = function(url) {
win.getTopToolbar().add(sliderLabel);
if(url && (!vlc.playlist || vlc.playlist == 'undefined')) {
- missingPlugin.style.display = 'none';
+ vlc.style.display = 'none';
var chUrl = 'the stream';
missingPlugin.innerHTML = 'You are missing a plugin for your browser.
';
From 7b1ce7852bda14ded502226ebdc3f3ffee65f0ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Tue, 1 Feb 2011 22:43:22 +0100
Subject: [PATCH 28/72] Initial ticket support, needs testing
---
src/access.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++
src/access.h | 24 +++++++-
src/http.c | 9 +++
src/http.h | 5 --
src/webui/webui.c | 13 +++--
5 files changed, 176 insertions(+), 12 deletions(-)
diff --git a/src/access.c b/src/access.c
index e04f7ad5..696822f2 100644
--- a/src/access.c
+++ b/src/access.c
@@ -37,10 +37,144 @@
#include "settings.h"
struct access_entry_queue access_entries;
+struct access_ticket_queue access_tickets;
const char *superuser_username;
const char *superuser_password;
+static pthread_mutex_t access_ticket_mutex;
+
+/**
+ *
+ */
+static void
+access_ticket_destroy(access_ticket_t *at)
+{
+ free(at->at_id);
+ free(at->at_resource);
+ TAILQ_REMOVE(&access_tickets, at, at_link);
+ free(at);
+}
+
+/**
+ *
+ */
+static access_ticket_t *
+access_ticket_find(const char *id)
+{
+ access_ticket_t *at = NULL;
+
+ if(id != NULL) {
+ TAILQ_FOREACH(at, &access_tickets, at_link)
+ if(!strcmp(at->at_id, id))
+ return at;
+ }
+
+ return NULL;
+}
+
+/**
+ *
+ */
+static void
+access_ticket_timout(void *aux)
+{
+ access_ticket_t *at = aux;
+
+ pthread_mutex_lock(&access_ticket_mutex);
+ access_ticket_destroy(at);
+ pthread_mutex_unlock(&access_ticket_mutex);
+}
+
+/**
+ * Create a new ticket for the requested resource and generate a id for it
+ */
+const char *
+access_ticket_create(const char *resource)
+{
+ uint8_t buf[20];
+ char id[41];
+ unsigned int i, rnd;
+ access_ticket_t *at;
+ SHA_CTX shactx;
+
+ static const char hex_string[16] = "0123456789ABCDEF";
+
+ pthread_mutex_lock(&access_ticket_mutex);
+
+ at = calloc(1, sizeof(access_ticket_t));
+ rnd = time(NULL);
+
+ //Generate a pseudo-random ticket id
+ SHA_Init(&shactx);
+ for(i=0; i<5; i++) {
+ SHA_Update(&shactx, (const uint8_t *)&rnd, sizeof(int));
+ rnd = rand_r(&rnd);
+ }
+ SHA_Final(buf, &shactx);
+
+ //convert to hexstring
+ for(i=0; i> 4) & 0xF)];
+ id[(i*2)+1] = hex_string[(buf[i]) & 0x0F];
+ }
+ id[40] = '\0';
+
+ at->at_id = strdup(id);
+ at->at_resource = strdup(resource);
+
+ TAILQ_INSERT_TAIL(&access_tickets, at, at_link);
+ gtimer_arm(&at->at_timer, access_ticket_timout, at, 60*5);
+
+ pthread_mutex_unlock(&access_ticket_mutex);
+
+ return at->at_id;
+}
+
+/**
+ *
+ */
+int
+access_ticket_delete(const char *id)
+{
+ access_ticket_t *at;
+ pthread_mutex_lock(&access_ticket_mutex);
+
+ if((at = access_ticket_find(id)) == NULL) {
+ pthread_mutex_unlock(&access_ticket_mutex);
+ return -1;
+ }
+
+ gtimer_disarm(&at->at_timer);
+ access_ticket_destroy(at);
+
+ pthread_mutex_unlock(&access_ticket_mutex);
+ return 0;
+}
+
+/**
+ *
+ */
+int
+access_ticket_verify(const char *id, const char *resource)
+{
+ access_ticket_t *at;
+
+ pthread_mutex_lock(&access_ticket_mutex);
+
+ if((at = access_ticket_find(id)) == NULL) {
+ pthread_mutex_unlock(&access_ticket_mutex);
+ return -1;
+ }
+
+ if(strcmp(at->at_resource, resource)) {
+ pthread_mutex_unlock(&access_ticket_mutex);
+ return -1;
+ }
+
+ pthread_mutex_unlock(&access_ticket_mutex);
+ return 0;
+}
/**
*
@@ -424,6 +558,9 @@ access_init(int createdefault)
const char *s;
TAILQ_INIT(&access_entries);
+ TAILQ_INIT(&access_tickets);
+
+ pthread_mutex_init(&access_ticket_mutex, NULL);
dt = dtable_create(&access_dtc, "accesscontrol", NULL);
diff --git a/src/access.h b/src/access.h
index 834a372c..7af118e2 100644
--- a/src/access.h
+++ b/src/access.h
@@ -42,14 +42,36 @@ typedef struct access_entry {
uint32_t ae_netmask; /* derived from ae_prefixlen */
} access_entry_t;
+TAILQ_HEAD(access_ticket_queue, access_ticket);
+
+extern struct access_ticket_queue access_tickets;
+
+typedef struct access_ticket {
+ char *at_id;
+
+ TAILQ_ENTRY(access_ticket) at_link;
+
+ gtimer_t at_timer;
+ char *at_resource;
+} access_ticket_t;
#define ACCESS_STREAMING 0x1
#define ACCESS_WEB_INTERFACE 0x2
#define ACCESS_RECORDER 0x4
#define ACCESS_ADMIN 0x8
-
#define ACCESS_FULL 0x3f
+/**
+ * Create a new ticket for the requested resource and generate a id for it
+ */
+const char* access_ticket_create(const char *resource);
+
+/**
+ * Verifies that a given ticket id matches a resource
+ */
+int access_ticket_verify(const char *id, const char *resource);
+
+int access_ticket_delete(const char *ticket_id);
/**
* Verifies that the given user in combination with the source ip
* complies with the requested mask
diff --git a/src/http.c b/src/http.c
index ea8a4f98..9cca5036 100644
--- a/src/http.c
+++ b/src/http.c
@@ -308,6 +308,15 @@ http_redirect(http_connection_t *hc, const char *location)
int
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)) {
+ tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
+ inet_ntoa(hc->hc_peer->sin_addr), ticket_id,
+ hc->hc_url);
+ return 0;
+ }
+
return access_verify(hc->hc_username, hc->hc_password,
(struct sockaddr *)hc->hc_peer, mask);
}
diff --git a/src/http.h b/src/http.h
index 8cc497c3..27897dfe 100644
--- a/src/http.h
+++ b/src/http.h
@@ -80,11 +80,6 @@ typedef struct http_connection {
char *hc_username;
char *hc_password;
- int hc_authenticated; /* Used by RTSP, it seems VLC does not
- send authentication headers for each
- command, so we just say that it's ok
- if it has authenticated at least once */
-
struct config_head *hc_user_config;
int hc_no_output;
diff --git a/src/webui/webui.c b/src/webui/webui.c
index c2060b92..13e754f4 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -243,6 +243,9 @@ static void
http_stream_playlist(http_connection_t *hc, channel_t *channel)
{
htsbuf_queue_t *hq = &hc->hc_reply;
+ char buf[255];
+ const char *ticket_id = NULL;
+
channel_t *ch = NULL;
const char *host = http_arg_get(&hc->hc_args, "Host");
@@ -252,7 +255,10 @@ http_stream_playlist(http_connection_t *hc, channel_t *channel)
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
if (channel == NULL || ch == channel) {
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name);
- htsbuf_qprintf(hq, "http://%s/stream/channelid/%d\n", host, ch->ch_id);
+
+ snprintf(buf, sizeof(buf), "/stream/channelid/%d", ch->ch_id);
+ ticket_id = access_ticket_create(buf);
+ htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, ticket_id);
}
}
@@ -385,11 +391,6 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
channel_t *ch = NULL;
service_t *service = NULL;
- if(http_access_verify(hc, ACCESS_STREAMING)) {
- http_error(hc, HTTP_STATUS_UNAUTHORIZED);
- return HTTP_STATUS_UNAUTHORIZED;
- }
-
hc->hc_keep_alive = 0;
if(remain == NULL) {
From 33abd5d885fa19492293dc2227c157152989cb8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Wed, 2 Feb 2011 09:51:07 +0100
Subject: [PATCH 29/72] Don't check for authentication twice
---
src/webui/webui.c | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 13e754f4..0b500ce2 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -276,13 +276,6 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
char *components[2];
channel_t *ch = NULL;
- if(http_access_verify(hc, ACCESS_STREAMING)) {
- http_error(hc, HTTP_STATUS_UNAUTHORIZED);
- return HTTP_STATUS_UNAUTHORIZED;
- }
-
- hc->hc_keep_alive = 0;
-
if(remain == NULL) {
http_stream_playlist(hc, NULL);
return 0;
@@ -482,11 +475,6 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
pthread_mutex_lock(&global_lock);
- if(http_access_verify(hc, ACCESS_RECORDER)) {
- pthread_mutex_unlock(&global_lock);
- return HTTP_STATUS_UNAUTHORIZED;
- }
-
de = dvr_entry_find_by_id(atoi(remain));
if(de == NULL || de->de_filename == NULL) {
pthread_mutex_unlock(&global_lock);
From 3ca26c10edd7056fca166ad06db09020dd95ee2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Wed, 2 Feb 2011 18:48:14 +0100
Subject: [PATCH 30/72] removed mutex from ticket, it's reduntant
---
src/access.c | 26 +++-----------------------
1 file changed, 3 insertions(+), 23 deletions(-)
diff --git a/src/access.c b/src/access.c
index 696822f2..6307196c 100644
--- a/src/access.c
+++ b/src/access.c
@@ -42,8 +42,6 @@ struct access_ticket_queue access_tickets;
const char *superuser_username;
const char *superuser_password;
-static pthread_mutex_t access_ticket_mutex;
-
/**
*
*/
@@ -81,9 +79,7 @@ access_ticket_timout(void *aux)
{
access_ticket_t *at = aux;
- pthread_mutex_lock(&access_ticket_mutex);
access_ticket_destroy(at);
- pthread_mutex_unlock(&access_ticket_mutex);
}
/**
@@ -100,7 +96,6 @@ access_ticket_create(const char *resource)
static const char hex_string[16] = "0123456789ABCDEF";
- pthread_mutex_lock(&access_ticket_mutex);
at = calloc(1, sizeof(access_ticket_t));
rnd = time(NULL);
@@ -126,8 +121,6 @@ access_ticket_create(const char *resource)
TAILQ_INSERT_TAIL(&access_tickets, at, at_link);
gtimer_arm(&at->at_timer, access_ticket_timout, at, 60*5);
- pthread_mutex_unlock(&access_ticket_mutex);
-
return at->at_id;
}
@@ -138,17 +131,13 @@ int
access_ticket_delete(const char *id)
{
access_ticket_t *at;
- pthread_mutex_lock(&access_ticket_mutex);
- if((at = access_ticket_find(id)) == NULL) {
- pthread_mutex_unlock(&access_ticket_mutex);
+ if((at = access_ticket_find(id)) == NULL)
return -1;
- }
gtimer_disarm(&at->at_timer);
access_ticket_destroy(at);
- pthread_mutex_unlock(&access_ticket_mutex);
return 0;
}
@@ -160,19 +149,12 @@ access_ticket_verify(const char *id, const char *resource)
{
access_ticket_t *at;
- pthread_mutex_lock(&access_ticket_mutex);
-
- if((at = access_ticket_find(id)) == NULL) {
- pthread_mutex_unlock(&access_ticket_mutex);
+ if((at = access_ticket_find(id)) == NULL)
return -1;
- }
- if(strcmp(at->at_resource, resource)) {
- pthread_mutex_unlock(&access_ticket_mutex);
+ if(strcmp(at->at_resource, resource))
return -1;
- }
- pthread_mutex_unlock(&access_ticket_mutex);
return 0;
}
@@ -560,8 +542,6 @@ access_init(int createdefault)
TAILQ_INIT(&access_entries);
TAILQ_INIT(&access_tickets);
- pthread_mutex_init(&access_ticket_mutex, NULL);
-
dt = dtable_create(&access_dtc, "accesscontrol", NULL);
if(dtable_load(dt) == 0 && createdefault) {
From e4a0f14081928fb6bc98fbfaa25b9beb4f906b3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Wed, 2 Feb 2011 19:11:37 +0100
Subject: [PATCH 31/72] Use OpenSSL to generate a random number, it is probobly
better at it.
---
src/access.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/access.c b/src/access.c
index 6307196c..080d6d84 100644
--- a/src/access.c
+++ b/src/access.c
@@ -30,6 +30,7 @@
#include
#include
+#include
#include "tvheadend.h"
#include "access.h"
@@ -90,23 +91,13 @@ access_ticket_create(const char *resource)
{
uint8_t buf[20];
char id[41];
- unsigned int i, rnd;
+ unsigned int i;
access_ticket_t *at;
- SHA_CTX shactx;
-
static const char hex_string[16] = "0123456789ABCDEF";
-
at = calloc(1, sizeof(access_ticket_t));
- rnd = time(NULL);
-
- //Generate a pseudo-random ticket id
- SHA_Init(&shactx);
- for(i=0; i<5; i++) {
- SHA_Update(&shactx, (const uint8_t *)&rnd, sizeof(int));
- rnd = rand_r(&rnd);
- }
- SHA_Final(buf, &shactx);
+
+ RAND_bytes(buf, 20);
//convert to hexstring
for(i=0; i
Date: Wed, 2 Feb 2011 19:53:33 +0100
Subject: [PATCH 32/72] Fix the VLC plugin-detection. The HTML embed element
have to be layed in order for the plugin to kick in.
---
src/webui/static/app/tvheadend.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index 2cd955fa..b0a78647 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -69,6 +69,8 @@ tvheadend.VLC = function(url) {
chName = 'the stream';
}
+ vlc.style.display = 'block';
+
if(!vlc.playlist || vlc.playlist == 'undefined') {
var innerHTML = '';
innerHTML += 'You are missing a plugin for your browser.'
@@ -78,12 +80,11 @@ tvheadend.VLC = function(url) {
innerHTML += '
Direct URL
';
missingPlugin.innerHTML = innerHTML;
+ vlc.style.display = 'none';
missingPlugin.style.display = 'block';
return;
}
- vlc.style.display = 'block';
-
if(vlc.playlist && vlc.playlist.isPlaying) {
vlc.playlist.stop();
}
From eb6ae500178c521afb780608c694eae39ece2961 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 3 Feb 2011 17:45:59 +0100
Subject: [PATCH 33/72] Use the playlist url for the VLC player
---
src/webui/static/app/chconf.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/webui/static/app/chconf.js b/src/webui/static/app/chconf.js
index c2f10f5c..6bbc35bc 100644
--- a/src/webui/static/app/chconf.js
+++ b/src/webui/static/app/chconf.js
@@ -163,7 +163,7 @@ tvheadend.chconf = function()
dataIndex: 'chid',
width: 50,
renderer: function(value, metadata, record, row, col, store) {
- url = 'stream/channelid/' + value
+ url = 'playlist/channelid/' + value
return "Play"
}
},
From cae47cfd31903a6aab3bd433a49844e942d3d313 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20T=C3=B6rnblom?=
Date: Thu, 3 Feb 2011 17:47:50 +0100
Subject: [PATCH 34/72] Added playlist support for dvr files
---
src/webui/static/app/dvr.js | 2 +-
src/webui/webui.c | 69 +++++++++++++++++++++++++++++++++----
2 files changed, 64 insertions(+), 7 deletions(-)
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index 10fed19a..26c578d7 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -69,7 +69,7 @@ tvheadend.dvrDetails = function(entry) {
content += '';
}
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 0b500ce2..723d5676 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -237,9 +237,9 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq)
}
/**
- * Output playlist with http streams (.m3u format)
+ * Output a playlist with http streams for a channel (.m3u format)
*/
-static void
+static int
http_stream_playlist(http_connection_t *hc, channel_t *channel)
{
htsbuf_queue_t *hq = &hc->hc_reply;
@@ -265,6 +265,59 @@ http_stream_playlist(http_connection_t *hc, channel_t *channel)
http_output_content(hc, "application/x-mpegURL");
pthread_mutex_unlock(&global_lock);
+
+ return 0;
+}
+
+/**
+ * Output a playlist with a http stream for a dvr entry (.m3u format)
+ */
+static int
+http_dvr_playlist(http_connection_t *hc, int dvr_id)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ char buf[255];
+ const char *ticket_id = NULL;
+ dvr_entry_t *de = NULL;
+ time_t durration = 0;
+ off_t fsize = 0;
+ int bandwidth = 0;
+ const char *host = http_arg_get(&hc->hc_args, "Host");
+
+ pthread_mutex_lock(&global_lock);
+
+ de = dvr_entry_find_by_id(dvr_id);
+
+ if(de) {
+ durration = de->de_stop - de->de_start;
+ durration += (de->de_stop_extra + de->de_start_extra)*60;
+
+ fsize = dvr_get_filesize(de);
+
+ if(fsize) {
+ bandwidth = ((8*fsize) / (durration*1024.0));
+
+ htsbuf_qprintf(hq, "#EXTM3U\n");
+ htsbuf_qprintf(hq, "#EXTINF:%d,%s\n", durration, de->de_title);
+
+ htsbuf_qprintf(hq, "#EXT-X-TARGETDURATION:%d\n", durration);
+ htsbuf_qprintf(hq, "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%d\n", bandwidth);
+
+ snprintf(buf, sizeof(buf), "/dvrfile/%d", dvr_id);
+ ticket_id = access_ticket_create(buf);
+ htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, ticket_id);
+
+ http_output_content(hc, "application/x-mpegURL");
+ }
+ }
+ pthread_mutex_unlock(&global_lock);
+
+ if(!de || !fsize) {
+ http_error(hc, HTTP_STATUS_BAD_REQUEST);
+ return HTTP_STATUS_BAD_REQUEST;
+ } else {
+ return 0;
+ }
}
/**
@@ -275,6 +328,7 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
{
char *components[2];
channel_t *ch = NULL;
+ int dvr_id = -1;
if(remain == NULL) {
http_stream_playlist(hc, NULL);
@@ -294,17 +348,20 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
ch = channel_find_by_identifier(atoi(components[1]));
} else if(!strcmp(components[0], "channel")) {
ch = channel_find_by_name(components[1], 0, 0);
+ } else if(!strcmp(components[0], "dvrid")) {
+ dvr_id = atoi(components[1]);
}
pthread_mutex_unlock(&global_lock);
- if(ch == NULL) {
+ if(ch)
+ return http_stream_playlist(hc, ch);
+ else if(dvr_id >= 0)
+ return http_dvr_playlist(hc, dvr_id);
+ else {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return HTTP_STATUS_BAD_REQUEST;
}
-
- http_stream_playlist(hc, ch);
- return 0;
}
/**
From 5d3c14e070ca182ca3457df6340b96290f226dff Mon Sep 17 00:00:00 2001
From: jouk
Date: Fri, 18 Feb 2011 19:39:13 +0100
Subject: [PATCH 35/72] Added contrib directory with redhat specfile and init
script.
---
contrib/redhat/tvheadend | 128 ++++++++++++++++++++++++++++++++++
contrib/redhat/tvheadend.spec | 75 ++++++++++++++++++++
2 files changed, 203 insertions(+)
create mode 100755 contrib/redhat/tvheadend
create mode 100644 contrib/redhat/tvheadend.spec
diff --git a/contrib/redhat/tvheadend b/contrib/redhat/tvheadend
new file mode 100755
index 00000000..fa4b9100
--- /dev/null
+++ b/contrib/redhat/tvheadend
@@ -0,0 +1,128 @@
+#!/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
new file mode 100644
index 00000000..a15bf85c
--- /dev/null
+++ b/contrib/redhat/tvheadend.spec
@@ -0,0 +1,75 @@
+Name: tvheadend
+Summary: TV streaming server
+Version: 2.12.cae47cf
+Release: 1%{dist}
+License: GPL
+Group: Applications/Multimedia
+URL: http://www.lonelycoder.com/hts/tvheadend_overview.html
+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 adac4f636a7d99d0b83c1f3b0a730b50f98649e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Fri, 18 Feb 2011 19:51:50 +0100
Subject: [PATCH 36/72] Use correct URL
---
contrib/redhat/tvheadend.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contrib/redhat/tvheadend.spec b/contrib/redhat/tvheadend.spec
index a15bf85c..dd2fa8c4 100644
--- a/contrib/redhat/tvheadend.spec
+++ b/contrib/redhat/tvheadend.spec
@@ -4,7 +4,7 @@ Version: 2.12.cae47cf
Release: 1%{dist}
License: GPL
Group: Applications/Multimedia
-URL: http://www.lonelycoder.com/hts/tvheadend_overview.html
+URL: http://www.lonelycoder.com/tvheadend
Packager: Jouk Hettema
Source: tvheadend-%{version}.tar.bz2
Prefix: /usr
From 15679510e196a44234b3929ca8420b4e5e011b27 Mon Sep 17 00:00:00 2001
From: Thies Schroeder
Date: Fri, 18 Feb 2011 22:11:24 +0100
Subject: [PATCH 37/72] add functionality to delete recordings
---
src/dvr/dvr.h | 2 ++
src/dvr/dvr_db.c | 15 +++++++++++++++
src/webui/extjs.c | 13 +++++++++++++
src/webui/static/app/dvr.js | 23 +++++++++++++++++++++++
4 files changed, 53 insertions(+)
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index 888fc986..f31249ff 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -274,6 +274,8 @@ void dvr_extra_time_pre_set(dvr_config_t *cfg, int d);
void dvr_extra_time_post_set(dvr_config_t *cfg, int d);
+int dvr_entry_delete(dvr_entry_t *de);
+
/**
* Query interface
*/
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index c7933c62..254c7745 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -1160,3 +1160,18 @@ dvr_val2pri(dvr_prio_t v)
{
return val2str(v, priotab) ?: "invalid";
}
+
+int
+dvr_entry_delete(dvr_entry_t *de)
+{
+ int result;
+ tvhlog(LOG_DEBUG, "dvr_db", "Going to delete recording '%s'", de->de_filename);
+ result = unlink(de->de_filename);
+ if( result == 0 || result == ENOENT )
+ {
+ dvr_entry_remove(de);
+ }
+ return result;
+
+}
+
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index e6111064..09349a05 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -804,6 +804,19 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
+
+ } else if(!strcmp(op, "deleteEntry")) {
+ s = http_arg_get(&hc->hc_req_args, "entryId");
+
+ if((de = dvr_entry_find_by_id(atoi(s))) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return HTTP_STATUS_BAD_REQUEST;
+ }
+
+ dvr_entry_delete(de);
+
+ out = htsmsg_create_map();
+ htsmsg_add_u32(out, "success", 1);
} else if(!strcmp(op, "createEntry")) {
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index 26c578d7..7439c7a8 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -98,6 +98,14 @@ tvheadend.dvrDetails = function(entry) {
text: "Abort recording"
});
break;
+ case 'completedError':
+ case 'completed':
+ win.addButton({
+ handler: deleteEvent,
+ text: "Delete recording"
+
+ });
+ break;
}
@@ -119,6 +127,21 @@ tvheadend.dvrDetails = function(entry) {
}
});
}
+
+ function deleteEvent() {
+ Ext.Ajax.request({
+ url: 'dvr',
+ params: {entryId: entry.id, op: 'deleteEntry'},
+
+ success:function(response, options) {
+ win.close();v
+ },
+
+ failure:function(response, options) {
+ Ext.MessageBox.alert('DVR', response.statusText);
+ }
+ });
+ }
}
From c586071546b101919e2e60352eabb1d34a33e34a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sat, 19 Feb 2011 12:33:40 +0100
Subject: [PATCH 38/72] Minor cleanup of
15679510e196a44234b3929ca8420b4e5e011b27
---
src/dvr/dvr_db.c | 14 +++++++-------
src/webui/extjs.c | 8 ++++----
src/webui/static/app/dvr.js | 3 +--
3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 254c7745..365d8f14 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -1164,14 +1164,14 @@ dvr_val2pri(dvr_prio_t v)
int
dvr_entry_delete(dvr_entry_t *de)
{
- int result;
- tvhlog(LOG_DEBUG, "dvr_db", "Going to delete recording '%s'", de->de_filename);
- result = unlink(de->de_filename);
- if( result == 0 || result == ENOENT )
- {
+ if(!unlink(de->de_filename) || errno == ENOENT) {
+ tvhlog(LOG_DEBUG, "dvr", "Delete recording '%s'", de->de_filename);
dvr_entry_remove(de);
+ return 0;
+ } else {
+ tvhlog(LOG_WARNING, "dvr", "Unable to delete recording '%s' -- %s",
+ de->de_filename, strerror(errno));
+ return -1;
}
- return result;
-
}
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index 09349a05..7468dea2 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -764,7 +764,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
event_t *e;
dvr_entry_t *de;
const char *s;
- int flags = 0;
+ int flags = 0, retval;
dvr_config_t *cfg;
if(op == NULL)
@@ -804,7 +804,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
-
+
} else if(!strcmp(op, "deleteEntry")) {
s = http_arg_get(&hc->hc_req_args, "entryId");
@@ -813,10 +813,10 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
return HTTP_STATUS_BAD_REQUEST;
}
- dvr_entry_delete(de);
+ retval = dvr_entry_delete(de);
out = htsmsg_create_map();
- htsmsg_add_u32(out, "success", 1);
+ htsmsg_add_u32(out, "success", !retval);
} else if(!strcmp(op, "createEntry")) {
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index 7439c7a8..a2129b98 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -103,7 +103,6 @@ tvheadend.dvrDetails = function(entry) {
win.addButton({
handler: deleteEvent,
text: "Delete recording"
-
});
break;
}
@@ -127,7 +126,7 @@ tvheadend.dvrDetails = function(entry) {
}
});
}
-
+
function deleteEvent() {
Ext.Ajax.request({
url: 'dvr',
From c021dad967a1750c430ec63a1c17199effa454af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sat, 19 Feb 2011 12:59:36 +0100
Subject: [PATCH 39/72] Rename debian package hts-tvheadend -> tvheadend Fix
the rules file to be more standardized (thanks Lars Op den Kamp) Ticket #316
---
debian/changelog | 33 ++++++++++++++
debian/control | 12 +++---
debian/rules | 43 ++++++++-----------
...{hts-tvheadend.config => tvheadend.config} | 0
debian/{hts-tvheadend.docs => tvheadend.docs} | 0
...-tvheadend.postinst => tvheadend.postinst} | 0
...{hts-tvheadend.postrm => tvheadend.postrm} | 0
...vheadend.templates => tvheadend.templates} | 0
...vheadend.init => tvheadend.tvheadend.init} | 0
9 files changed, 58 insertions(+), 30 deletions(-)
rename debian/{hts-tvheadend.config => tvheadend.config} (100%)
rename debian/{hts-tvheadend.docs => tvheadend.docs} (100%)
rename debian/{hts-tvheadend.postinst => tvheadend.postinst} (100%)
rename debian/{hts-tvheadend.postrm => tvheadend.postrm} (100%)
rename debian/{hts-tvheadend.templates => tvheadend.templates} (100%)
rename debian/{hts-tvheadend.tvheadend.init => tvheadend.tvheadend.init} (100%)
diff --git a/debian/changelog b/debian/changelog
index b8283b76..86bc0ec1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,36 @@
+tvheadend (2.12.99) hts; urgency=low
+
+ * Debian package has been renamed from hts-tvheadend to tvheadend
+
+ * Add functionality to delete recordings
+
+ * Better support for playing in web clients.
+ Temporary tickets are used instead of username/password authentication
+
+ * Remove lock contention in CWC updates
+
+ * Fix bug in IPTV PAT parser (Ticket #318)
+
+ * Store EPG on disk so it can be reloaded on restart
+
+ * Add support for Viaccess EMM
+
+ * Add support for DRECrypt EMM
+
+ * Depend on OpenSSL for cryptographic features
+
+ * RTSP has been dropped. It was too buggy and noone wanted to maintain it
+
+ * Fix bug in JSON encoder. Ticket #163
+
+ * Fix crash in HTTP service. Ticket #334
+
+ * Added http-streaming of services. Tested and working with mplayer.
+
+ * Add support for building RPM packages
+
+ -- Andreas Öman Sat, 19 Feb 2011 12:57:09 +0100
+
hts-tvheadend (2.12) hts; urgency=low
* Add support for IPTV over IPv6
diff --git a/debian/control b/debian/control
index ee7e9234..c9e5cc54 100644
--- a/debian/control
+++ b/debian/control
@@ -1,15 +1,17 @@
-Source: hts-tvheadend
+Source: tvheadend
Section: main
Priority: extra
Maintainer: Andreas Öman
Build-Depends: debhelper (>= 5)
Standards-Version: 3.7.3
-Package: hts-tvheadend
+Package: tvheadend
Architecture: any
Depends: ${shlibs:Depends}, libavahi-client3
Recommends: xmltv
-Enhances: hts-showtime
+Enhances: showtime
+Replaces: hts-tvheadend
+Homepage: http://www.lonelycoder.com/tvheadend
Description: HTS Tvheadend
- TV backend for use with hts-showtime and various other clients.
- Based on ffmpeg 'http://www.ffmpeg.org/' and ExtJS 'http://www.extjs.org/'
+ TV backend for use with Showtime, XBMC and various other clients.
+ Uses ExtJS 'http://www.extjs.org/'
diff --git a/debian/rules b/debian/rules
index ac4a519a..7f2b1a1d 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,38 +1,31 @@
#!/usr/bin/make -f
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-config.mak: configure
- dh_testdir
- ./configure --release --prefix=/usr
+%:
+ dh $@
-clean:
+override_dh_clean:
dh_testdir
dh_testroot
rm -rf build.*
- dh_clean
+ dh_clean
+override_dh_auto_clean:
+ dh_clean
-build: config.mak
+override_dh_auto_configure:
+ dh_testdir
+ ./configure --release --prefix=/usr
+
+override_dh_auto_build:
$(MAKE)
-binary:
- dh_testdir
- dh_testroot
- dh_installdirs
- dh_install
+override_dh_install:
$(MAKE) prefix=$(CURDIR)/debian/hts-tvheadend/usr install
- dh_installchangelogs
- dh_installinit --name tvheadend
- dh_installdocs
- dh_installdebconf
- dh_link
- dh_strip
- dh_compress
- dh_fixperms
- dh_makeshlibs
- dh_installdeb
- dh_shlibdeps
- dh_gencontrol
- dh_md5sums
- dh_builddeb
+
+override_dh_installinit:
+ dh_installinit --name tvheadend
diff --git a/debian/hts-tvheadend.config b/debian/tvheadend.config
similarity index 100%
rename from debian/hts-tvheadend.config
rename to debian/tvheadend.config
diff --git a/debian/hts-tvheadend.docs b/debian/tvheadend.docs
similarity index 100%
rename from debian/hts-tvheadend.docs
rename to debian/tvheadend.docs
diff --git a/debian/hts-tvheadend.postinst b/debian/tvheadend.postinst
similarity index 100%
rename from debian/hts-tvheadend.postinst
rename to debian/tvheadend.postinst
diff --git a/debian/hts-tvheadend.postrm b/debian/tvheadend.postrm
similarity index 100%
rename from debian/hts-tvheadend.postrm
rename to debian/tvheadend.postrm
diff --git a/debian/hts-tvheadend.templates b/debian/tvheadend.templates
similarity index 100%
rename from debian/hts-tvheadend.templates
rename to debian/tvheadend.templates
diff --git a/debian/hts-tvheadend.tvheadend.init b/debian/tvheadend.tvheadend.init
similarity index 100%
rename from debian/hts-tvheadend.tvheadend.init
rename to debian/tvheadend.tvheadend.init
From d7e4dc376b2ddd087e0a31ccfcc1313b17437bdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Sat, 19 Feb 2011 13:07:28 +0100
Subject: [PATCH 40/72] Build debug package
---
debian/control | 12 ++++++++++--
debian/rules | 3 +++
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/debian/control b/debian/control
index c9e5cc54..28dc1e97 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: tvheadend
Section: main
Priority: extra
Maintainer: Andreas Öman
-Build-Depends: debhelper (>= 5)
+Build-Depends: debhelper (>= 7.0.50)
Standards-Version: 3.7.3
Package: tvheadend
@@ -12,6 +12,14 @@ Recommends: xmltv
Enhances: showtime
Replaces: hts-tvheadend
Homepage: http://www.lonelycoder.com/tvheadend
-Description: HTS Tvheadend
+Description: Tvheadend
TV backend for use with Showtime, XBMC and various other clients.
Uses ExtJS 'http://www.extjs.org/'
+
+Package: tvheadend-dbg
+Architecture: any
+Section: debug
+Priority: extra
+Depends: tvheadend (= ${binary:Version}), ${misc:Depends}
+Description: Debug symbols for Tvheadend
+ This package contains the debugging symbols for Tvheadend.
diff --git a/debian/rules b/debian/rules
index 7f2b1a1d..08bab07f 100755
--- a/debian/rules
+++ b/debian/rules
@@ -29,3 +29,6 @@ override_dh_install:
override_dh_installinit:
dh_installinit --name tvheadend
+
+override_dh_strip:
+ dh_strip --dbg-package=tvheadend-dbg
From a629798b62575d79b5a62429faae475c1eed70b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Mon, 21 Feb 2011 22:40:10 +0100
Subject: [PATCH 41/72] Better backtraces thanks to addr2link
---
src/trap.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/src/trap.c b/src/trap.c
index 0184f1f8..1e92497d 100644
--- a/src/trap.c
+++ b/src/trap.c
@@ -30,6 +30,7 @@ char tvh_binshasum[20];
#include
#include
#include
+#include
#if ENABLE_EXECINFO
#include
#endif
@@ -49,6 +50,7 @@ char tvh_binshasum[20];
static char line1[200];
static char tmpbuf[1024];
static char libs[1024];
+static char self[PATH_MAX];
static void
sappend(char *buf, size_t l, const char *fmt, ...)
@@ -62,10 +64,76 @@ sappend(char *buf, size_t l, const char *fmt, ...)
+/**
+ *
+ */
+static int
+add2lineresolve(const char *binary, void *addr, char *buf0, size_t buflen)
+{
+ char *buf = buf0;
+ int fd[2], r, f;
+ const char *argv[5];
+ pid_t p;
+ char addrstr[30], *cp;
+
+ argv[0] = "addr2line";
+ argv[1] = "-e";
+ argv[2] = binary;
+ argv[3] = addrstr;
+ argv[4] = NULL;
+
+ snprintf(addrstr, sizeof(addrstr), "%p", (void *)((intptr_t)addr-1));
+
+ if(pipe(fd) == -1)
+ return -1;
+
+ if((p = fork()) == -1)
+ return -1;
+
+ if(p == 0) {
+ close(0);
+ close(2);
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ if((f = open("/dev/null", O_RDWR)) == -1)
+ exit(1);
+
+ dup2(f, 0);
+ dup2(f, 2);
+ close(f);
+
+ execve("/usr/bin/addr2line", (char *const *) argv, environ);
+ exit(2);
+ }
+
+ close(fd[1]);
+ *buf = 0;
+ while(buflen > 1) {
+ r = read(fd[0], buf, buflen);
+ if(r < 1)
+ break;
+
+ buf += r;
+ buflen -= r;
+ *buf = 0;
+ cp = strchr(buf0, '\n');
+ if(cp != NULL) {
+ *cp = 0;
+ break;
+ }
+ }
+ close(fd[0]);
+ return 0;
+}
+
+
+
static void
traphandler(int sig, siginfo_t *si, void *UC)
{
ucontext_t *uc = UC;
+ char buf[200];
#if ENABLE_EXECINFO
static void *frames[MAXFRAMES];
int nframes = backtrace(frames, MAXFRAMES);
@@ -122,6 +190,11 @@ traphandler(int sig, siginfo_t *si, void *UC)
continue;
}
+ if(self[0] && !add2lineresolve(self, frames[i], buf, sizeof(buf))) {
+ tvhlog_spawn(LOG_ALERT, "CRASH", "%s %p", buf, frames[i]);
+ continue;
+ }
+
if(dli.dli_fname != NULL && dli.dli_fbase != NULL) {
tvhlog_spawn(LOG_ALERT, "CRASH", "%s %p",
dli.dli_fname,
@@ -153,6 +226,7 @@ extern const char *htsversion_full;
void
trap_init(const char *ver)
{
+ int r;
uint8_t digest[20];
struct sigaction sa, old;
char path[256];
@@ -160,6 +234,11 @@ trap_init(const char *ver)
SHA_CTX binsum;
int fd;
+ r = readlink("/proc/self/exe", self, sizeof(self) - 1);
+ if(r == -1)
+ self[0] = 0;
+ else
+ self[r] = 0;
if((fd = open("/proc/self/exe", O_RDONLY)) != -1) {
struct stat st;
From eee6e1c45b8cb343c36f948827a3e8d84d1701b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 22 Feb 2011 09:15:43 +0100
Subject: [PATCH 42/72] Fix fallout from hts-tvheadend => tvheadend rename
---
debian/rules | 2 +-
debian/tvheadend.config | 6 +++---
debian/tvheadend.postinst | 4 ++--
debian/tvheadend.templates | 6 +++---
debian/tvheadend.tvheadend.init | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/debian/rules b/debian/rules
index 08bab07f..76591cd5 100755
--- a/debian/rules
+++ b/debian/rules
@@ -25,7 +25,7 @@ override_dh_auto_build:
$(MAKE)
override_dh_install:
- $(MAKE) prefix=$(CURDIR)/debian/hts-tvheadend/usr install
+ $(MAKE) prefix=$(CURDIR)/debian/tvheadend/usr install
override_dh_installinit:
dh_installinit --name tvheadend
diff --git a/debian/tvheadend.config b/debian/tvheadend.config
index cffd8a59..50da1916 100644
--- a/debian/tvheadend.config
+++ b/debian/tvheadend.config
@@ -12,17 +12,17 @@ while [ "$STATE" != 0 -a "$STATE" != 4 ]; do
case "$STATE" in
1)
# Ask for username
- db_input high hts-tvheadend/admin_username || true
+ db_input high tvheadend/admin_username || true
;;
2)
# Ask for password
- db_input high hts-tvheadend/admin_password || true
+ db_input high tvheadend/admin_password || true
;;
3)
# Display a final note
- db_input high hts-tvheadend/webinterface || true
+ db_input high tvheadend/webinterface || true
;;
esac
diff --git a/debian/tvheadend.postinst b/debian/tvheadend.postinst
index 22692d09..1d5e60cc 100644
--- a/debian/tvheadend.postinst
+++ b/debian/tvheadend.postinst
@@ -27,11 +27,11 @@ configure)
echo >>"${HTS_SUPERUSERCONF}" "{"
- if db_get hts-tvheadend/admin_username; then
+ if db_get tvheadend/admin_username; then
echo >>"${HTS_SUPERUSERCONF}" '"username": "'$RET'",'
fi
- if db_get hts-tvheadend/admin_password; then
+ if db_get tvheadend/admin_password; then
echo >>"${HTS_SUPERUSERCONF}" '"password": "'$RET'"'
fi
diff --git a/debian/tvheadend.templates b/debian/tvheadend.templates
index 79ffea26..2e5a650c 100644
--- a/debian/tvheadend.templates
+++ b/debian/tvheadend.templates
@@ -1,4 +1,4 @@
-Template: hts-tvheadend/admin_username
+Template: tvheadend/admin_username
Type: string
Description: Choose a username for Tvheadend administrator.
Tvheadend is accessed via a world reachable web interface (assuming your
@@ -8,10 +8,10 @@ Description: Choose a username for Tvheadend administrator.
If you want to change the superuser account you need to reconfigure the
Tvheadend package.
-Template: hts-tvheadend/admin_password
+Template: tvheadend/admin_password
Type: password
Description: Administrator password.
-Template: hts-tvheadend/webinterface
+Template: tvheadend/webinterface
Type: note
Description: After installation Tvheadend can be accessed via HTTP on port 9981. From this machine you can point your web-browser to http://localhost:9981/
diff --git a/debian/tvheadend.tvheadend.init b/debian/tvheadend.tvheadend.init
index a98f50d2..2e424b97 100644
--- a/debian/tvheadend.tvheadend.init
+++ b/debian/tvheadend.tvheadend.init
@@ -1,6 +1,6 @@
#! /bin/sh
### BEGIN INIT INFO
-# Provides: hts-tvheadend
+# Provides: tvheadend
# Required-Start: $local_fs $remote_fs udev
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
From c365f235cc997bd3df5f94939aa0fce8e7bea4f8 Mon Sep 17 00:00:00 2001
From: "ton.van.der.poel"
Date: Wed, 23 Feb 2011 21:42:57 +0100
Subject: [PATCH 43/72] DVR - Bugfix - Duplicate recordings
In some situations (I suspend my main pvr machine, and use a script to wake it up before
recordings, this means I stop and start tvheadend multiple times a day), tvheadend starts
parsing EPG-data agains AutoRec rules before previously scheduled recordings are loaded
from disk.
Since the check for duplicate recordings is not present in the code that loads previously
scheduled recordings from disk, it would mean that when an autorec-rule was triggered, this
would result in duplicate scheduled recordings.
By first restoring previously scheduled recordings from disk and then loading the AutoRec
rules, this situation can not happen anymore...
---
src/dvr/dvr_db.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 365d8f14..aa59112f 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -834,9 +834,9 @@ dvr_init(void)
}
}
- dvr_autorec_init();
-
dvr_db_load();
+
+ dvr_autorec_init();
}
/**
From 98975cf4b39d2329ed4a34751115257f39661519 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 1 Mar 2011 18:16:08 +0100
Subject: [PATCH 44/72] Skip over extensions in RTP header. Ticket #388
---
src/iptv_input.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/iptv_input.c b/src/iptv_input.c
index 8a4b2bd7..44938edc 100644
--- a/src/iptv_input.c
+++ b/src/iptv_input.c
@@ -124,7 +124,7 @@ iptv_ts_input(service_t *t, const uint8_t *tsb)
static void *
iptv_thread(void *aux)
{
- int nfds, fd, r, j;
+ int nfds, fd, r, j, hlen;
uint8_t tsb[65536], *buf;
struct epoll_event ev;
service_t *t;
@@ -158,7 +158,17 @@ iptv_thread(void *aux)
if((tsb[1] & 0x7f) != 33)
continue;
- int hlen = (tsb[0] & 0xf) * 4 + 12;
+ hlen = (tsb[0] & 0xf) * 4 + 12;
+
+ if(tsb[0] & 0x10) {
+ // Extension (X bit) == true
+
+ if(r < hlen + 4)
+ continue; // Packet size < hlen + extension header
+
+ // Skip over extension header (last 2 bytes of header is length)
+ hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4;
+ }
if(r < hlen || (r - hlen) % 188 != 0)
continue;
From 2ea7764431b5c81a3885d5be1fa48b92ef36fc53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 2 Mar 2011 23:32:45 +0100
Subject: [PATCH 45/72] Fix a couple of bugs related to sending files > 2GB
Ticket #391
---
src/http.c | 8 +++++---
src/http.h | 3 ++-
src/webui/webui.c | 27 ++++++++++++++++++---------
3 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/src/http.c b/src/http.c
index 9cca5036..8d94c8bc 100644
--- a/src/http.c
+++ b/src/http.c
@@ -123,7 +123,8 @@ static const char *
http_rc2str(int code)
{
switch(code) {
- case HTTP_STATUS_OK: return "Ok";
+ case HTTP_STATUS_OK: return "OK";
+ case HTTP_STATUS_PARTIAL_CONTENT: return "Partial Content";
case HTTP_STATUS_NOT_FOUND: return "Not found";
case HTTP_STATUS_UNAUTHORIZED: return "Unauthorized";
case HTTP_STATUS_BAD_REQUEST: return "Bad request";
@@ -149,7 +150,8 @@ static const char *cachemonths[12] = {
*/
void
http_send_header(http_connection_t *hc, int rc, const char *content,
- int contentlen, const char *encoding, const char *location,
+ int64_t contentlen,
+ const char *encoding, const char *location,
int maxage, const char *range)
{
struct tm tm0, *tm;
@@ -204,7 +206,7 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
htsbuf_qprintf(&hdrs, "Content-Type: %s\r\n", content);
if(contentlen > 0)
- htsbuf_qprintf(&hdrs, "Content-Length: %d\r\n", contentlen);
+ htsbuf_qprintf(&hdrs, "Content-Length: %"PRId64"\r\n", contentlen);
if(range) {
htsbuf_qprintf(&hdrs, "Accept-Ranges: %s\r\n", "bytes");
diff --git a/src/http.h b/src/http.h
index 27897dfe..86adf1b0 100644
--- a/src/http.h
+++ b/src/http.h
@@ -30,6 +30,7 @@ typedef struct http_arg {
} http_arg_t;
#define HTTP_STATUS_OK 200
+#define HTTP_STATUS_PARTIAL_CONTENT 206
#define HTTP_STATUS_FOUND 302
#define HTTP_STATUS_BAD_REQUEST 400
#define HTTP_STATUS_UNAUTHORIZED 401
@@ -111,7 +112,7 @@ void http_output_content(http_connection_t *hc, const char *content);
void http_redirect(http_connection_t *hc, const char *location);
void http_send_header(http_connection_t *hc, int rc, const char *content,
- int contentlen, const char *encoding,
+ int64_t contentlen, const char *encoding,
const char *location, int maxage, const char *range);
typedef int (http_callback_t)(http_connection_t *hc,
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 723d5676..6c721ac6 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -525,7 +525,8 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
dvr_entry_t *de;
char *fname;
char range_buf[255];
- off_t content_len, file_start, file_end;
+ off_t content_len, file_start, file_end, chunk;
+ ssize_t r;
if(remain == NULL)
return 404;
@@ -577,19 +578,27 @@ 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);
+ sprintf(range_buf, "bytes %"PRId64"-%"PRId64"/%"PRId64"",
+ file_start, file_end, st.st_size);
if(file_start > 0)
lseek(fd, file_start, SEEK_SET);
- http_send_header(hc, 200, content, content_len, NULL, NULL, 10, range_buf);
- sendfile(hc->hc_fd, fd, NULL, content_len);
- close(fd);
+ http_send_header(hc, range ? HTTP_STATUS_PARTIAL_CONTENT : HTTP_STATUS_OK,
+ content, content_len, NULL, NULL, 10,
+ range ? range_buf : NULL);
- if(range)
- return 206;
- else
- return 0;
+ 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)
+ return -1;
+ content_len -= r;
+ }
+ }
+ close(fd);
+ return 0;
}
From b80b3e0fbc9536447ee8eb91696f9db9874cc653 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 2 Mar 2011 23:47:57 +0100
Subject: [PATCH 46/72] Use content-disposition to set a filename to be used
when downloading recorded files
---
src/http.c | 8 ++++++--
src/http.h | 3 ++-
src/webui/webui.c | 25 +++++++++++++++++++++----
3 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/src/http.c b/src/http.c
index 8d94c8bc..694f56ae 100644
--- a/src/http.c
+++ b/src/http.c
@@ -152,7 +152,8 @@ void
http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen,
const char *encoding, const char *location,
- int maxage, const char *range)
+ int maxage, const char *range,
+ const char *disposition)
{
struct tm tm0, *tm;
htsbuf_queue_t hdrs;
@@ -212,6 +213,9 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
htsbuf_qprintf(&hdrs, "Accept-Ranges: %s\r\n", "bytes");
htsbuf_qprintf(&hdrs, "Content-Range: %s\r\n", range);
}
+
+ if(disposition != NULL)
+ htsbuf_qprintf(&hdrs, "Content-Disposition: %s\r\n", disposition);
htsbuf_qprintf(&hdrs, "\r\n");
@@ -228,7 +232,7 @@ http_send_reply(http_connection_t *hc, int rc, const char *content,
const char *encoding, const char *location, int maxage)
{
http_send_header(hc, rc, content, hc->hc_reply.hq_size,
- encoding, location, maxage, 0);
+ encoding, location, maxage, 0, NULL);
if(hc->hc_no_output)
return;
diff --git a/src/http.h b/src/http.h
index 86adf1b0..2a29b8a9 100644
--- a/src/http.h
+++ b/src/http.h
@@ -113,7 +113,8 @@ void http_redirect(http_connection_t *hc, const char *location);
void http_send_header(http_connection_t *hc, int rc, const char *content,
int64_t contentlen, const char *encoding,
- const char *location, int maxage, const char *range);
+ const char *location, int maxage, const char *range,
+ const char *disposition);
typedef int (http_callback_t)(http_connection_t *hc,
const char *remain, void *opaque);
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 6c721ac6..7a696652 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -115,7 +115,7 @@ page_static_file(http_connection_t *hc, const char *remain, void *opaque)
return 404;
}
- http_send_header(hc, 200, content, st.st_size, NULL, NULL, 10, 0);
+ http_send_header(hc, 200, content, st.st_size, NULL, NULL, 10, 0, NULL);
sendfile(hc->hc_fd, fd, NULL, st.st_size);
close(fd);
return 0;
@@ -503,7 +503,8 @@ page_static_bundle(http_connection_t *hc, const char *remain, void *opaque)
if(!strcmp(fbe->filename, remain)) {
http_send_header(hc, 200, content, fbe->size,
- fbe->original_size == -1 ? NULL : "gzip", NULL, 10, 0);
+ fbe->original_size == -1 ? NULL : "gzip", NULL, 10, 0,
+ NULL);
/* ignore return value */
n = write(hc->hc_fd, fbe->data, fbe->size);
return 0;
@@ -519,12 +520,13 @@ page_static_bundle(http_connection_t *hc, const char *remain, void *opaque)
static int
page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
{
- int fd;
+ int fd, i;
struct stat st;
const char *content = NULL, *postfix, *range;
dvr_entry_t *de;
char *fname;
char range_buf[255];
+ char disposition[256];
off_t content_len, file_start, file_end, chunk;
ssize_t r;
@@ -584,9 +586,24 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
if(file_start > 0)
lseek(fd, file_start, SEEK_SET);
+ if(de->de_title != NULL) {
+ snprintf(disposition, sizeof(disposition),
+ "attachment; filename=%s.mkv", de->de_title);
+ i = 20;
+ while(disposition[i]) {
+ if(disposition[i] == ' ')
+ disposition[i] = '_';
+ i++;
+ }
+
+ } else {
+ disposition[0] = 0;
+ }
+
http_send_header(hc, range ? HTTP_STATUS_PARTIAL_CONTENT : HTTP_STATUS_OK,
content, content_len, NULL, NULL, 10,
- range ? range_buf : NULL);
+ range ? range_buf : NULL,
+ disposition[0] ? disposition : NULL);
if(!hc->hc_no_output) {
while(content_len > 0) {
From 069ac95251481f4b19cf77853f2d9547155c7a1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Mon, 7 Mar 2011 20:58:07 +0100
Subject: [PATCH 47/72] epg: Keep track of next event for all channels. (Useful
if there is no current event we can refer to) htsp: Add nextEventId to HTSP
channel updates
---
src/channels.h | 1 +
src/epg.c | 32 +++++++++++++++++++++-----------
src/htsp.c | 12 +++++++-----
src/htsp.h | 2 +-
src/main.c | 2 ++
5 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/src/channels.h b/src/channels.h
index 9cb02fdc..6dfbc73e 100644
--- a/src/channels.h
+++ b/src/channels.h
@@ -45,6 +45,7 @@ typedef struct channel {
struct event_tree ch_epg_events;
struct event *ch_epg_current;
+ struct event *ch_epg_next;
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
diff --git a/src/epg.c b/src/epg.c
index 24bf59d2..aac82ea9 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -53,16 +53,20 @@ e_ch_cmp(const event_t *a, const event_t *b)
*
*/
static void
-epg_set_current(channel_t *ch, event_t *e)
+epg_set_current(channel_t *ch, event_t *e, event_t *next)
{
- if(ch->ch_epg_current == e)
+ if(next == NULL && e != NULL)
+ next = RB_NEXT(e, e_channel_link);
+
+ if(ch->ch_epg_current == e && ch->ch_epg_next == next)
return;
ch->ch_epg_current = e;
+ ch->ch_epg_next = next;
if(e != NULL)
gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
ch, MAX(e->e_stop, dispatch_clock + 1));
- htsp_event_update(ch, e);
+ htsp_channgel_update_current(ch);
}
/**
@@ -73,17 +77,16 @@ epg_ch_check_current_event(void *aux)
{
channel_t *ch = aux;
event_t skel, *e = ch->ch_epg_current;
-
if(e != NULL) {
if(e->e_start <= dispatch_clock && e->e_stop > dispatch_clock) {
- epg_set_current(ch, e);
+ epg_set_current(ch, e, NULL);
return;
}
if((e = RB_NEXT(e, e_channel_link)) != NULL) {
if(e->e_start <= dispatch_clock && e->e_stop > dispatch_clock) {
- epg_set_current(ch, e);
+ epg_set_current(ch, e, NULL);
return;
}
}
@@ -91,22 +94,22 @@ epg_ch_check_current_event(void *aux)
e = epg_event_find_by_time(ch, dispatch_clock);
if(e != NULL) {
- epg_set_current(ch, e);
+ epg_set_current(ch, e, NULL);
return;
}
- epg_set_current(ch, NULL);
-
skel.e_start = dispatch_clock;
e = RB_FIND_GT(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp);
if(e != NULL) {
gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
ch, MAX(e->e_start, dispatch_clock + 1));
+ epg_set_current(ch, NULL, e);
+ } else {
+ epg_set_current(ch, NULL, NULL);
}
}
-
/**
*
*/
@@ -303,11 +306,13 @@ epg_remove_event_from_channel(channel_t *ch, event_t *e)
epg_event_unref(e);
if(ch->ch_epg_current == e) {
- epg_set_current(ch, NULL);
+ epg_set_current(ch, NULL, n);
if(n != NULL)
gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
ch, n->e_start);
+ } else if(ch->ch_epg_next == e) {
+ epg_set_current(ch, ch->ch_epg_current, NULL);
}
if(wasfirst && (e = RB_FIRST(&ch->ch_epg_events)) != NULL) {
@@ -645,7 +650,12 @@ epg_load(void)
void
epg_init(void)
{
+ channel_t *ch;
+
epg_load();
+
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link)
+ epg_ch_check_current_event(ch);
}
diff --git a/src/htsp.c b/src/htsp.c
index 9a49ad21..571ceb34 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -309,6 +309,8 @@ htsp_build_channel(channel_t *ch, const char *method)
htsmsg_add_u32(out, "eventId",
ch->ch_epg_current != NULL ? ch->ch_epg_current->e_id : 0);
+ htsmsg_add_u32(out, "nextEventId",
+ ch->ch_epg_next ? ch->ch_epg_next->e_id : 0);
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
ct = ctm->ctm_tag;
@@ -1256,7 +1258,7 @@ htsp_async_send(htsmsg_t *m)
* global_lock is held
*/
void
-htsp_event_update(channel_t *ch, event_t *e)
+htsp_channgel_update_current(channel_t *ch)
{
htsmsg_t *m;
time_t now;
@@ -1266,10 +1268,10 @@ htsp_event_update(channel_t *ch, event_t *e)
htsmsg_add_str(m, "method", "channelUpdate");
htsmsg_add_u32(m, "channelId", ch->ch_id);
- if(e == NULL)
- e = epg_event_find_by_time(ch, now);
-
- htsmsg_add_u32(m, "eventId", e ? e->e_id : 0);
+ htsmsg_add_u32(m, "eventId",
+ ch->ch_epg_current ? ch->ch_epg_current->e_id : 0);
+ htsmsg_add_u32(m, "nextEventId",
+ ch->ch_epg_next ? ch->ch_epg_next->e_id : 0);
htsp_async_send(m);
}
diff --git a/src/htsp.h b/src/htsp.h
index db6ca8d7..7dcf33e6 100644
--- a/src/htsp.h
+++ b/src/htsp.h
@@ -24,7 +24,7 @@
void htsp_init(void);
-void htsp_event_update(channel_t *ch, event_t *e);
+void htsp_channgel_update_current(channel_t *ch);
void htsp_channel_add(channel_t *ch);
void htsp_channel_update(channel_t *ch);
diff --git a/src/main.c b/src/main.c
index d2ee3a4a..17683a41 100644
--- a/src/main.c
+++ b/src/main.c
@@ -357,6 +357,8 @@ main(int argc, char **argv)
pthread_mutex_lock(&global_lock);
+ time(&dispatch_clock);
+
trap_init(argv[0]);
/**
From 3ad76eb6fc6111d4bb61de6e3b0ed354fd25843b Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Fri, 11 Mar 2011 00:02:28 +0100
Subject: [PATCH 48/72] add the ability to add custom dvr entries via htsp
---
src/htsp.c | 58 +++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/src/htsp.c b/src/htsp.c
index 571ceb34..317ffeb5 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -499,21 +499,55 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
dvr_entry_t *de;
dvr_entry_sched_state_t dvr_status;
const char *dvr_config_name;
-
- if(htsmsg_get_u32(in, "eventId", &eventid))
- return htsp_error("Missing argument 'eventId'");
-
- if((e = epg_event_find_by_id(eventid)) == NULL)
- return htsp_error("Event does not exist");
if((dvr_config_name = htsmsg_get_str(in, "configName")) == NULL)
dvr_config_name = "";
-
- //create the dvr entry
- de = dvr_entry_create_by_event(dvr_config_name,e,
- htsp->htsp_username ?
- htsp->htsp_username : "anonymous",
- NULL, DVR_PRIO_NORMAL);
+
+ if(htsmsg_get_u32(in, "eventId", &eventid))
+ eventid = -1;
+
+ if ((e = epg_event_find_by_id(eventid)) == NULL)
+ {
+ uint32_t iChannelId, iStartTime, iStopTime, iPriority;
+ channel_t *channel;
+ const char *strTitle = NULL, *strDescription = NULL, *strCreator = NULL;
+
+ // no event found with this event id.
+ // check if there is at least a start time, stop time, channel id and title set
+ if (htsmsg_get_u32(in, "channelId", &iChannelId) ||
+ htsmsg_get_u32(in, "start", &iStartTime) ||
+ htsmsg_get_u32(in, "stop", &iStopTime) ||
+ (strTitle = htsmsg_get_str(in, "title")) == NULL)
+ {
+ // not enough info available to create a new entry
+ return htsp_error("Invalid arguments");
+ }
+
+ // invalid channel
+ if ((channel = channel_find_by_identifier(iChannelId)) == NULL)
+ return htsp_error("Channel does not exist");
+
+ // get the optional attributes
+ if (htsmsg_get_u32(in, "priority", &iPriority))
+ iPriority = 0;
+
+ if ((strDescription = htsmsg_get_str(in, "description")) == NULL)
+ strDescription = "";
+
+ if ((strCreator = htsmsg_get_str(in, "creator")) == NULL)
+ strCreator = "";
+
+ // create the dvr entry
+ de = dvr_entry_create(dvr_config_name, channel, iStartTime, iStopTime, strTitle, strDescription, strCreator, NULL, NULL, 0, iPriority);
+ }
+ else
+ {
+ //create the dvr entry
+ de = dvr_entry_create_by_event(dvr_config_name,e,
+ htsp->htsp_username ?
+ htsp->htsp_username : "anonymous",
+ NULL, DVR_PRIO_NORMAL);
+ }
dvr_status = de != NULL ? de->de_sched_state : DVR_NOSTATE;
From ed2334b2cac6f3c850b5fdc230c9975cb16a5deb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Mon, 21 Mar 2011 22:35:32 +0100
Subject: [PATCH 49/72] Dont crash when deleting recordings and filename is not
yet set. Ticket #383
---
src/dvr/dvr.h | 2 +-
src/dvr/dvr_db.c | 19 ++++++++++---------
src/webui/extjs.c | 6 +++---
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index f31249ff..2481d0df 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -274,7 +274,7 @@ void dvr_extra_time_pre_set(dvr_config_t *cfg, int d);
void dvr_extra_time_post_set(dvr_config_t *cfg, int d);
-int dvr_entry_delete(dvr_entry_t *de);
+void dvr_entry_delete(dvr_entry_t *de);
/**
* Query interface
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index aa59112f..a8678f99 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -1161,17 +1161,18 @@ dvr_val2pri(dvr_prio_t v)
return val2str(v, priotab) ?: "invalid";
}
-int
+
+/**
+ *
+ */
+void
dvr_entry_delete(dvr_entry_t *de)
{
- if(!unlink(de->de_filename) || errno == ENOENT) {
- tvhlog(LOG_DEBUG, "dvr", "Delete recording '%s'", de->de_filename);
- dvr_entry_remove(de);
- return 0;
- } else {
- tvhlog(LOG_WARNING, "dvr", "Unable to delete recording '%s' -- %s",
- de->de_filename, strerror(errno));
- return -1;
+ if(de->de_filename != NULL) {
+ if(unlink(de->de_filename) && errno != ENOENT)
+ tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
+ de->de_filename, strerror(errno));
}
+ dvr_entry_remove(de);
}
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index 7468dea2..40495d29 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -764,7 +764,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
event_t *e;
dvr_entry_t *de;
const char *s;
- int flags = 0, retval;
+ int flags = 0;
dvr_config_t *cfg;
if(op == NULL)
@@ -813,10 +813,10 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
return HTTP_STATUS_BAD_REQUEST;
}
- retval = dvr_entry_delete(de);
+ dvr_entry_delete(de);
out = htsmsg_create_map();
- htsmsg_add_u32(out, "success", !retval);
+ htsmsg_add_u32(out, "success", 1);
} else if(!strcmp(op, "createEntry")) {
From fa941ddcbf7d8f1e98b81d2e39d20faa0ca90997 Mon Sep 17 00:00:00 2001
From: Martin Mrvka
Date: Sat, 26 Mar 2011 00:13:56 +0100
Subject: [PATCH 50/72] epg: merged support for settable dvb default charset
from master
---
src/dvb/dvb_multiplex.c | 3 +++
src/dvb/dvb_support.c | 16 +++++++++++----
src/dvb/dvb_support.h | 4 ++--
src/dvb/dvb_tables.c | 24 +++++++++++++---------
src/dvb/dvb_transport.c | 13 ++++++++++--
src/service.c | 19 +++++++++++++++++
src/service.h | 7 +++++++
src/webui/extjs.c | 11 ++++++++++
src/webui/static/app/dvb.js | 41 ++++++++++++++++++++++++++++++++++++-
9 files changed, 119 insertions(+), 19 deletions(-)
diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c
index 3d910d86..9756fd6e 100644
--- a/src/dvb/dvb_multiplex.c
+++ b/src/dvb/dvb_multiplex.c
@@ -1104,6 +1104,9 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src)
if(t_src->s_svcname != NULL)
t_dst->s_svcname = strdup(t_src->s_svcname);
+ if(t_src->s_dvb_default_charset != NULL)
+ t_dst->s_dvb_default_charset = strdup(t_src->s_dvb_default_charset);
+
if(t_src->s_ch != NULL)
service_map_channel(t_dst, t_src->s_ch, 0);
diff --git a/src/dvb/dvb_support.c b/src/dvb/dvb_support.c
index b7098a55..3ad58cd5 100644
--- a/src/dvb/dvb_support.c
+++ b/src/dvb/dvb_support.c
@@ -74,7 +74,7 @@ dvb_conversion_init(void)
*/
int
-dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen)
+dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, char *dvb_default_charset)
{
iconv_t ic;
int len;
@@ -121,7 +121,15 @@ dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen)
return -1;
default:
- ic = convert_latin1;
+ if (dvb_default_charset != NULL && sscanf(dvb_default_charset, "ISO8859-%d", &i) > 0) {
+ if (i > 0 && i < 16) {
+ ic = convert_iso_8859[i];
+ } else {
+ ic = convert_latin1;
+ }
+ } else {
+ ic = convert_latin1;
+ }
break;
}
@@ -175,14 +183,14 @@ dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen)
int
dvb_get_string_with_len(char *dst, size_t dstlen,
- const uint8_t *buf, size_t buflen)
+ const uint8_t *buf, size_t buflen, char *dvb_default_charset)
{
int l = buf[0];
if(l + 1 > buflen)
return -1;
- if(dvb_get_string(dst, dstlen, buf + 1, l))
+ if(dvb_get_string(dst, dstlen, buf + 1, l, dvb_default_charset))
return -1;
return l + 1;
diff --git a/src/dvb/dvb_support.h b/src/dvb/dvb_support.h
index 2cdf474b..7ec2d5ee 100644
--- a/src/dvb/dvb_support.h
+++ b/src/dvb/dvb_support.h
@@ -51,10 +51,10 @@
#define DVB_DESC_LOCAL_CHAN 0x83
int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
- const size_t srclen);
+ const size_t srclen, char *dvb_default_charset);
int dvb_get_string_with_len(char *dst, size_t dstlen,
- const uint8_t *buf, size_t buflen);
+ const uint8_t *buf, size_t buflen, char *dvb_default_charset);
#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c
index de18cd02..a9ca19e6 100644
--- a/src/dvb/dvb_tables.c
+++ b/src/dvb/dvb_tables.c
@@ -369,7 +369,8 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
static int
dvb_desc_short_event(uint8_t *ptr, int len,
char *title, size_t titlelen,
- char *desc, size_t desclen)
+ char *desc, size_t desclen,
+ char *dvb_default_charset)
{
int r;
@@ -377,11 +378,11 @@ dvb_desc_short_event(uint8_t *ptr, int len,
return -1;
ptr += 3; len -= 3;
- if((r = dvb_get_string_with_len(title, titlelen, ptr, len)) < 0)
+ if((r = dvb_get_string_with_len(title, titlelen, ptr, len, dvb_default_charset)) < 0)
return -1;
ptr += r; len -= r;
- if((r = dvb_get_string_with_len(desc, desclen, ptr, len)) < 0)
+ if((r = dvb_get_string_with_len(desc, desclen, ptr, len, dvb_default_charset)) < 0)
return -1;
return 0;
@@ -394,7 +395,8 @@ static int
dvb_desc_extended_event(uint8_t *ptr, int len,
char *desc, size_t desclen,
char *item, size_t itemlen,
- char *text, size_t textlen)
+ char *text, size_t textlen,
+ char *dvb_default_charset)
{
int count = ptr[4], r;
uint8_t *localptr = ptr + 5, *items = localptr;
@@ -434,7 +436,7 @@ dvb_desc_extended_event(uint8_t *ptr, int len,
count = localptr[0];
/* get text */
- if((r = dvb_get_string_with_len(text, textlen, localptr, locallen)) < 0)
+ if((r = dvb_get_string_with_len(text, textlen, localptr, locallen, dvb_default_charset)) < 0)
return -1;
return 0;
@@ -459,11 +461,11 @@ dvb_desc_service(uint8_t *ptr, int len, uint8_t *typep,
ptr++;
len--;
- if((r = dvb_get_string_with_len(provider, providerlen, ptr, len)) < 0)
+ if((r = dvb_get_string_with_len(provider, providerlen, ptr, len, NULL)) < 0)
return -1;
ptr += r; len -= r;
- if((r = dvb_get_string_with_len(name, namelen, ptr, len)) < 0)
+ if((r = dvb_get_string_with_len(name, namelen, ptr, len, NULL)) < 0)
return -1;
ptr += r; len -= r;
return 0;
@@ -586,7 +588,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
case DVB_DESC_SHORT_EVENT:
if(!dvb_desc_short_event(ptr, dlen,
title, sizeof(title),
- desc, sizeof(desc))) {
+ desc, sizeof(desc),
+ t->s_dvb_default_charset)) {
changed |= epg_event_set_title(e, title);
changed |= epg_event_set_desc(e, desc);
}
@@ -602,7 +605,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(!dvb_desc_extended_event(ptr, dlen,
extdesc, sizeof(extdesc),
extitem, sizeof(extitem),
- exttext, sizeof(exttext))) {
+ exttext, sizeof(exttext),
+ t->s_dvb_default_charset)) {
char language[4];
memcpy(language, &ptr[1], 3);
@@ -1122,7 +1126,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
switch(tag) {
case DVB_DESC_NETWORK_NAME:
- if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen))
+ if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen, NULL))
return -1;
if(strcmp(tdmi->tdmi_network ?: "", networkname))
diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c
index b438d964..62b776bc 100644
--- a/src/dvb/dvb_transport.c
+++ b/src/dvb/dvb_transport.c
@@ -234,11 +234,14 @@ dvb_transport_load(th_dvb_mux_instance_t *tdmi)
service_make_nicename(t);
psi_load_service_settings(c, t);
pthread_mutex_unlock(&t->s_stream_mutex);
-
+
+ s = htsmsg_get_str(c, "dvb_default_charset");
+ t->s_dvb_default_charset = s ? strdup(s) : NULL;
+
s = htsmsg_get_str(c, "channelname");
if(htsmsg_get_u32(c, "mapped", &u32))
u32 = 0;
-
+
if(s && u32)
service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
}
@@ -271,6 +274,9 @@ dvb_transport_save(service_t *t)
htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
htsmsg_add_u32(m, "mapped", 1);
}
+
+ if(t->s_dvb_default_charset != NULL)
+ htsmsg_add_str(m, "dvb_default_charset", t->s_dvb_default_charset);
pthread_mutex_lock(&t->s_stream_mutex);
psi_save_service_settings(m, t);
@@ -431,6 +437,9 @@ dvb_transport_build_msg(service_t *t)
if(t->s_ch != NULL)
htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
+ if(t->s_dvb_default_charset != NULL)
+ htsmsg_add_str(m, "dvb_default_charset", t->s_dvb_default_charset);
+
return m;
}
diff --git a/src/service.c b/src/service.c
index 94c0f345..49e04bd7 100644
--- a/src/service.c
+++ b/src/service.c
@@ -465,6 +465,7 @@ service_destroy(service_t *t)
free(t->s_identifier);
free(t->s_svcname);
free(t->s_provider);
+ free(t->s_dvb_default_charset);
while((st = TAILQ_FIRST(&t->s_components)) != NULL) {
TAILQ_REMOVE(&t->s_components, st, es_link);
@@ -503,6 +504,7 @@ service_create(const char *identifier, int type, int source_type)
t->s_refcount = 1;
t->s_enabled = 1;
t->s_pcr_last = PTS_UNSET;
+ t->s_dvb_default_charset = NULL;
TAILQ_INIT(&t->s_components);
streaming_pad_init(&t->s_streaming_pad);
@@ -674,6 +676,23 @@ service_map_channel(service_t *t, channel_t *ch, int save)
t->s_config_save(t);
}
+/**
+ *
+ */
+void
+service_set_dvb_default_charset(service_t *t, const char *dvb_default_charset)
+{
+ lock_assert(&global_lock);
+
+ if(t->s_dvb_default_charset != NULL && !strcmp(t->s_dvb_default_charset, dvb_default_charset))
+ return;
+
+ free(t->s_dvb_default_charset);
+ t->s_dvb_default_charset = strdup(dvb_default_charset);
+ t->s_config_save(t);
+}
+
+
/**
*
*/
diff --git a/src/service.h b/src/service.h
index 4042eff6..4b8458e5 100644
--- a/src/service.h
+++ b/src/service.h
@@ -476,6 +476,12 @@ typedef struct service {
int64_t s_current_pts;
+ /**
+ * DVB default charset
+ * used to overide the default ISO6937 per service
+ */
+ char *s_dvb_default_charset;
+
} service_t;
@@ -561,5 +567,6 @@ uint16_t service_get_encryption(service_t *t);
int service_get_signal_status(service_t *t, signal_status_t *status);
+void service_set_dvb_default_charset(service_t *t, const char *dvb_default_charset);
#endif // SERVICE_H__
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index 40495d29..cd95ceb4 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -1116,6 +1116,7 @@ service_update(htsmsg_t *in)
uint32_t u32;
const char *id;
const char *chname;
+ const char *dvb_default_charset;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((c = htsmsg_get_map_by_field(f)) == NULL ||
@@ -1130,6 +1131,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((dvb_default_charset = htsmsg_get_str(c, "dvb_default_charset")) != NULL)
+ service_set_dvb_default_charset(t, dvb_default_charset);
}
}
@@ -1211,6 +1215,9 @@ extjs_servicedetails(http_connection_t *hc,
htsmsg_add_msg(out, "streams", streams);
+ if(t->s_dvb_default_charset != NULL)
+ htsmsg_add_str(out, "dvb_default_charset", t->s_dvb_default_charset);
+
pthread_mutex_unlock(&global_lock);
htsmsg_json_serialize(out, hq, 0);
@@ -1435,6 +1442,7 @@ extjs_service_update(htsmsg_t *in)
uint32_t u32;
const char *id;
const char *chname;
+ const char *dvb_default_charset;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((c = htsmsg_get_map_by_field(f)) == NULL ||
@@ -1449,6 +1457,9 @@ extjs_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((dvb_default_charset = htsmsg_get_str(c, "dvb_default_charset")) != NULL)
+ service_set_dvb_default_charset(t, dvb_default_charset);
}
}
diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js
index c82611a3..9063cdbc 100644
--- a/src/webui/static/app/dvb.js
+++ b/src/webui/static/app/dvb.js
@@ -394,6 +394,45 @@ tvheadend.dvb_services = function(adapterId) {
displayField:'name'
})
},
+ {
+ header: "DVB default charset",
+ dataIndex: 'dvb_default_charset',
+ width: 200,
+ renderer: function(value, metadata, record, row, col, store) {
+ return value ? value :
+ 'ISO6937';
+ },
+ editor: new fm.ComboBox({
+ mode: 'local',
+ store: new Ext.data.SimpleStore({
+ fields: ['key','value'],
+ data: [
+ ['ISO6937','default'],
+ ['ISO6937','ISO6937'],
+ ['ISO8859-1','ISO8859-1'],
+ ['ISO8859-2','ISO8859-2'],
+ ['ISO8859-3','ISO8859-3'],
+ ['ISO8859-4','ISO8859-4'],
+ ['ISO8859-5','ISO8859-5'],
+ ['ISO8859-6','ISO8859-6'],
+ ['ISO8859-7','ISO8859-7'],
+ ['ISO8859-8','ISO8859-8'],
+ ['ISO8859-9','ISO8859-9'],
+ ['ISO8859-10','ISO8859-10'],
+ ['ISO8859-11','ISO8859-11'],
+ ['ISO8859-12','ISO8859-12'],
+ ['ISO8859-13','ISO8859-13'],
+ ['ISO8859-14','ISO8859-14'],
+ ['ISO8859-15','ISO8859-15']
+ ]
+ }),
+ typeAhead: true,
+ lazyRender: true,
+ triggerAction: 'all',
+ displayField:'value',
+ valueField:'key'
+ })
+ },
{
header: "Type",
dataIndex: 'type',
@@ -440,7 +479,7 @@ tvheadend.dvb_services = function(adapterId) {
root: 'entries',
fields: Ext.data.Record.create([
'id', 'enabled', 'type', 'sid', 'pmt', 'pcr',
- 'svcname', 'network', 'provider', 'mux', 'channelname'
+ 'svcname', 'network', 'provider', 'mux', 'channelname', 'dvb_default_charset'
]),
url: "dvb/services/" + adapterId,
autoLoad: true,
From fe373b28b7055a21511f40fc4174cedb392f16bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Bidoul?=
Date: Wed, 9 Mar 2011 16:13:04 +0100
Subject: [PATCH 51/72] Fix problem with xml parser eating spaces after
character entity references
---
src/htsmsg_xml.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/htsmsg_xml.c b/src/htsmsg_xml.c
index 87ba6494..7c494770 100644
--- a/src/htsmsg_xml.c
+++ b/src/htsmsg_xml.c
@@ -637,7 +637,7 @@ htsmsg_xml_parse_cd0(xmlparser_t *xp,
}
if(cc == NULL) {
- if(*src <= 32) {
+ if(*src < 32) {
src++;
continue;
}
From 2f47066f6ef233b8044bb53900067f5a7e9ef98c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Fri, 1 Apr 2011 15:32:07 +0200
Subject: [PATCH 52/72] Correctly deal with return value from write()
---
src/tcp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/tcp.c b/src/tcp.c
index 171a4dc6..27ed5602 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -181,18 +181,18 @@ int
tcp_write_queue(int fd, htsbuf_queue_t *q)
{
htsbuf_data_t *hd;
- int l, r;
+ int l, r = 0;
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);
+ r |= !!write(fd, hd->hd_data + hd->hd_data_off, l);
free(hd->hd_data);
free(hd);
}
q->hq_size = 0;
- return 0;
+ return r;
}
From 3b3669ea6ec66dfd7ee09ed2f38e148d8e35d7f0 Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Fri, 1 Apr 2011 05:26:22 +0800
Subject: [PATCH 53/72] Set a default creator name if an empty or no creator
name is provided when adding a DVR entry without an EPG event. Adding an
entry with an empty creator name causes the GUI not to display any DVR entry
at all.
---
src/htsp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/htsp.c b/src/htsp.c
index 4318346b..c6d6be25 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -534,8 +534,8 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
if ((strDescription = htsmsg_get_str(in, "description")) == NULL)
strDescription = "";
- if ((strCreator = htsmsg_get_str(in, "creator")) == NULL)
- strCreator = "";
+ if ((strCreator = htsmsg_get_str(in, "creator")) == NULL || strcmp(strCreator, "") == 0)
+ strCreator = htsp->htsp_username ? htsp->htsp_username : "anonymous";
// create the dvr entry
de = dvr_entry_create(dvr_config_name, channel, iStartTime, iStopTime, strTitle, strDescription, strCreator, NULL, NULL, 0, iPriority);
From d2101f6203628b18adac4fca7870bc3dbd49fad2 Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Fri, 1 Apr 2011 05:53:32 +0800
Subject: [PATCH 54/72] set the default priorit to DVR_PRIO_NORMAL instead of 0
with creating a DVR entry without an EPG event.
---
src/htsp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/htsp.c b/src/htsp.c
index c6d6be25..9d916ec4 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -529,7 +529,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
// get the optional attributes
if (htsmsg_get_u32(in, "priority", &iPriority))
- iPriority = 0;
+ iPriority = DVR_PRIO_NORMAL;
if ((strDescription = htsmsg_get_str(in, "description")) == NULL)
strDescription = "";
From 90e35703b9f6fb81df16f8cd59fd5cb258931946 Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Mon, 18 Apr 2011 00:57:52 +0800
Subject: [PATCH 55/72] implement emm updates for nagra and nds. credits: neoen
---
src/cwc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/src/cwc.c b/src/cwc.c
index c42ec3f0..408b0d5f 100644
--- a/src/cwc.c
+++ b/src/cwc.c
@@ -59,6 +59,8 @@ typedef enum {
CARD_CONAX,
CARD_SECA,
CARD_VIACCESS,
+ CARD_NAGRA,
+ CARD_NDS,
CARD_UNKNOWN
} card_type_t;
@@ -276,6 +278,8 @@ void cwc_emm_irdeto(cwc_t *cwc, uint8_t *data, int len);
void cwc_emm_dre(cwc_t *cwc, uint8_t *data, int len);
void cwc_emm_seca(cwc_t *cwc, uint8_t *data, int len);
void cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int len);
+void cwc_emm_nagra(cwc_t *cwc, uint8_t *data, int len);
+void cwc_emm_nds(cwc_t *cwc, uint8_t *data, int len);
/**
@@ -567,7 +571,7 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len)
cwc->cwc_connected = 1;
cwc_comet_status_update(cwc);
cwc->cwc_caid = (msg[4] << 8) | msg[5];
- n = psi_caid2name(cwc->cwc_caid) ?: "Unknown";
+ n = psi_caid2name(cwc->cwc_caid & 0xff00) ?: "Unknown";
memcpy(cwc->cwc_ua, &msg[6], 8);
@@ -675,6 +679,16 @@ cwc_detect_card_type(cwc_t *cwc)
tvhlog(LOG_INFO, "cwc", "%s: dre card",
cwc->cwc_hostname);
break;
+ case 0x18:
+ cwc->cwc_card_type = CARD_NAGRA;
+ tvhlog(LOG_INFO, "cwc", "%s: nagra card",
+ cwc->cwc_hostname);
+ break;
+ case 0x09:
+ cwc->cwc_card_type = CARD_NDS;
+ tvhlog(LOG_INFO, "cwc", "%s: nds card",
+ cwc->cwc_hostname);
+ break;
default:
cwc->cwc_card_type = CARD_UNKNOWN;
break;
@@ -1183,6 +1197,12 @@ cwc_emm(uint8_t *data, int len)
case CARD_DRE:
cwc_emm_dre(cwc, data, len);
break;
+ case CARD_NAGRA:
+ cwc_emm_nagra(cwc, data, len);
+ break;
+ case CARD_NDS:
+ cwc_emm_nds(cwc, data, len);
+ break;
case CARD_UNKNOWN:
break;
}
@@ -1581,6 +1601,51 @@ cwc_emm_dre(cwc_t *cwc, uint8_t *data, int len)
cwc_send_msg(cwc, data, len, 0, 1);
}
+void
+cwc_emm_nagra(cwc_t *cwc, uint8_t *data, int len)
+{
+ int match = 0;
+ unsigned char hexserial[4];
+
+ if (data[0] == 0x83) { // unique|shared
+ hexserial[0] = data[5];
+ hexserial[1] = data[4];
+ hexserial[2] = data[3];
+ hexserial[3] = data[6];
+ if (memcmp(hexserial, &cwc->cwc_ua[4], (data[7] == 0x10) ? 3 : 4) == 0)
+ match = 1;
+ }
+ else if (data[0] == 0x82) { // global
+ match = 1;
+ }
+
+ if (match)
+ cwc_send_msg(cwc, data, len, 0, 1);
+}
+
+void
+cwc_emm_nds(cwc_t *cwc, uint8_t *data, int len)
+{
+ int match = 0;
+ int i;
+ int serial_count = ((data[3] >> 4) & 3) + 1;
+ unsigned char emmtype = (data[3] & 0xC0) >> 6;
+
+ if (emmtype == 1 || emmtype == 2) { // unique|shared
+ for (i = 0; i < serial_count; i++) {
+ if (memcmp(&data[i * 4 + 4], &cwc->cwc_ua[4], 5 - emmtype) == 0) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ else if (emmtype == 0) { // global
+ match = 1;
+ }
+
+ if (match)
+ cwc_send_msg(cwc, data, len, 0, 1);
+}
/**
*
From f1cbc7348014d7fb926adf65c52e9a94701f8eaf Mon Sep 17 00:00:00 2001
From: Dimitris Kazakos
Date: Wed, 20 Apr 2011 20:32:33 +0300
Subject: [PATCH 56/72] Also delete created directories (if they exist and are
empty) when deleting a recording file from disk
---
src/dvr/dvr_db.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index acc626fa..4bd16a03 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -1192,6 +1192,34 @@ dvr_entry_delete(dvr_entry_t *de)
if(unlink(de->de_filename) && errno != ENOENT)
tvhlog(LOG_WARNING, "dvr", "Unable to remove file '%s' from disk -- %s",
de->de_filename, strerror(errno));
+
+ /* Also delete directories, if they were created for the recording and if they are empty */
+
+ dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+ char path[500];
+
+ snprintf(path, sizeof(path), "%s", cfg->dvr_storage);
+
+ if(cfg->dvr_flags & DVR_DIR_PER_TITLE || cfg->dvr_flags & DVR_DIR_PER_CHANNEL || cfg->dvr_flags & DVR_DIR_PER_DAY) {
+ char *p;
+ int l;
+
+ l = strlen(de->de_filename);
+ p = alloca(l + 1);
+ memcpy(p, de->de_filename, l);
+ p[l--] = 0;
+
+ for(; l >= 0; l--) {
+ if(p[l] == '/') {
+ p[l] = 0;
+ if(strncmp(p, cfg->dvr_storage, strlen(p)) == 0)
+ break;
+ if(rmdir(p) == -1)
+ break;
+ }
+ }
+ }
+
}
dvr_entry_remove(de);
}
From 9408f1d5bc4189a4b1ad054743919b3ce14458bd Mon Sep 17 00:00:00 2001
From: Dimitris Kazakos
Date: Wed, 20 Apr 2011 12:51:41 +0300
Subject: [PATCH 57/72] Implement dvr_entry_cancel_delete in dvr_db and
deleteDvrEntry/cancelDvrEntry in HTSP in order to provide HTSP clients the
ability to delete the actual recording files along with a dvr entry
---
src/dvr/dvr.h | 2 ++
src/dvr/dvr_db.c | 26 ++++++++++++++++++++++++++
src/htsp.c | 36 +++++++++++++++++++++++++++++++-----
3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index d4940d67..e8ebee9e 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -278,6 +278,8 @@ void dvr_extra_time_post_set(dvr_config_t *cfg, int d);
void dvr_entry_delete(dvr_entry_t *de);
+void dvr_entry_cancel_delete(dvr_entry_t *de);
+
/**
* Query interface
*/
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index acc626fa..c4e55b49 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -1196,3 +1196,29 @@ dvr_entry_delete(dvr_entry_t *de)
dvr_entry_remove(de);
}
+/**
+ *
+ */
+void
+dvr_entry_cancel_delete(dvr_entry_t *de)
+{
+ switch(de->de_sched_state) {
+ case DVR_SCHEDULED:
+ dvr_entry_remove(de);
+ break;
+
+ case DVR_RECORDING:
+ de->de_dont_reschedule = 1;
+ dvr_stop_recording(de, SM_CODE_ABORTED);
+ case DVR_COMPLETED:
+ dvr_entry_delete(de);
+ break;
+
+ case DVR_MISSED_TIME:
+ dvr_entry_remove(de);
+ break;
+
+ default:
+ abort();
+ }
+}
diff --git a/src/htsp.c b/src/htsp.c
index 9d916ec4..6077f009 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -609,15 +609,15 @@ htsp_method_updateDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
}
/**
- * delete a Dvrentry
+ * cancel a Dvrentry
*/
-static htsmsg_t *
-htsp_method_deleteDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
+static htsmsg_t *
+htsp_method_cancelDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
{
htsmsg_t *out;
uint32_t dvrEntryId;
dvr_entry_t *de;
-
+
if(htsmsg_get_u32(in, "id", &dvrEntryId))
return htsp_error("Missing argument 'id'");
@@ -629,7 +629,32 @@ htsp_method_deleteDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
//create response
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
-
+
+ return out;
+}
+
+/**
+ * delete a Dvrentry
+ */
+static htsmsg_t *
+htsp_method_deleteDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
+{
+ htsmsg_t *out;
+ uint32_t dvrEntryId;
+ dvr_entry_t *de;
+
+ if(htsmsg_get_u32(in, "id", &dvrEntryId))
+ return htsp_error("Missing argument 'id'");
+
+ if( (de = dvr_entry_find_by_id(dvrEntryId)) == NULL)
+ return htsp_error("id not found");
+
+ dvr_entry_cancel_delete(de);
+
+ //create response
+ out = htsmsg_create_map();
+ htsmsg_add_u32(out, "success", 1);
+
return out;
}
@@ -1000,6 +1025,7 @@ struct {
{ "subscriptionChangeWeight", htsp_method_change_weight, ACCESS_STREAMING},
{ "addDvrEntry", htsp_method_addDvrEntry, ACCESS_RECORDER},
{ "updateDvrEntry", htsp_method_updateDvrEntry, ACCESS_RECORDER},
+ { "cancelDvrEntry", htsp_method_cancelDvrEntry, ACCESS_RECORDER},
{ "deleteDvrEntry", htsp_method_deleteDvrEntry, ACCESS_RECORDER},
{ "epgQuery", htsp_method_epgQuery, ACCESS_STREAMING},
From 542acadc8faa0c9ae045217a488ca5382ceac30b Mon Sep 17 00:00:00 2001
From: Lars Op den Kamp
Date: Thu, 21 Apr 2011 00:12:08 +0200
Subject: [PATCH 58/72] add the number of channels and rate to audio stream
descriptors and the aspect to video stream descriptors in htsp.
---
src/htsp.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/htsp.c b/src/htsp.c
index 9d916ec4..f178fdee 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -1600,6 +1600,18 @@ htsp_subscription_start(htsp_subscription_t *hs, const streaming_start_t *ss)
htsmsg_add_u32(c, "width", ssc->ssc_width);
if(ssc->ssc_height)
htsmsg_add_u32(c, "height", ssc->ssc_height);
+ if (ssc->ssc_aspect_num)
+ htsmsg_add_u32(c, "aspect_num", ssc->ssc_aspect_num);
+ if (ssc->ssc_aspect_den)
+ htsmsg_add_u32(c, "aspect_den", ssc->ssc_aspect_den);
+ }
+
+ if (SCT_ISAUDIO(ssc->ssc_type))
+ {
+ if (ssc->ssc_channels)
+ htsmsg_add_u32(c, "channels", ssc->ssc_channels);
+ if (ssc->ssc_sri)
+ htsmsg_add_u32(c, "rate", ssc->ssc_sri);
}
htsmsg_add_msg(streams, NULL, c);
From cd4614f09d432bce46eea1d92b107a0c23efde56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 3 May 2011 20:32:17 +0200
Subject: [PATCH 59/72] not so verbose compile output
---
Makefile | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index d4a104ca..05761a71 100644
--- a/Makefile
+++ b/Makefile
@@ -159,6 +159,15 @@ CFLAGS_com += -D_FILE_OFFSET_BITS=64
CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
CFLAGS_com += -DHTS_VERSION=\"$(VERSION)\"
+ifndef V
+ECHO = printf "$(1)\t%s\n" $(2)
+BRIEF = CC MKBUNDLE CXX
+MSG = $@
+$(foreach VAR,$(BRIEF), \
+ $(eval $(VAR) = @$$(call ECHO,$(VAR),$$(MSG)); $($(VAR))))
+endif
+
+
all: ${PROG}
.PHONY: clean distclean
@@ -186,7 +195,7 @@ ifneq ($(VERSION), $(CURVERSION))
.PHONY: src/version.c
$(info Version changed)
src/version.c:
- echo $(VERSION) >${BUILDDIR}/ver
+ @echo $(VERSION) >${BUILDDIR}/ver
endif
From 935114b608e5b57bbbd2d7bd44c4e61071aac1bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 3 May 2011 21:03:49 +0200
Subject: [PATCH 60/72] Improve SUMMARY metadata in matroska files
Choose longest string from EPG's description, extended description,
extended item or extended text.
---
src/dvr/dvr_db.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 01170fff..efbc6c17 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -320,6 +320,20 @@ dvr_entry_create(const char *config_name,
}
+/**
+ *
+ */
+static const char *
+longest_string(const char *a, const char *b)
+{
+ if(b == NULL)
+ return a;
+ if(a == NULL)
+ return b;
+ return strlen(a) > strlen(b) ? a : b;
+}
+
+
/**
*
*/
@@ -328,12 +342,19 @@ dvr_entry_create_by_event(const char *config_name,
event_t *e, const char *creator,
dvr_autorec_entry_t *dae, dvr_prio_t pri)
{
+ const char *desc = NULL;
if(e->e_channel == NULL || e->e_title == NULL)
return NULL;
+ // Try to find best description
+
+ desc = longest_string(e->e_desc, e->e_ext_desc);
+ desc = longest_string(desc, e->e_ext_item);
+ desc = longest_string(desc, e->e_ext_text);
+
return dvr_entry_create(config_name,
e->e_channel, e->e_start, e->e_stop,
- e->e_title, e->e_desc, creator, dae, &e->e_episode,
+ e->e_title, desc, creator, dae, &e->e_episode,
e->e_content_type, pri);
}
From c26a0b4b65c8b579ddd8d775d544d5285f5919f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 3 May 2011 21:53:21 +0200
Subject: [PATCH 61/72] dvb: Better handling of uncorrected blocks counter
Some DVB adapters return delta value from last read. Some return
absolute value since adapter was created.
Add some heuristics that tries to autodetect this.
Ticket #393
---
src/dvb/dvb.h | 5 +++++
src/dvb/dvb_fe.c | 53 +++++++++++++++++++++++++++++++++++++++---------
2 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h
index 347fe31a..9c14e23f 100644
--- a/src/dvb/dvb.h
+++ b/src/dvb/dvb.h
@@ -200,6 +200,11 @@ typedef struct th_dvb_adapter {
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
+ * return dela values */
+
} th_dvb_adapter_t;
diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c
index 23821134..9e7b7890 100644
--- a/src/dvb/dvb_fe.c
+++ b/src/dvb/dvb_fe.c
@@ -40,6 +40,42 @@
#include "notify.h"
#include "dvr/dvr.h"
+/**
+ * Return uncorrected block (since last read)
+ *
+ * Some adapters report delta themselfs, some return ever increasing value
+ * we need to deal with that ourselfs
+ */
+static int
+dvb_fe_get_unc(th_dvb_adapter_t *tda)
+{
+ uint32_t fec;
+ int d, r;
+
+ if(ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec))
+ return 0; // read failed, just say 0
+
+ if(tda->tda_unc_is_delta)
+ return fec;
+
+ d = (int)fec - (int)tda->tda_last_fec;
+
+ if(d < 0) {
+ tda->tda_unc_is_delta = 1;
+ tvhlog(LOG_DEBUG, "dvb",
+ "%s: FE_READ_UNCORRECTED_BLOCKS returns delta updates (delta=%d)",
+ tda->tda_displayname, d);
+ return fec;
+ }
+
+ r = fec - tda->tda_last_fec;
+
+ tda->tda_last_fec = fec;
+ return r;
+}
+
+
+
/**
* Front end monitor
*
@@ -79,7 +115,7 @@ dvb_fe_monitor(void *aux)
if(status == -1) { /* We have a lock, don't hold off */
tda->tda_fe_monitor_hold = 0;
/* Reset FEC counter */
- ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec);
+ dvb_fe_get_unc(tda);
} else {
tda->tda_fe_monitor_hold--;
return;
@@ -89,8 +125,7 @@ dvb_fe_monitor(void *aux)
if(status == -1) {
/* Read FEC counter (delta) */
- if(ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &fec))
- fec = 0;
+ fec = dvb_fe_get_unc(tda);
tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec;
if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE)
@@ -277,7 +312,7 @@ 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, uncorrected_blocks;
+ uint32_t ber;
int timeout = 0;
do {
@@ -292,15 +327,13 @@ static int check_frontend (int fe_fd, int dvr, int human_readable) {
snr = -2;
if (ioctl(fe_fd, FE_READ_BER, &ber) == -1)
ber = -2;
- if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1)
- uncorrected_blocks = -2;
if (human_readable) {
- printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ",
- status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks);
+ 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 | unc %08x | ",
- status, signal, snr, ber, uncorrected_blocks);
+ printf ("status %02x | signal %04x | snr %04x | ber %08x | ",
+ status, signal, snr, ber);
}
if (status & FE_HAS_LOCK)
printf("FE_HAS_LOCK");
From 1f84a09e7e27de51a5852cfea86f845cdcfa9b2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 3 May 2011 22:51:19 +0200
Subject: [PATCH 62/72] Fix MKBUNDLE in makefile silencer
---
Makefile | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
index 05761a71..10219170 100644
--- a/Makefile
+++ b/Makefile
@@ -159,6 +159,8 @@ CFLAGS_com += -D_FILE_OFFSET_BITS=64
CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
CFLAGS_com += -DHTS_VERSION=\"$(VERSION)\"
+MKBUNDLE = $(CURDIR)/support/mkbundle
+
ifndef V
ECHO = printf "$(1)\t%s\n" $(2)
BRIEF = CC MKBUNDLE CXX
@@ -209,6 +211,5 @@ include support/${OSENV}.mk
$(BUILDDIR)/bundles/%.o: $(BUILDDIR)/bundles/%.c
$(CC) -I${CURDIR}/src -c -o $@ $<
-$(BUILDDIR)/bundles/%.c: % $(CURDIR)/support/mkbundle
- $(CURDIR)/support/mkbundle \
- -o $@ -s $< -d ${BUILDDIR}/bundles/$<.d -p $< -z
+$(BUILDDIR)/bundles/%.c: %
+ $(MKBUNDLE) -o $@ -s $< -d ${BUILDDIR}/bundles/$<.d -p $< -z
From 844deeb08b27dc0b02ff29ddce7d093f40e64070 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 3 May 2011 23:41:55 +0200
Subject: [PATCH 63/72] Split debugging symbols in separate files
Done according to the guidelines at:
http://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
---
support/posix.mk | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/support/posix.mk b/support/posix.mk
index af845586..995afc9f 100644
--- a/support/posix.mk
+++ b/support/posix.mk
@@ -1,16 +1,24 @@
-prefix ?= $(INSTALLPREFIX)
-INSTBIN= $(prefix)/bin
-INSTMAN= $(prefix)/share/man1
+INSTBIN= ${DESTDIR}${INSTALLPREFIX}/bin
+INSTMAN= ${DESTDIR}${INSTALLPREFIX}/share/man1
+INSTDBG= ${DESTDIR}/usr/lib/debug/${INSTALLPREFIX}/bin
MAN=man/tvheadend.1
install: ${PROG} ${MAN}
- mkdir -p ${DESTDIR}$(INSTBIN)
- install -s ${PROG} ${DESTDIR}$(INSTBIN)
+ mkdir -p ${INSTBIN}
+ mkdir -p ${INSTDBG}
+ install -T ${PROG} ${INSTBIN}/tvheadend
- mkdir -p ${DESTDIR}$(INSTMAN)
- install ${MAN} ${DESTDIR}$(INSTMAN)
+ objcopy --only-keep-debug ${INSTBIN}/tvheadend ${INSTDBG}/tvheadend.debug
+ strip -g ${INSTBIN}/tvheadend
+
+ objcopy --add-gnu-debuglink=${INSTDBG}/tvheadend.debug ${INSTBIN}/tvheadend
+
+
+ mkdir -p ${INSTMAN}
+ install ${MAN} ${INSTMAN}
uninstall:
- rm -f ${DESTDIR}$(INSTBIN)/${PROG}
- rm -f ${DESTDIR}$(INSTMAN)/${MAN}
+ rm -f ${INSTBIN}/tvheadend
+ rm -f ${INSTDBG}/tvheadend.debug
+ rm -f ${INSTMAN}/tvheadend.1
From 24b7b2ed15c72edb27c1259545cc7c164c9cd6dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 4 May 2011 13:45:31 +0200
Subject: [PATCH 64/72] Check and act on return values from write()
---
src/cwc.c | 3 +++
src/htsp.c | 5 +++++
2 files changed, 8 insertions(+)
diff --git a/src/cwc.c b/src/cwc.c
index 408b0d5f..1c9eeb11 100644
--- a/src/cwc.c
+++ b/src/cwc.c
@@ -484,6 +484,9 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq)
pthread_mutex_unlock(&cwc->cwc_writer_mutex);
} else {
n = write(cwc->cwc_fd, buf, len);
+ if(n != len)
+ tvhlog(LOG_INFO, "cwc", "write error %s", strerror(errno));
+
free(cm);
}
return seq & 0xffff;
diff --git a/src/htsp.c b/src/htsp.c
index 09c9580e..608b5001 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -1248,8 +1248,13 @@ htsp_write_scheduler(void *aux)
/* 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);
pthread_mutex_lock(&htsp->htsp_out_mutex);
+ if(r != dlen)
+ break;
}
pthread_mutex_unlock(&htsp->htsp_out_mutex);
From 371ed5eeb5e05b6601fb87ebcf21579b86f51c72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 4 May 2011 13:45:56 +0200
Subject: [PATCH 65/72] Avoid assignment of write-only variables, silences
gcc4.6
---
src/dvb/dvb_tables.c | 44 ++++++++++++++++----------------------------
src/parser_h264.c | 4 ++--
src/teletext.c | 3 +--
3 files changed, 19 insertions(+), 32 deletions(-)
diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c
index a9ca19e6..738f1b4b 100644
--- a/src/dvb/dvb_tables.c
+++ b/src/dvb/dvb_tables.c
@@ -484,13 +484,7 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
uint16_t serviceid;
- int version;
- uint8_t section_number;
- uint8_t last_section_number;
uint16_t transport_stream_id;
- uint16_t original_network_id;
- uint8_t segment_last_section_number;
- uint8_t last_table_id;
uint16_t event_id;
time_t start_time, stop_time;
@@ -515,13 +509,13 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
return -1;
serviceid = ptr[0] << 8 | ptr[1];
- version = ptr[2] >> 1 & 0x1f;
- section_number = ptr[3];
- last_section_number = ptr[4];
+ // version = ptr[2] >> 1 & 0x1f;
+ // section_number = ptr[3];
+ // last_section_number = ptr[4];
transport_stream_id = ptr[5] << 8 | ptr[6];
- original_network_id = ptr[7] << 8 | ptr[8];
- segment_last_section_number = ptr[9];
- last_table_id = ptr[10];
+ // original_network_id = ptr[7] << 8 | ptr[8];
+ // segment_last_section_number = ptr[9];
+ // last_table_id = ptr[10];
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
@@ -644,15 +638,10 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
service_t *t;
- int version;
uint8_t section_number;
- uint8_t last_section_number;
uint16_t service_id;
uint16_t transport_stream_id;
- uint16_t original_network_id;
-
- int reserved;
- int running_status, free_ca_mode;
+ int free_ca_mode;
int dllen;
uint8_t dtag, dlen;
@@ -669,11 +658,11 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(tdmi->tdmi_transport_stream_id != transport_stream_id)
return -1;
- version = ptr[2] >> 1 & 0x1f;
+ // 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];
+ // last_section_number = ptr[4];
+ // original_network_id = ptr[5] << 8 | ptr[6];
+ // reserved = ptr[7];
if((ptr[2] & 1) == 0) {
/* current_next_indicator == next, skip this */
@@ -686,8 +675,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
while(len >= 5) {
service_id = ptr[0] << 8 | ptr[1];
- reserved = ptr[2];
- running_status = (ptr[3] >> 5) & 0x7;
+ // reserved = ptr[2];
+ // running_status = (ptr[3] >> 5) & 0x7;
free_ca_mode = (ptr[3] >> 4) & 0x1;
dllen = ((ptr[3] & 0x0f) << 8) | ptr[4];
@@ -841,7 +830,6 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
int tag, tlen;
- uint16_t caid;
uint16_t pid;
if((ptr[2] & 1) == 0) {
@@ -858,7 +846,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
len -= 2;
switch(tag) {
case DVB_DESC_CA:
- caid = ( ptr[0] << 8) | ptr[1];
+ // caid = ( ptr[0] << 8) | ptr[1];
pid = ((ptr[2] & 0x1f) << 8) | ptr[3];
if(pid == 0)
@@ -960,7 +948,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint16_t tsid)
{
int freq, symrate;
- uint16_t orbital_pos;
+ // uint16_t orbital_pos;
struct dvb_mux_conf dmc;
if(!tdmi->tdmi_adapter->tda_autodiscovery)
@@ -980,7 +968,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(!freq)
return -1;
- orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]);
+ // orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]);
symrate =
bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
diff --git a/src/parser_h264.c b/src/parser_h264.c
index 0ab7e944..c77fc2d1 100644
--- a/src/parser_h264.c
+++ b/src/parser_h264.c
@@ -348,7 +348,7 @@ h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype,
int *duration, int *isfield)
{
h264_private_t *p;
- int slice_type, pps_id, sps_id, fnum;
+ int slice_type, pps_id, sps_id;
if((p = st->es_priv) == NULL)
return -1;
@@ -378,7 +378,7 @@ h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype,
if(p->sps[sps_id].max_frame_num_bits == 0)
return -1;
- fnum = read_bits(bs, p->sps[sps_id].max_frame_num_bits);
+ skip_bits(bs, p->sps[sps_id].max_frame_num_bits);
int field = 0;
int timebase = 180000;
diff --git a/src/teletext.c b/src/teletext.c
index 86057d76..ff7a72db 100644
--- a/src/teletext.c
+++ b/src/teletext.c
@@ -375,7 +375,7 @@ tt_subtitle_deliver(service_t *t, elementary_stream_t *parent, tt_mag_t *ttm)
static void
tt_decode_line(service_t *t, elementary_stream_t *st, uint8_t *buf)
{
- uint8_t mpag, line, s12, s34, c;
+ uint8_t mpag, line, s12, c;
int page, magidx, i;
tt_mag_t *ttm;
tt_private_t *ttp;
@@ -416,7 +416,6 @@ tt_decode_line(service_t *t, elementary_stream_t *st, uint8_t *buf)
ttm->ttm_curpage = page;
s12 = ham_decode(buf[4], buf[5]);
- s34 = ham_decode(buf[6], buf[7]);
c = ham_decode(buf[8], buf[9]);
ttm->ttm_lang = c >> 5;
From 982802181c937cec8308c8608285712dc86dbcb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 4 May 2011 14:16:10 +0200
Subject: [PATCH 66/72] Drop another "write-only" variable
---
src/dvb/dvb_tables.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c
index 738f1b4b..f5b34b3d 100644
--- a/src/dvb/dvb_tables.c
+++ b/src/dvb/dvb_tables.c
@@ -638,7 +638,6 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
service_t *t;
- uint8_t section_number;
uint16_t service_id;
uint16_t transport_stream_id;
int free_ca_mode;
@@ -659,7 +658,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
return -1;
// version = ptr[2] >> 1 & 0x1f;
- section_number = ptr[3];
+ // section_number = ptr[3];
// last_section_number = ptr[4];
// original_network_id = ptr[5] << 8 | ptr[6];
// reserved = ptr[7];
From b197beb3e3f7b41a14b5787b2c54bae269fdad72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Wed, 4 May 2011 14:16:32 +0200
Subject: [PATCH 67/72] Correctly deal with write errors when delivering files
over HTTP
---
src/http.c | 12 +++++++-----
src/webui/webui.c | 4 ++--
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/http.c b/src/http.c
index 694f56ae..f56f5f22 100644
--- a/src/http.c
+++ b/src/http.c
@@ -333,7 +333,7 @@ http_access_verify(http_connection_t *hc, int mask)
* Returns 1 if we should disconnect
*
*/
-static void
+static int
http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
{
int err;
@@ -343,8 +343,12 @@ http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
else
err = hp->hp_callback(hc, remain, hp->hp_opaque);
+ if(err == -1)
+ return 1;
+
if(err)
http_error(hc, err);
+ return 0;
}
@@ -368,8 +372,7 @@ http_cmd_get(http_connection_t *hc)
if(args != NULL)
http_parse_get_args(hc, args);
- http_exec(hc, hp, remain);
- return 0;
+ return http_exec(hc, hp, remain);
}
@@ -431,8 +434,7 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
http_error(hc, HTTP_STATUS_NOT_FOUND);
return 0;
}
- http_exec(hc, hp, remain);
- return 0;
+ return http_exec(hc, hp, remain);
}
diff --git a/src/webui/webui.c b/src/webui/webui.c
index 7a696652..fc1b39ff 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -487,7 +487,6 @@ 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;
- int n;
if(remain == NULL)
return 404;
@@ -506,7 +505,8 @@ page_static_bundle(http_connection_t *hc, const char *remain, void *opaque)
fbe->original_size == -1 ? NULL : "gzip", NULL, 10, 0,
NULL);
/* ignore return value */
- n = write(hc->hc_fd, fbe->data, fbe->size);
+ if(write(hc->hc_fd, fbe->data, fbe->size) != fbe->size)
+ return -1;
return 0;
}
}
From f4756050ff811d3698b7955b0f9dfb6d31ceffa5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 10 May 2011 20:00:49 +0200
Subject: [PATCH 68/72] webui: Set default sortorder for channels to assigned
number
---
src/webui/static/app/chconf.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/webui/static/app/chconf.js b/src/webui/static/app/chconf.js
index 6bbc35bc..18476ec7 100644
--- a/src/webui/static/app/chconf.js
+++ b/src/webui/static/app/chconf.js
@@ -30,6 +30,7 @@ tvheadend.channels = new Ext.data.JsonStore({
fields: ['name', 'chid', 'xmltvsrc', 'tags', 'ch_icon',
'epg_pre_start', 'epg_post_end', 'number'],
id: 'chid',
+ sortInfo: { field: 'number', direction: "ASC" },
url: "channels",
baseParams: {
op: 'list'
From aaa86197bfd1d6bc92969ae0bd400b21e97f4786 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 10 May 2011 20:01:34 +0200
Subject: [PATCH 69/72] Change the M3U playlist return mime-type to
"audio/x-mpegurl"
According to (http://www.w3schools.com/media/media_mimeref.asp) this is more correct (there's no application/... variant).
Patch by cyberjunk
---
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 fc1b39ff..2d812615 100644
--- a/src/webui/webui.c
+++ b/src/webui/webui.c
@@ -262,7 +262,7 @@ http_stream_playlist(http_connection_t *hc, channel_t *channel)
}
}
- http_output_content(hc, "application/x-mpegURL");
+ http_output_content(hc, "audio/x-mpegurl");
pthread_mutex_unlock(&global_lock);
From a33441bfc684896adc91fa7bcbb7e2a7ec56c240 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 10 May 2011 20:04:26 +0200
Subject: [PATCH 70/72] Improve VLC player window
* Enable resize for player-window (inner vlc-plugin autoscales to 100%, allows movie-resize during playback)
* Fixes channel-switching (didn't work before here, it played always the same channel)
* Enables multiple vlc-playback-windows for parallel playback of more than 1 stream
* Adds a message to fullscreen-button click, that fullscreen mode is broken in VLC 1.1.x if VLC plugin 1.1.x is detected
(see http://forum.videolan.org/viewtopic.php?f=4&t=82721&p=274383, last known version to work: 1.0.5 <- that one has no DXVA for example :( )
* Now shows M3U and DirectStream URL if plugin is missing in BOTH cases (there were 2 in code)
Patch by cyberjunk
---
src/webui/static/app/tvheadend.js | 107 ++++++++++++++++--------------
1 file changed, 58 insertions(+), 49 deletions(-)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index b0a78647..25d9be98 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -31,24 +31,30 @@ tvheadend.help = function(title, pagename) {
* Displays a mediaplayer using VLC plugin
*/
tvheadend.VLC = function(url) {
+
+ function randomString() {
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
+ var string_length = 8;
+ var randomstring = '';
+ for (var i=0; iYou are missing a plugin for your browser.'
- innerHTML += 'You can still watch ' + chName;
- innerHTML += ' using an external player.
';
- innerHTML += 'M3U Playlist
';
- innerHTML += 'Direct URL
';
-
- missingPlugin.innerHTML = innerHTML;
- vlc.style.display = 'none';
- missingPlugin.style.display = 'block';
- return;
+ missingPlugin.innerHTML = 'Embedded player could not be started.
You are probably missing VLC Mozilla plugin for your browser.
';
+ missingPlugin.innerHTML += 'M3U Playlist
';
+ missingPlugin.innerHTML += 'Direct URL
';
}
-
- if(vlc.playlist && vlc.playlist.isPlaying) {
+ else {
vlc.playlist.stop();
- }
- if(vlc.playlist && vlc.playlist.items.count) {
- vlc.playlist.items.clear();
- }
-
- vlc.playlist.add(url, chName, "");
- vlc.playlist.play();
- vlc.audio.volume = slider.getValue();
+ vlc.playlist.items.clear();
+ vlc.playlist.add(streamurl);
+ vlc.playlist.playItem(0);
+ vlc.audio.volume = slider.getValue();
+ }
}
);
@@ -125,7 +116,7 @@ tvheadend.VLC = function(url) {
height: 384 + 56,
constrainHeader: true,
iconCls: 'eye',
- resizable: false,
+ resizable: true,
tbar: [
selectChannel,
'-',
@@ -151,9 +142,8 @@ tvheadend.VLC = function(url) {
iconCls: 'control_stop',
tooltip: 'Stop',
handler: function() {
- if(vlc.playlist && vlc.playlist.items.count) {
+ if(vlc.playlist) {
vlc.playlist.stop();
- vlc.style.display = 'none';
}
}
},
@@ -162,9 +152,12 @@ tvheadend.VLC = function(url) {
iconCls: 'control_fullscreen',
tooltip: 'Fullscreen',
handler: function() {
- if(vlc.playlist && vlc.playlist.isPlaying) {
+ if(vlc.playlist && vlc.playlist.isPlaying && (vlc.VersionInfo.substr(0,3) != '1.1')) {
vlc.video.toggleFullscreen();
}
+ else if (vlc.VersionInfo.substr(0,3) == '1.1') {
+ alert('Fullscreen mode is broken in VLC 1.1.x');
+ }
}
},
'-',
@@ -177,21 +170,37 @@ tvheadend.VLC = function(url) {
items: [vlc, missingPlugin]
});
- win.on('render', function() {
+ win.on('beforeShow', function() {
win.getTopToolbar().add(slider);
win.getTopToolbar().add(new Ext.Toolbar.Spacer());
win.getTopToolbar().add(new Ext.Toolbar.Spacer());
win.getTopToolbar().add(new Ext.Toolbar.Spacer());
win.getTopToolbar().add(sliderLabel);
- if(url && (!vlc.playlist || vlc.playlist == 'undefined')) {
+ // check if vlc plugin wasn't initialised correctly
+ if(!vlc.playlist || (vlc.playlist == 'undefined')) {
vlc.style.display = 'none';
+
+ missingPlugin.innerHTML = 'Embedded player could not be started.
You are probably missing VLC Mozilla plugin for your browser.
';
+
+ if (url) {
+ var channelid = url.substr(url.lastIndexOf('/'));
+ var streamurl = 'stream/channelid/' + channelid;
+ var playlisturl = 'playlist/channelid/' + channelid;
+ missingPlugin.innerHTML += 'M3U Playlist
';
+ missingPlugin.innerHTML += 'Direct URL
';
+ }
- var chUrl = 'the stream';
- missingPlugin.innerHTML = 'You are missing a plugin for your browser.
';
- missingPlugin.innerHTML += 'You can still watch ' + chUrl + ' using an external player.
';
missingPlugin.style.display = 'block';
}
+ else {
+ // check if the window was opened with an url-parameter
+ if (url) {
+ vlc.playlist.items.clear();
+ vlc.playlist.add(url);
+ vlc.playlist.playItem(0);
+ }
+ }
});
win.show();
From 1b05817d86d6b89e3336ff41e959f985cb0065dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 10 May 2011 20:05:57 +0200
Subject: [PATCH 71/72] webui: Fix path to when playing from DVR
---
src/webui/static/app/dvr.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js
index a2129b98..74de7cf2 100644
--- a/src/webui/static/app/dvr.js
+++ b/src/webui/static/app/dvr.js
@@ -69,7 +69,7 @@ tvheadend.dvrDetails = function(entry) {
content += '';
}
From 77cbb4ba34aa4ede333a4d631c692b4cb8953235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=96man?=
Date: Tue, 10 May 2011 20:07:05 +0200
Subject: [PATCH 72/72] webui: Enable YADIF in VLC if VLC version >1.1
---
src/webui/static/app/tvheadend.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js
index 25d9be98..5835b3f1 100644
--- a/src/webui/static/app/tvheadend.js
+++ b/src/webui/static/app/tvheadend.js
@@ -199,6 +199,14 @@ tvheadend.VLC = function(url) {
vlc.playlist.items.clear();
vlc.playlist.add(url);
vlc.playlist.playItem(0);
+
+ //enable yadif2x deinterlacer for vlc > 1.1
+ var point1 = vlc.VersionInfo.indexOf('.');
+ var point2 = vlc.VersionInfo.indexOf('.', point1+1);
+ var majVersion = vlc.VersionInfo.substring(0,point1);
+ var minVersion = vlc.VersionInfo.substring(point1+1,point2);
+ if ((majVersion >= 1) && (minVersion >= 1))
+ vlc.video.deinterlace.enable("yadif2x");
}
}
});