summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Lampard <mlampard@users.sourceforge.net>2004-12-13 10:34:49 +0000
committerMike Lampard <mlampard@users.sourceforge.net>2004-12-13 10:34:49 +0000
commitfb7eb9e819809be8a8da1518954403de91859a6d (patch)
tree5df39f0831a8d8aa2b5969e75b7132c4857bdcd7
parent6237a28d14eb8168b07f0e99d3dc583edf9abc2b (diff)
downloadxine-lib-fb7eb9e819809be8a8da1518954403de91859a6d.tar.gz
xine-lib-fb7eb9e819809be8a8da1518954403de91859a6d.tar.bz2
numerous bugfixes, code cleanup, new epg code, based on patch by Pekka
Jääskeläinen. CVS patchset: 7237 CVS date: 2004/12/13 10:34:49
-rw-r--r--src/input/input_dvb.c1198
1 files changed, 796 insertions, 402 deletions
diff --git a/src/input/input_dvb.c b/src/input/input_dvb.c
index c1d59729a..a30873cde 100644
--- a/src/input/input_dvb.c
+++ b/src/input/input_dvb.c
@@ -25,7 +25,7 @@
* - Use EIT info for current/next programming OSD
* - Pause recording
* - allow user to select card to use, rather than hard-code to adapter0
- * -
+ *
* TODO/Wishlist: (not in any order)
* - Parse all Administrative PIDs - NIT,SDT,CAT etc
* - As per James' suggestion, we need a way for the demuxer
@@ -56,6 +56,7 @@
#include "config.h"
#endif
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -109,12 +110,13 @@
#define AC3FILTER 7
#define TXTFILTER 8
-#define MAXFILTERS 9
+#define MAX_FILTERS 9
#define MAX_AUTOCHANNELS 200
#define MAX_SUBTITLES 4
+
/* Mouse button codes. */
#define MOUSE_BUTTON_LEFT 1
#define MOUSE_BUTTON_MIDDLE 2
@@ -128,14 +130,52 @@
/* EPG settings. */
-/* How many chars (max) of the program name to display. */
-#define MAX_PROGRAM_NAME_LENGTH 32
+/* define to have EPG come up on left mouse click */
+/*
+#define LEFT_MOUSE_DOES_EPG
+*/
+
+/* Width of the EPG OSD area. */
+#define EPG_WIDTH 520
+
+/* Height of the EPG OSD area. */
+#define EPG_HEIGHT 520
+
+/* Minimum top margin of the EPG in the video window. */
+#define EPG_TOP 50
+
+/* Font size of the channel name text. */
+#define EPG_CHANNEL_FONT_SIZE 32
+
+/* Font size of the header text (duration and program name). */
+#define EPG_TITLE_FONT_SIZE 24
+/* Font size of the content type and rating text. */
+#define EPG_CONTENT_FONT_SIZE 18
+
+/* Font size of the program description text. */
+#define EPG_DESCRIPTION_FONT_SIZE 18
+
+#define EPG_PIXELS_BETWEEN_TEXT_ROWS 2
+#define EPG_PIXELS_BETWEEN_PROGRAM_ENTRIES 2
+
+/* How many pixels the background of the OSD is bigger than the text area?
+ The margin is for each side of the background box. */
+#define EPG_BACKGROUND_MARGIN 5
+
+#define MAX_EPG_PROGRAM_NAME_LENGTH 255
+#define MAX_EPG_PROGRAM_DESCRIPTION_LENGTH 255
+#define MAX_EPG_CONTENT_TYPE_LENGTH 20
+#define MAX_EPG_ENTRIES_PER_CHANNEL 10
+
+/*
+#define DEBUG_LOAD_EIT
+*/
#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
typedef struct {
int fd_frontend;
- int fd_pidfilter[MAXFILTERS];
+ int fd_pidfilter[MAX_FILTERS];
int fd_subfilter[MAX_SUBTITLES];
struct dvb_frontend_info feinfo;
@@ -146,35 +186,52 @@ typedef struct {
char dvr_device[100];
char demux_device[100];
- struct dmx_pes_filter_params pesFilterParams[MAXFILTERS];
+ struct dmx_pes_filter_params pesFilterParams[MAX_FILTERS];
struct dmx_pes_filter_params subFilterParams[MAX_SUBTITLES];
- struct dmx_sct_filter_params sectFilterParams[MAXFILTERS];
+ struct dmx_sct_filter_params sectFilterParams[MAX_FILTERS];
xine_t *xine;
} tuner_t;
+
+/* EPG entry. */
typedef struct {
- /* EIT Information */
+
+ /* Program's name. */
char *progname;
+
+ /* Textual description of the program. */
char *description;
- char *starttime;
-
- char *duration;
+
+ /* The content type string. */
char *content;
+
+ /* Age recommendation. 0 if not available. */
int rating;
-} eit_info_t;
+
+ time_t starttime;
+
+ /* Program's duration in hours + minutes. */
+ char duration_hours;
+ char duration_minutes;
+
+ /* Running status information should be calculated automatically.
+ char running; */
+
+} epg_entry_t;
typedef struct {
char *name;
struct dvb_frontend_parameters front_param;
- int pid[MAXFILTERS];
+ int pid[MAX_FILTERS];
int subpid[MAX_SUBTITLES];
int service_id;
int sat_no;
int tone;
int pol;
int pmtpid;
- eit_info_t eit[2];
+ int epg_count;
+ epg_entry_t *epg[MAX_EPG_ENTRIES_PER_CHANNEL];
} channel_t;
typedef struct {
@@ -230,10 +287,11 @@ typedef struct {
int record_paused;
/* centre cutout zoom */
int zoom_ok;
- /* display channel name */
- int displaying;
+ /* Is EPG displaying? */
+ int epg_displaying;
+
/* buffer for EIT data */
- char *eitbuffer;
+ /*char *eitbuffer;*/
int num_streams_in_this_ts;
/* number of timedout reads in plugin_read */
int read_failcount;
@@ -305,8 +363,8 @@ static const Param transmissionmode_list [] = {
};
-struct tm * dvb_mjdtime (char *buf);
-static void do_eit(dvb_input_plugin_t *this);
+time_t dvb_mjdtime (char *buf);
+static void load_epg_data(dvb_input_plugin_t *this);
/* Utility Functions */
@@ -373,17 +431,17 @@ static int find_descriptor(uint8_t tag, const unsigned char *buf, int descriptor
return 0;
}
-/* extract UTC time and date encoded in modified julian date format and return pointer to tm
- * in localtime
+/* Extract UTC time and date encoded in modified julian date format and return it as a time_t.
*/
-struct tm * dvb_mjdtime (char *buf)
+time_t dvb_mjdtime (char *buf)
{
int i;
unsigned int year, month, day, hour, min, sec;
unsigned long int mjd;
struct tm *tma = xine_xmalloc(sizeof(struct tm));
- struct tm *dvb_time;
time_t t;
+
+ _x_assert(tma != NULL);
mjd = (unsigned int)(buf[0] & 0xff) << 8;
mjd +=(unsigned int)(buf[1] & 0xff);
@@ -408,11 +466,11 @@ struct tm * dvb_mjdtime (char *buf)
tma->tm_mon=month-1;
tma->tm_year=year;
- t=timegm(tma);
- dvb_time=localtime(&t);
+
+ t = timegm(tma);
free(tma);
- return dvb_time;
+ return t;
}
@@ -424,7 +482,7 @@ static void tuner_dispose(tuner_t * this)
close(this->fd_frontend);
/* close all pid filter filedescriptors */
- for (x = 0; x < MAXFILTERS; x++)
+ for (x = 0; x < MAX_FILTERS; x++)
if (this->fd_pidfilter[x] >= 0)
close(this->fd_pidfilter[x]);
@@ -445,11 +503,15 @@ static tuner_t *tuner_init(xine_t * xine, int adapter)
int x;
int test_video;
char *video_device=xine_xmalloc(200);
+
+ _x_assert(video_device != NULL);
this = (tuner_t *) xine_xmalloc(sizeof(tuner_t));
+
+ _x_assert(this != NULL);
this->fd_frontend = -1;
- for (x = 0; x < MAXFILTERS; x++)
+ for (x = 0; x < MAX_FILTERS; x++)
this->fd_pidfilter[x] = 0;
this->xine = xine;
@@ -472,7 +534,7 @@ static tuner_t *tuner_init(xine_t * xine, int adapter)
return NULL;
}
- for (x = 0; x < MAXFILTERS; x++) {
+ for (x = 0; x < MAX_FILTERS; x++) {
this->fd_pidfilter[x] = open(this->demux_device, O_RDWR);
if (this->fd_pidfilter[x] < 0) {
xprintf(this->xine, XINE_VERBOSITY_DEBUG, "DEMUX DEVICE PIDfilter: %s\n", strerror(errno));
@@ -722,7 +784,8 @@ static channel_t *load_channels(dvb_input_plugin_t *this, int *num_ch, fe_type_t
char filename[BUFSIZE];
channel_t *channels;
int num_channels;
- xine_t *xine = this->class->xine;
+ int i;
+ xine_t *xine = this->class->xine;
snprintf(filename, BUFSIZE, "%s/.xine/channels.conf", xine_get_homedir());
@@ -751,6 +814,8 @@ static channel_t *load_channels(dvb_input_plugin_t *this, int *num_ch, fe_type_t
channels = xine_xmalloc (sizeof (channel_t) * num_channels);
+ _x_assert(channels != NULL);
+
/*
* load channel list
*/
@@ -758,7 +823,13 @@ static channel_t *load_channels(dvb_input_plugin_t *this, int *num_ch, fe_type_t
f = fopen (filename, "rb");
num_channels = 0;
while ( fgets (str, BUFSIZE, f)) {
- if(extract_channel_from_string(&(channels[num_channels]),str,fe_type) < 0)continue;
+ if (extract_channel_from_string(&(channels[num_channels]),str,fe_type) < 0)
+ continue;
+
+ /* Initially there's no EPG data in the EPG structs. */
+ channels[num_channels].epg_count = 0;
+ for (i = 0; i < MAX_EPG_ENTRIES_PER_CHANNEL; ++i)
+ channels[num_channels].epg[i] = NULL;
num_channels++;
}
@@ -864,10 +935,10 @@ static int tuner_tune_it (tuner_t *this, struct dvb_frontend_parameters
xprintf(this->xine,XINE_VERBOSITY_LOG,"input_dvb: Signal/Noise Ratio: %i\n",strength);
if (event.status & FE_HAS_LOCK) {
- xprintf(this->xine,XINE_VERBOSITY_LOG,"input_dvb: Lock achieved at %.lu Hz\n",(unsigned long)front_param->frequency);
+ xprintf(this->xine,XINE_VERBOSITY_LOG,"input_dvb: Lock achieved at %lu Hz\n",(unsigned long)front_param->frequency);
return 1;
} else {
- xprintf(this->xine,XINE_VERBOSITY_LOG,"input_dvb: Unable to achieve lock at %.lu Hz\n",(unsigned long)front_param->frequency);
+ xprintf(this->xine,XINE_VERBOSITY_LOG,"input_dvb: Unable to achieve lock at %lu Hz\n",(unsigned long)front_param->frequency);
return 0;
}
@@ -982,6 +1053,9 @@ static void dvb_parse_si(dvb_input_plugin_t *this) {
tuner_t *tuner = this->tuner;
tmpbuffer = xine_xmalloc (8192);
+
+ _x_assert(tmpbuffer != NULL);
+
bufptr = tmpbuffer;
pfd.fd=tuner->fd_pidfilter[INTERNAL_FILTER];
@@ -1075,181 +1149,199 @@ static void dvb_parse_si(dvb_input_plugin_t *this) {
free(tmpbuffer);
}
+/* Helper function for finding the channel index in the channels struct
+ given the service_id. If channel is not found, -1 is returned. */
+static int channel_index(dvb_input_plugin_t* this, unsigned int service_id) {
+ int n;
+ for (n=0; n < this->num_channels; n++)
+ if (this->channels[n].service_id == service_id)
+ return n;
-/* this function parses the EIT table. It needs to be called at least twice - once for
- current program, once for next */
-static void do_eit(dvb_input_plugin_t *this)
-{
+ return -1;
+}
+
+static int compare_epg_by_starttime(const void* a, const void* b) {
+ const epg_entry_t **epg_a, **epg_b;
+ epg_a = (const struct epg_entry_t**)a;
+ epg_b = (const struct epg_entry_t**)b;
+
+ if ((*epg_a)->starttime < (*epg_b)->starttime) {
+ return -1;
+ } else if ((*epg_a)->starttime > (*epg_b)->starttime) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Finds the index of EPG entry with given starting time. If not found, returns -1. */
+static int epg_with_starttime(channel_t* channel, time_t starttime) {
+ int i;
+
+ for (i = 0; i < channel->epg_count; i++) {
+ if (channel->epg[i]->starttime == starttime)
+ return i;
+ }
+ return -1;
+}
+/* This function parses the EIT table and saves the data used in
+ EPG OSD of all channels found in the currently tuned stream. */
+static void load_epg_data(dvb_input_plugin_t *this)
+{
int table_id;
int descriptor_id;
- int section_len=0;
+ int section_len = 0;
unsigned int service_id=-1;
- int n,y,x;
- char *eit=NULL;
- char *foo=NULL;
+ int n;
+ char *eit = NULL;
+ char *foo = NULL;
+ char *seen_channels = NULL;
int text_len;
- int current_next=0;
- int running_status=0;
struct pollfd fd;
- char *buffer;
int loops;
- int current_channel;
+ int current_channel_index;
+ epg_entry_t* current_epg = NULL;
+ channel_t* current_channel = NULL;
+ int i;
+#ifdef DEBUG_LOAD_EIT
+ struct tm* starttime = NULL;
+#endif
+ /* seen_channels array is used to store information of channels that were
+ already "found" in the stream. This information is used to initialize the
+ channel's EPG structs when the EPG information for the channel is seen in
+ the stream the first time. */
+ seen_channels = xine_xmalloc(this->num_channels*sizeof(char));
+ _x_assert(seen_channels != NULL);
+ for (i = 0; i < this->num_channels; i++) {
+ seen_channels[i] = 0;
+ }
foo = xine_xmalloc(8192);
+ _x_assert(foo != NULL);
fd.fd = this->tuner->fd_pidfilter[EITFILTER];
fd.events = POLLPRI;
-
- /* reset all pointers to NULL - if there's info available, we'll find it */
- for(x=0;x<this->num_channels;x++){
- this->channels[x].eit[0].progname=NULL;
- this->channels[x].eit[0].description=NULL;
- this->channels[x].eit[0].starttime=NULL;
- this->channels[x].eit[0].duration=NULL;
- this->channels[x].eit[0].content=NULL;
- this->channels[x].eit[1].progname=NULL;
- this->channels[x].eit[1].description=NULL;
- this->channels[x].eit[1].starttime=NULL;
- this->channels[x].eit[1].duration=NULL;
- this->channels[x].eit[1].content=NULL;
- }
- /* we assume that because we are displaying the channel, service_id for this channel
- must be known. We should be accepting all information into the various channel
- structs, with a time value so we only really check once every five minutes or so
- per TS. Each section delivered by the driver contains info about 1 service. so we
- may need to read it multiple times to retrieve the one we're after. */
-
- for(loops=0;loops<=this->num_streams_in_this_ts*2;loops++){
- eit=foo;
+ for (loops = 0; loops <= this->num_streams_in_this_ts*2; loops++) {
+ eit = foo;
if (poll(&fd,1,2000)<1) {
- xprintf(this->stream->xine,XINE_VERBOSITY_LOG,"(TImeout in EPG loop!! Quitting\n");
+ xprintf(this->stream->xine,XINE_VERBOSITY_LOG,"(Timeout in EPG loop!! Quitting\n");
return;
}
- current_channel=-1;
- n = read (this->tuner->fd_pidfilter[EITFILTER],eit,3);
- table_id=getbits(eit,0,8);
- section_len=(unsigned int)getbits(eit,12,12);
- n = read (this->tuner->fd_pidfilter[EITFILTER],eit+3,section_len);
-
- service_id=(unsigned int)getbits(eit, 24, 16);
- for (n=0;n<this->num_channels;n++)
- if(this->channels[n].service_id==service_id)
- current_channel=n;
-
- current_next=getbits(foo,47,1);
-
- if(section_len>15){
+ n = read(this->tuner->fd_pidfilter[EITFILTER], eit, 3);
+ table_id = getbits(eit, 0, 8);
+ section_len = (unsigned int)getbits(eit, 12, 12);
+ n = read(this->tuner->fd_pidfilter[EITFILTER], eit + 3, section_len);
- /* do we have information about the current program, or the next? */
+ service_id = (unsigned int)getbits(eit, 24, 16);
+
+ if ((current_channel_index = channel_index(this, service_id)) == -1) {
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: load_epg_data(): unknown service_id: %d!\n", service_id);
+ continue;
+ }
+
+
+ if (section_len > 15) {
+ current_channel = &this->channels[current_channel_index];
+
+ /* Reset the EPG struct if this channel is seen the first time in the stream. */
+ if (!seen_channels[current_channel_index]) {
+ current_channel->epg_count = 0;
+ seen_channels[current_channel_index] = 1;
+ }
+
+ if (current_channel->epg_count >= MAX_EPG_ENTRIES_PER_CHANNEL) {
+
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: load_epg_data(): MAX_EPG_ENTRIES_PER_CHANNEL reached!\n");
+ continue;
+ }
+
+ /* Initialize the EPG struct if there's not one we can reuse.
+ Allocate space for the strings. */
+ if (current_channel->epg[current_channel->epg_count] == NULL) {
+ current_channel->epg[current_channel->epg_count] =
+ xine_xmalloc(sizeof(epg_entry_t));
+ _x_assert(current_channel->epg[current_channel->epg_count] != NULL);
+
+ current_channel->epg[current_channel->epg_count]->progname =
+ xine_xmalloc((MAX_EPG_PROGRAM_NAME_LENGTH + 1) * sizeof(char));
+ _x_assert(current_channel->epg[current_channel->epg_count]->progname != NULL);
+
+ current_channel->epg[current_channel->epg_count]->description =
+ xine_xmalloc((MAX_EPG_PROGRAM_DESCRIPTION_LENGTH + 1) * sizeof(char));
+ _x_assert(current_channel->epg[current_channel->epg_count]->description != NULL);
+
+ current_channel->epg[current_channel->epg_count]->content =
+ xine_xmalloc((MAX_EPG_CONTENT_TYPE_LENGTH + 1) * sizeof(char));
+ _x_assert(current_channel->epg[current_channel->epg_count]->content != NULL);
- if(getbits(foo,192,3) > 2){
- running_status=0; /* not currently running - must be next */
- } else {
- running_status=1; /* currently running */
}
- /* allocate an area in the eit buffer & clear it */
- buffer=this->eitbuffer+(current_channel*2048);
-
- y=running_status*1024;
- this->channels[current_channel].eit[running_status].progname=buffer+y;
- this->channels[current_channel].eit[running_status].description=buffer+y+256;
- this->channels[current_channel].eit[running_status].starttime=buffer+y+768;
- this->channels[current_channel].eit[running_status].duration=buffer+y+812;
- this->channels[current_channel].eit[running_status].content=buffer+y+900;
- memset(buffer+y,0,1024);
-
- /* gather up the start time and duration for this program unless duration is 0.
- no point in cluttering up the OSD... */
- if(bcdtoint(eit[21])+bcdtoint(eit[22])+bcdtoint(eit[23])>0){
- strftime(this->channels[current_channel].eit[running_status].starttime,21,"%a %l:%M%p",dvb_mjdtime(eit+16));
- snprintf(this->channels[current_channel].eit[running_status].duration,21,"%i:%02i",(char)bcdtoint(eit[21] & 0xff),(char)bcdtoint(eit[22] & 0xff));
+ current_epg = current_channel->epg[current_channel->epg_count];
+ current_epg->starttime = dvb_mjdtime(eit+16);
+
+ if (epg_with_starttime(current_channel, current_epg->starttime) != -1) {
+ /* Found already an entry with this starttime, let's not add it! */
+ continue;
}
- descriptor_id=eit[26];
- eit+=27;
- section_len-=27;
+
+ current_epg->duration_hours = (char)bcdtoint(eit[21] & 0xff);
+ current_epg->duration_minutes = (char)bcdtoint(eit[22] & 0xff);
+
+ descriptor_id = eit[26];
+ eit += 27;
+ section_len -= 27;
/* run the descriptor loop for the length of section_len */
- while (section_len>1)
+ while (section_len > 1)
{
- switch(descriptor_id)
- {
- case 0x4A: /* linkage descriptor */
- break;
+ switch(descriptor_id) {
case 0x4D: { /* simple program info descriptor */
int name_len;
int desc_len;
- xine_cfg_entry_t language;
+ xine_cfg_entry_t language;
+
desc_len = getbits(eit, 0, 8);
/* Let's get the EPG data only in the wanted language. */
- xine_config_lookup_entry(this->stream->xine, "media.dvd.language", &language);
+
- if (language.str_value && *language.str_value &&
+ if (xine_config_lookup_entry(this->stream->xine, "media.dvd.language", &language) &&
+ language.str_value && strlen(language.str_value) >= 2 &&
strncmp(language.str_value, &eit[1], 2)) {
- /*printf("### Skipping language: %C%C%C\n",eit[1],eit[2],eit[3]); */
+ /*
+ printf("input_dvb: EPG Skipping language: %C%C%C\n",eit[1],eit[2],eit[3]);
+ printf("input_dvb: EPG language.str_value: %s\n", language.str_value);
+ */
break;
+ } else {
+ /*
+ printf("input_dvb: EPG Using language: %C%C%C\n",eit[1],eit[2],eit[3]);
+ printf("input_dvb: EPG anguage.str_value: %s\n", language.str_value);
+ */
}
/* program name */
name_len = (unsigned char)eit[4];
- memcpy(this->channels[current_channel].eit[running_status].progname,
- eit + 5, name_len);
+ memcpy(current_epg->progname, eit + 5, name_len);
+ current_epg->progname[name_len] = '\0';
/* detailed program information (max 256 chars)*/
text_len = (unsigned char)eit[5+name_len];
- memcpy(this->channels[current_channel].eit[running_status].description,
- eit + 6 + name_len, text_len);
+ memcpy(current_epg->description, eit + 6 + name_len, text_len);
+ current_epg->description[text_len] = '\0';
}
break;
- case 0x4E: {
- /* extended descriptor - not currently used as the simple descriptor gives us enough for now
- and the only broadcaster in my locale using it sends an empty one... */
- /*
- int item_desc_len;
- int total_item_len;
- int y;
- int desc_len;
-
- desc_len=getbits(eit,0,8);
-
- printf("Descriptornum: %i\n",getbits(eit,8,4));
- printf("LastDescNum: %i\n",getbits(eit,12,4));
- printf("Language: %C%C%C\n",(char)getbits(eit,16,8),(char)getbits(eit,24,8),(char)getbits(eit,32,8));
-
- total_item_len = getbits(eit,40,8);
- item_desc_len = getbits(eit,48,8);
-
- if(item_desc_len==1)
- printf("-");
- else
- for(y=0;y<item_desc_len;y++)
- printf("%c",eit[y]);
- printf("\n");
- */
- }
- break;
- case 0x4F: /* timeshifted event not used */
- break;
- case 0x50:{ /* video content descriptor nothing here we can reliably use */
- /* if(getbits(eit,12,4)==1)
- printf("Video Content - flags are %X\n",getbits(eit,16,8)); */
- }
- break;
- case 0x53: /* CA descriptor */
- break;
+
case 0x54: { /* Content Descriptor, riveting stuff */
- int content_bits=getbits(eit,8,4);
+ int content_bits = getbits(eit, 8, 4);
char *content[] = {
"UNKNOWN","MOVIE","NEWS","ENTERTAINMENT","SPORT",
"CHILDRENS","MUSIC","ARTS/CULTURE","CURRENT AFFAIRS",
"EDUCATIONAL","INFOTAINMENT","SPECIAL","COMEDY","DRAMA",
"DOCUMENTARY","UNK"};
- snprintf(this->channels[current_channel].eit[running_status].content, 40,
- content[content_bits]);
+ snprintf(current_epg->content, MAX_EPG_CONTENT_TYPE_LENGTH, content[content_bits]);
}
break;
case 0x55: { /* Parental Rating descriptor describes minimum recommened age -3 */
@@ -1261,196 +1353,418 @@ static void do_eit(dvb_input_plugin_t *this)
greater than 0xF are "defined by broadcaster", which is not supported
for now. */
if (eit[4] > 0 && eit[4] <= 0xF)
- this->channels[current_channel].eit[running_status].rating = eit[4] + 3;
+ current_epg->rating = eit[4] + 3;
else
- this->channels[current_channel].eit[running_status].rating = 0;
+ current_epg->rating = 0;
}
break;
- case 0x57: /* telephone descriptor not used */
- break;
- case 0x5E: /* multilingual component descriptor - we should be using this... */
- break;
- case 0x5F: /* private data specifier */
- case 0x61: /* short smoothing buffer should use this to reduce buffers when avail */
- case 0x64: /* data broadcast descriptor */
- case 0x69: /* PDC descriptor?? */
- case 0x75: /* TVA_ID descriptor */
- case 0x76: /* content identifier */
default:
break;
}
- section_len-=getbits(eit,0,8)+2;
- eit+=getbits(eit,0,8);
- descriptor_id=eit[1];
- eit+=2;
+ section_len -= getbits(eit, 0, 8) + 2;
+ eit += getbits(eit, 0, 8);
+ descriptor_id = eit[1];
+ eit += 2;
}
+#ifdef DEBUG_LOAD_EIT
+ if (current_channel_index == this->channel) {
+ starttime =
+ localtime(&this->channels[current_channel_index].epg[current_channel->epg_count]->starttime);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"\n##############################################################\n");
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG current_channel = %s\n", this->channels[current_channel_index].name);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG start time: %d:%d\n", starttime->tm_hour, starttime->tm_min);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG program name: \"%s\"\n",
+ this->channels[current_channel_index].epg[current_channel->epg_count]->progname);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG description: \"%s\"\n",
+ this->channels[current_channel_index].epg[current_channel->epg_count]->description);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG content type: \"%s\"\n",
+ this->channels[current_channel_index].epg[current_channel->epg_count]->content);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG age recommendation: \"%d\"\n",
+ this->channels[current_channel_index].epg[current_channel->epg_count]->rating);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG duration: \"%dh%dmin\"\n",
+ this->channels[current_channel_index].epg[current_channel->epg_count]->duration_hours,
+ this->channels[current_channel_index].epg[current_channel->epg_count]->duration_minutes);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"##############################################################\n");
+ }
+#endif
+ /* OK, we got all the data for a new EPG entry. */
+ current_channel->epg_count++;
+
}
}
+ /* Sort the EPG arrays by starttime. */
+ for (i = 0; i < this->num_channels; ++i) {
+ if (!seen_channels[i])
+ continue;
+ qsort(this->channels[i].epg, this->channels[i].epg_count,
+ sizeof(epg_entry_t*), compare_epg_by_starttime);
+ }
+ free(seen_channels);
free(foo);
}
-static void show_eit(dvb_input_plugin_t *this) {
-
- char *line;
- char *description;
- int x,y,ok;
+/* Prints text to an area, tries to cut the lines in between words. */
+/* TODO: boundary checking (assure not writing beyond osd screen limits). */
+/* TODO: split a word if it's too long to fit in a line. */
+static void render_text_area(osd_renderer_t* renderer, osd_object_t* osd, char* text,
+ int x, int y, int max_x, int max_y, int* height, int color_base) {
+
+ /* The position of the text to be printed. */
+ char* cursor = text;
+
+ /* The line to be printed next. */
+ char text_line[512];
+ int text_width, text_height;
+ int old_line_length, line_cursor;
+ char* bound, *old_bound;
+
+ *height = 0;
+ while (cursor < text + strlen(text)) {
+
+ bound = cursor;
+ line_cursor = 0;
+ text_line[0] = '\0';
+ /* Find out how much fits in a row. */
+ do {
+
+ /* Find out the next word boundary. */
+ old_bound = bound;
+ old_line_length = strlen(text_line);
+ line_cursor = old_line_length;
+
+ /* Strip leading white space. */
+ while (isspace(*bound))
+ bound++;
+
+ /* Copy text to the text_line until end of word or end of string. */
+ while (!isspace(*bound) && *bound != '\0') {
+ text_line[line_cursor] = *bound;
+ bound++;
+ line_cursor++;
+ }
- if(this->displaying==0){
- this->displaying=1;
+ text_line[line_cursor++] = ' ';
+ text_line[line_cursor] = '\0';
- line=xine_xmalloc(512);
+ /* Try if the line with a new word still fits to the given area. */
+ renderer->get_text_size(osd, text_line, &text_width, &text_height);
+ if (x + text_width > max_x) {
+ /* It didn't fit, restore the old line and stop trying to fit more.*/
+ text_line[old_line_length] = '\0';
+ bound = old_bound;
+ break;
+ }
- this->stream->osd_renderer->clear(this->proginfo_osd);
- /* Channel Name */
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- this->stream->osd_renderer->render_text (this->proginfo_osd,350,150,"Searching for Info",OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
+ /* OK, it did fit, let's try to fit some more. */
+
+ } while (bound < text + strlen(text));
- this->stream->osd_renderer->show (this->background, 0);
- this->stream->osd_renderer->show_unscaled (this->proginfo_osd, 0);
+ renderer->render_text(osd, x, y, text_line, color_base);
+ *height += text_height + EPG_PIXELS_BETWEEN_TEXT_ROWS;
+ y += text_height + EPG_PIXELS_BETWEEN_TEXT_ROWS;
+ cursor = bound;
+ }
+}
- do_eit(this);
+/* For debugging. Prints time in human readable form to log. */
+static void print_time(time_t time_to_print) {
+ struct tm* timetime;
+ timetime = localtime(&time_to_print);
- this->stream->osd_renderer->hide (this->proginfo_osd, 0);
+ printf("%d:%d\n", timetime->tm_hour, timetime->tm_min);
- this->stream->osd_renderer->clear(this->proginfo_osd);
- /* Channel Name */
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- this->stream->osd_renderer->render_text (this->proginfo_osd,15,10,this->channels[this->channel].name,OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
+}
+
+/* Finds the EPG of the ith next program. 0 means the current program, 1 next.
+ If not found, returns NULL. All these functions expect the EPG entries
+ are sorted by starting time. */
+static epg_entry_t* ith_next_epg(channel_t* channel, int count) {
+ time_t current_time = time(NULL);
+ int counter = 0;
+#if 0
+ /* For testing: */
+ static int clock_addition = 0;
- this->stream->osd_renderer->render_text (this->proginfo_osd, 15, 50, "NOW:",OSD_TEXT3);
- this->stream->osd_renderer->render_text (this->proginfo_osd, 15, 300, "NEXT:",OSD_TEXT3);
+ current_time += clock_addition;
+ clock_addition += 10*60;
+
+ printf ("input_dvb: current_time: ");
+ print_time(current_time);
+
+ if (channel->epg_count == 0)
+ return NULL;
+
+ printf("input_dvb: EPG current_time: %d\n", current_time);
+ printf ("input_dvb: EPG channel->epg[counter + 1]->starttime: %d\n", channel->epg[counter + 1]->starttime);
- if(this->channels[this->channel].eit[0].progname!=NULL)
- {
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- /* we trim the program name down to 23 chars so it'll fit although with kerning you
- just don't know */
- snprintf(line, MAX_PROGRAM_NAME_LENGTH, "%s",
- this->channels[this->channel].eit[0].progname);
- this->stream->osd_renderer->render_text (this->proginfo_osd, 100, 46, line,OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
-
- /*start time and duration */
- y=0;
- if(strlen(this->channels[this->channel].eit[0].starttime)>3){
- snprintf(line,100,"%s (%s)",this->channels[this->channel].eit[0].starttime, this->channels[this->channel].eit[0].duration);
- if(strlen(this->channels[this->channel].eit[0].content)>3)
- y=15; /* offset vertically */
- this->stream->osd_renderer->render_text (this->proginfo_osd, 670, 50+y, line,OSD_TEXT3);
- }
- /*Content Type and Rating if any*/
- if(strlen(this->channels[this->channel].eit[0].content)>3){
- int prog_rating;
- snprintf(line, 94, "%s", this->channels[this->channel].eit[0].content);
-
- prog_rating = this->channels[this->channel].eit[0].rating;
- if (prog_rating > 0) {
- snprintf(line + strlen(line), 7, " (%i+)", prog_rating);
- }
-
- this->stream->osd_renderer->render_text (this->proginfo_osd, 670, 20+y, line,OSD_TEXT3);
- }
- /* some quick'n'dirty formatting to keep words whole */
- ok=0; y=0;
- description=this->channels[this->channel].eit[0].description;
- while(ok<250){
- x=65;
- while(getbits(description,x*8,8)>' ' && getbits(description,x*8,8)<254){
- x--;
- }
- x++;
- ok+=x;
- snprintf(line,x,"%s",description);
- this->stream->osd_renderer->render_text (this->proginfo_osd, 55, 110+(y),line,OSD_TEXT3);
- description+=x;
- y+=30;
- }
- } else {
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- /* we trim the program name down so it'll fit although with kerning you just
- don't know */
- snprintf(line, MAX_PROGRAM_NAME_LENGTH, "%s", "No Information Available");
- this->stream->osd_renderer->render_text (this->proginfo_osd, 100, 46, line,OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
+ if (difftime(channel->epg[counter + 1]->starttime, current_time) < 0.0)
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,"input_dvb: EPG YEAH.\n");
+#endif
+
+ /* Discard the entries of past programs. */
+ while (counter + 1 < channel->epg_count &&
+ difftime(channel->epg[counter + 1]->starttime, current_time) < 0.0)
+ counter++;
+
+#ifdef DEBUG_LOAD_EIT
+ if (counter > 0)
+ printf ("input_dvb: EPG discarded %d EPG(s)\n", counter);
+#endif
+ counter += count;
+
+ if (counter >= channel->epg_count)
+ return NULL;
+
+ return channel->epg[counter];
+}
+
+/* Finds the EPG of the current program. If not found, returns NULL. */
+static epg_entry_t* current_epg(channel_t* channel) {
+ return ith_next_epg(channel, 0);
+}
+
+/* Finds the EPG of the next program. If not found, returns NULL. */
+static epg_entry_t* next_epg(channel_t* channel) {
+ return ith_next_epg(channel, 1);
+}
+
+
+/* EPG TODO:
+
+ - the first lines of the program name and description are
+ indented for some reason -> try to remove indentation
+ - fetch more program infos from the stream if there are some
+ - cache for program infos, fetch only when necessary
+
+ CHANNEL LIST TODO:
+ - current program visible in channel selector when found in the cache
+*/
+
+/* Displays the program info of an EPG entry in OSD.
+
+ x,y The upper left coordinates of the program information area.
+ max_x, max_y The maximum right coordinate of the program information area.
+ last_y The position of y after printing the entry.
+ data The EPG entry to display.
+
+ Returns the height of the entry in the OSD in pixels.
+*/
+static void show_program_info(int x, int y, int max_x, int max_y, int* last_y,
+ epg_entry_t* epg_data, osd_renderer_t* renderer,
+ osd_object_t* osd) {
+ char* buffer;
+ int time_width, text_width, dummy;
+ int content_width = 0;
+ int text_height = 0;
+ int time_height = 0;
+ int prog_rating;
+ struct tm* starttime = NULL;
+
+ *last_y = y;
+
+ if (epg_data == NULL || epg_data->progname == NULL)
+ return;
+
+ buffer = xine_xmalloc(512);
+
+ _x_assert(buffer != NULL);
+
+ renderer->set_font(osd, "sans", EPG_TITLE_FONT_SIZE);
+
+ starttime = localtime(&epg_data->starttime);
+ strftime(buffer, 7, "%H:%M ", starttime);
+
+ /* Print the starting time. */
+ renderer->render_text(osd, x, y, buffer, OSD_TEXT3);
+ renderer->get_text_size(osd, buffer, &time_width, &time_height);
+
+ /*Content type and rating, if any. */
+ if (strlen(epg_data->content) > 3) {
+
+ snprintf(buffer, 94, "%s", epg_data->content);
+
+ prog_rating = epg_data->rating;
+ if (prog_rating > 0) {
+ snprintf(buffer + strlen(buffer), 7, " (%i+)", prog_rating);
}
+ renderer->set_font(osd, "sans", EPG_CONTENT_FONT_SIZE);
+ renderer->get_text_size(osd, buffer, &content_width, &dummy);
+ renderer->render_text(osd, max_x - 2 - content_width, y, buffer, OSD_TEXT3);
+ }
- if(this->channels[this->channel].eit[1].progname!=NULL)
- {
- /* and now the next program */
- snprintf(line, MAX_PROGRAM_NAME_LENGTH, "%s",
- this->channels[this->channel].eit[1].progname);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- this->stream->osd_renderer->render_text (this->proginfo_osd, 100, 296, line,OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
-
- y=0;
- if(strlen(this->channels[this->channel].eit[1].starttime)>3){
- snprintf(line,100,"%s (%s)",this->channels[this->channel].eit[1].starttime, this->channels[this->channel].eit[1].duration);
- if(strlen(this->channels[this->channel].eit[1].content)>3)
- y=15; /* offset vertically */
- this->stream->osd_renderer->render_text (this->proginfo_osd, 670, 300+y, line,OSD_TEXT3);
- }
- /*Content Type and Rating if any*/
- if(strlen(this->channels[this->channel].eit[1].content)>3){
- int prog_rating;
- snprintf(line, 94, "%s", this->channels[this->channel].eit[1].content);
-
- prog_rating = this->channels[this->channel].eit[1].rating;
- if (prog_rating > 0) {
- snprintf(line + strlen(line), 7, " (%i+)", prog_rating);
- }
-
+ text_width = max_x - x - time_width - content_width - 2;
- this->stream->osd_renderer->render_text (this->proginfo_osd, 670, 270+y, line,OSD_TEXT3);
- }
- /* some quick'n'dirty formatting to keep words whole */
- ok=0; y=0;
- description=this->channels[this->channel].eit[1].description;
- while( ok < 250 ) {
- x=65;
- while(getbits(description,x*8,8)>' ' && getbits(description,x*8,8)<254){
- x--;
- }
- x++;
- ok+=x;
- snprintf(line,x,"%s",description);
- this->stream->osd_renderer->render_text (this->proginfo_osd, 55, 360+(y),line,OSD_TEXT3);
- description+=x;
- y+=30;
- }
+ renderer->set_font(osd, "sans", EPG_TITLE_FONT_SIZE);
+
+ render_text_area(renderer, osd, epg_data->progname,
+ x + time_width, y, x + text_width + time_width, y + 300, &text_height,
+ OSD_TEXT4);
+
+ if (text_height == 0)
+ *last_y = y + time_height;
+ else
+ *last_y = y + text_height;
+
+ /* Print the description. */
+ if (epg_data->description && strlen(epg_data->description) > 0) {
+ renderer->set_font(osd, "sans", EPG_DESCRIPTION_FONT_SIZE);
+ sprintf(buffer, "%s", epg_data->description);
+ /* If the description is not complete (i.e., there is no comma at the end),
+ add "..." to the end. In my locale they often seem to send incomplete description
+ texts :( */
+ if (buffer[strlen(buffer)-1] != '.' && buffer[strlen(buffer)-1] != '?' &&
+ buffer[strlen(buffer)-1] != '!') {
+ strcat(buffer, "...");
+ }
+
+ /* If duration_hours is zero, do not print them. */
+ if (epg_data->duration_hours > 0)
+ sprintf(buffer + strlen(buffer), " (%dh%02dmin)",
+ epg_data->duration_hours, epg_data->duration_minutes);
+ else if (epg_data->duration_minutes > 0)
+ sprintf(buffer + strlen(buffer), " (%dmin)",
+ epg_data->duration_minutes);
+
+ render_text_area(renderer, osd, buffer, x + time_width,
+ *last_y + EPG_PIXELS_BETWEEN_TEXT_ROWS,
+ max_x, 300, &text_height, OSD_TEXT3);
+
+ *last_y += EPG_PIXELS_BETWEEN_TEXT_ROWS + text_height;
+ }
+
+ free(buffer);
+}
+
+/* Shows the EPG listing for the current channel. */
+static void show_eit(dvb_input_plugin_t *this) {
+ int y;
+ int centered_x, centered_y;
+ int y_pos = 0;
+ int window_width, window_height, stream_width, stream_height;
+
+ if (!this->epg_displaying) {
+
+ this->epg_displaying = 1;
+
+ /* Only load new EPG data if there's none in the cache. */
+ if (current_epg(&this->channels[this->channel]) == NULL) {
+
+ this->stream->osd_renderer->clear(this->proginfo_osd);
+ this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
+ this->stream->osd_renderer->render_text(
+ this->proginfo_osd, 0, 0, "Searching for Info", OSD_TEXT4);
+ this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
+
+ this->stream->osd_renderer->show(this->background, 0);
+ this->stream->osd_renderer->show(this->proginfo_osd, 0);
+
+ load_epg_data(this);
+ }
+
+
+ this->stream->osd_renderer->hide(this->proginfo_osd, 0);
+
+ this->stream->osd_renderer->clear(this->proginfo_osd);
+
+ /* Channel Name */
+ this->stream->osd_renderer->set_font(
+ this->proginfo_osd, "sans", EPG_CHANNEL_FONT_SIZE);
+ this->stream->osd_renderer->render_text(
+ this->proginfo_osd, 0, 0, this->channels[this->channel].name, OSD_TEXT4);
+
+ show_program_info(0, EPG_CHANNEL_FONT_SIZE + 2, EPG_WIDTH, EPG_HEIGHT, &y_pos,
+ current_epg(&this->channels[this->channel]),
+ this->stream->osd_renderer,
+ this->proginfo_osd);
+ y = y_pos;
+
+ show_program_info(0, y, EPG_WIDTH, EPG_HEIGHT, &y_pos,
+ next_epg(&this->channels[this->channel]),
+ this->stream->osd_renderer,
+ this->proginfo_osd);
+ y = y_pos;
+
+ window_width =
+ this->stream->video_out->get_property(
+ this->stream->video_out, VO_PROP_WINDOW_WIDTH);
+
+ window_height =
+ this->stream->video_out->get_property(
+ this->stream->video_out, VO_PROP_WINDOW_HEIGHT);
+
+ stream_width =
+ xine_get_stream_info(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH);
+
+ stream_height =
+ xine_get_stream_info(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
+
+ /* Resize the background to be the size of the OSD texts + margins. */
+ this->stream->osd_renderer->clear(this->background);
+ this->stream->osd_renderer->set_font(this->background, "cetus", 32);
+ this->stream->osd_renderer->set_encoding(this->background, NULL);
+ this->stream->osd_renderer->set_text_palette(
+ this->background, XINE_TEXTPALETTE_YELLOW_BLACK_TRANSPARENT, OSD_TEXT3);
+ this->stream->osd_renderer->filled_rect(
+ this->background, 0, 0,
+ EPG_WIDTH + EPG_BACKGROUND_MARGIN*2,
+ y + EPG_BACKGROUND_MARGIN*2, 4);
+
+ /* In case video is downscaled and the EPG fits, show it unscaled to make it
+ appear bigger thus more readable. NOT FULLY TESTED. */
+ if (stream_width > window_width && window_width > EPG_WIDTH) {
+
+ centered_x = (window_width - EPG_WIDTH) / 2;
+ centered_x = (centered_x > 0)?(centered_x):(0);
+
+ centered_y = (window_height - y) / 3;
+ centered_y = (centered_y > 0)?(centered_y):(EPG_TOP);
+
+ this->stream->osd_renderer->set_position(
+ this->proginfo_osd,
+ centered_x + EPG_BACKGROUND_MARGIN,
+ centered_y + EPG_BACKGROUND_MARGIN);
+
+ this->stream->osd_renderer->set_position(this->background, centered_x, centered_y);
+ this->stream->osd_renderer->show_unscaled(this->background, 0);
+ this->stream->osd_renderer->show_unscaled(this->proginfo_osd, 0);
} else {
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 32);
- /* we trim the program name down to 23 chars so it'll fit although with kerning you just don't know */
- snprintf(line,28,"%s","No Information Available");
- this->stream->osd_renderer->render_text (this->proginfo_osd, 100, 296, line,OSD_TEXT4);
- this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
+ /* Otherwise make it scaled. */
+ centered_x = (stream_width - EPG_WIDTH) / 2;
+ centered_x = (centered_x > 0)?(centered_x):(0);
+
+ centered_y = (stream_height - y) / 3;
+ centered_y = (centered_y > 0)?(centered_y):(EPG_TOP);
+
+ /* Center the OSD to stream. */
+ this->stream->osd_renderer->set_position(
+ this->proginfo_osd,
+ centered_x + EPG_BACKGROUND_MARGIN,
+ centered_y + EPG_BACKGROUND_MARGIN);
+
+ this->stream->osd_renderer->set_position(this->background, centered_x, centered_y);
+ this->stream->osd_renderer->show(this->background, 0);
+ this->stream->osd_renderer->show(this->proginfo_osd, 0);
}
- this->stream->osd_renderer->show(this->background,0);
- this->stream->osd_renderer->show_unscaled (this->proginfo_osd, 0);
-/*
- vpts=this->stream->xine->clock->get_current_time(this->stream->xine->clock);
- vpts+=1080000;
- this->stream->osd_renderer->hide (this->proginfo_osd,vpts);
- this->stream->osd_renderer->hide (this->background,vpts);
-*/
- free(line);
+ /* Alright, were are now showing stuff to the user so we can load more EPG
+ data "secretly" in case there's only one entry in EPG anymore ;) */
+ if (next_epg(&this->channels[this->channel]) == NULL)
+ load_epg_data(this);
+
} else {
- this->displaying=0;
+ this->epg_displaying = 0;
this->stream->osd_renderer->hide (this->proginfo_osd,0);
this->stream->osd_renderer->hide (this->background,0);
}
-
+
return;
}
-
-static int tuner_set_channel (dvb_input_plugin_t *this,
- channel_t *c) {
+static int tuner_set_channel (dvb_input_plugin_t *this, channel_t *c) {
tuner_t *tuner=this->tuner;
+ xine_cfg_entry_t lastchannel;
+ config_values_t *config = this->stream->xine->config;
if (tuner->feinfo.type==FE_QPSK) {
if(!(tuner->feinfo.caps & FE_CAN_INVERSION_AUTO))
@@ -1459,28 +1773,34 @@ static int tuner_set_channel (dvb_input_plugin_t *this,
return 0;
}
- if (!tuner_tune_it (tuner, &c->front_param))
+ if (!tuner_tune_it (tuner, &c->front_param)){
return 0;
-
+ }
+
+ if (xine_config_lookup_entry(this->stream->xine, "media.dvb.remember_channel", &lastchannel))
+ if (lastchannel.num_value){
+ /* Remember last watched channel. never show this entry*/
+ config->update_num(config, "media.dvb.last_channel", this->channel+1);
+ }
+
return 1; /* fixme: error handling */
}
+static void osd_show_channel (dvb_input_plugin_t *this, int channel) {
-static void osd_show_channel (dvb_input_plugin_t *this) {
-
- int i, channel ;
+ int i, channel_to_print;
this->stream->osd_renderer->filled_rect (this->channel_osd, 0, 0, 395, 400, 2);
- channel = this->channel - 5;
+ channel_to_print = channel - 5;
for (i=0; i<11; i++) {
- if ( (channel >= 0) && (channel < this->num_channels) )
- this->stream->osd_renderer->render_text (this->channel_osd, 110, 10+i*35,
- this->channels[channel].name,
+ if ( (channel_to_print >= 0) && (channel_to_print < this->num_channels) )
+ this->stream->osd_renderer->render_text(this->channel_osd, 110, 10+i*35,
+ this->channels[channel_to_print].name,
OSD_TEXT3);
- channel ++;
+ channel_to_print ++;
}
this->stream->osd_renderer->line (this->channel_osd, 105, 183, 390, 183, 10);
@@ -1490,22 +1810,21 @@ static void osd_show_channel (dvb_input_plugin_t *this) {
this->stream->osd_renderer->show (this->channel_osd, 0);
/* hide eit if showing */
- if (this->displaying==1) {
+ if (this->epg_displaying==1) {
this->stream->osd_renderer->hide (this->proginfo_osd,0);
this->stream->osd_renderer->hide (this->background,0);
}
}
-
-static void switch_channel (dvb_input_plugin_t *this) {
+static void switch_channel(dvb_input_plugin_t *this, int channel) {
int x;
xine_event_t event;
xine_pids_data_t data;
xine_ui_data_t ui_data;
-
- /* control_nop appears to stop an occasional (quite long) pause between channel-changes, which the user
- may see as a lockup. */
+
+ /* control_nop appears to stop an occasional (quite long) pause between
+ channel-changes, which the user may see as a lockup. */
_x_demux_control_nop(this->stream, BUF_FLAG_END_STREAM);
_x_demux_flush_engine(this->stream);
@@ -1513,20 +1832,21 @@ static void switch_channel (dvb_input_plugin_t *this) {
close (this->fd);
- for (x = 0; x < MAXFILTERS; x++) {
+ for (x = 0; x < MAX_FILTERS; x++) {
close(this->tuner->fd_pidfilter[x]);
this->tuner->fd_pidfilter[x] = open(this->tuner->demux_device, O_RDWR);
}
-
- if (!tuner_set_channel (this, &this->channels[this->channel])) {
- xprintf (this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: tuner_set_channel failed\n"));
+
+ if (!tuner_set_channel (this, &this->channels[channel])) {
+ xprintf (this->class->xine, XINE_VERBOSITY_LOG,
+ _("input_dvb: tuner_set_channel failed\n"));
pthread_mutex_unlock (&this->mutex);
return;
}
event.type = XINE_EVENT_PIDS_CHANGE;
- data.vpid = this->channels[this->channel].pid[VIDFILTER];
- data.apid = this->channels[this->channel].pid[AUDFILTER];
+ data.vpid = this->channels[channel].pid[VIDFILTER];
+ data.apid = this->channels[channel].pid[AUDFILTER];
event.data = &data;
event.data_length = sizeof (xine_pids_data_t);
@@ -1534,8 +1854,7 @@ static void switch_channel (dvb_input_plugin_t *this) {
xine_event_send (this->stream, &event);
- snprintf (ui_data.str, 256, "%04d - %s", this->channel,
- this->channels[this->channel].name);
+ snprintf (ui_data.str, 256, "%04d - %s", channel, this->channels[channel].name);
ui_data.str_len = strlen (ui_data.str);
_x_meta_info_set(this->stream, XINE_META_INFO_TITLE, ui_data.str);
@@ -1550,20 +1869,21 @@ static void switch_channel (dvb_input_plugin_t *this) {
this->fd = open (this->tuner->dvr_device, O_RDONLY | O_NONBLOCK);
+ this->channel = channel;
+
pthread_mutex_unlock (&this->mutex);
- /* now read the pat,find all accociated PIDs and add them to the stream */
+ /* now read the pat, find all accociated PIDs and add them to the stream */
dvb_parse_si(this);
- this->stream->osd_renderer->hide(this->channel_osd,0);
+ this->stream->osd_renderer->hide(this->channel_osd, 0);
/* show eit for this channel if necessary */
- if(this->displaying==1){
- this->displaying=0;
+ if(this->epg_displaying==1){
+ this->epg_displaying=0;
show_eit(this);
}
}
-
static void do_record (dvb_input_plugin_t *this) {
struct tm *tma;
@@ -1583,10 +1903,13 @@ static void do_record (dvb_input_plugin_t *this) {
this->record_paused=0;
} else {
t=xine_xmalloc(sizeof(time_t));
+
+ _x_assert(t != NULL);
+
time(t);
tma=localtime(t);
free(t);
- strftime(dates,63,"%F_%H%M",tma);
+ strftime(dates,63,"%Y-%m-%d_%H%M",tma);
if (xine_config_lookup_entry(this->stream->xine, "media.capture.save_dir", &savedir)){
if(strlen(savedir.str_value)>1){
@@ -1628,12 +1951,13 @@ static void dvb_event_handler (dvb_input_plugin_t *this) {
xine_event_t *event;
static int channel_menu_visible = 0;
+ static int next_channel = -1;
while ((event = xine_event_get (this->event_queue))) {
xprintf(this->class->xine,XINE_VERBOSITY_DEBUG,"got event %08x\n", event->type);
- if (this->fd<0) {
+ if (this->fd < 0) {
xine_event_free (event);
return;
}
@@ -1647,35 +1971,48 @@ static void dvb_event_handler (dvb_input_plugin_t *this) {
case MOUSE_BUTTON_LEFT:
if (channel_menu_visible) {
channel_menu_visible = 0;
- switch_channel (this);
+ if (next_channel != this->channel){
+ this->channel = next_channel;
+ switch_channel (this, next_channel);
+ }
+ else
+ this->stream->osd_renderer->hide(this->channel_osd, 0);
}
-#if 0 /* disable mouse left click -> EPG for now. */
- else {
+#ifdef LEFT_MOUSE_DOES_EPG
+ else { /* show EPG on left click of videowindow */
show_eit(this);
}
-#endif
+#endif
break;
case MOUSE_WHEEL_UP:
- if (this->channel>0)
- this->channel--;
- channel_menu_visible = 1;
- osd_show_channel (this);
+ if (!channel_menu_visible)
+ next_channel = this->channel;
+ if (next_channel > 0)
+ next_channel--;
+
+ channel_menu_visible = 1;
+ osd_show_channel(this, next_channel);
break;
case MOUSE_WHEEL_DOWN:
- if (this->channel < (this->num_channels-1))
- this->channel++;
+
+ if (!channel_menu_visible)
+ next_channel = this->channel;
+
+ if (next_channel < (this->num_channels-1))
+ next_channel++;
+
channel_menu_visible = 1;
- osd_show_channel (this);
+ osd_show_channel(this, next_channel);
break;
case MOUSE_SIDE_LEFT:
if (this->channel > 0) {
this->channel--;
channel_menu_visible = 0;
- switch_channel (this);
+ switch_channel(this, this->channel);
}
break;
@@ -1683,72 +2020,91 @@ static void dvb_event_handler (dvb_input_plugin_t *this) {
if (this->channel < (this->num_channels-1)) {
this->channel++;
channel_menu_visible = 0;
- switch_channel (this);
+ switch_channel (this, this->channel);
}
break;
default:
- printf("Unknown mouse button number: %d.\n", input->button);
- /* Unknown mouse event. */
+ /* Unused mouse event. */
+ break;
}
break;
}
case XINE_EVENT_INPUT_DOWN:
- if (this->channel < (this->num_channels-1))
- this->channel++;
+ if (!channel_menu_visible)
+ next_channel = this->channel;
+
+ if (next_channel < (this->num_channels-1))
+ next_channel++;
+
channel_menu_visible = 1;
- osd_show_channel (this);
+ osd_show_channel(this, next_channel);
+
break;
case XINE_EVENT_INPUT_UP:
- if (this->channel>0)
- this->channel--;
+ if (!channel_menu_visible)
+ next_channel = this->channel;
+
+ if (next_channel > 0)
+ next_channel--;
+
channel_menu_visible = 1;
- osd_show_channel (this);
+ osd_show_channel(this, next_channel);
break;
case XINE_EVENT_INPUT_NEXT:
if (this->channel < (this->num_channels-1)) {
- this->channel++;
channel_menu_visible = 0;
- switch_channel (this);
+ switch_channel (this, this->channel + 1);
+ }
+ break;
+
+ case XINE_EVENT_INPUT_SELECT:
+ channel_menu_visible = 0;
+ if (next_channel != this->channel){
+ switch_channel (this, next_channel);
+ this->channel = next_channel;
}
+ else
+ this->stream->osd_renderer->hide(this->channel_osd, 0);
break;
case XINE_EVENT_INPUT_PREVIOUS:
if (this->channel>0) {
- this->channel--;
channel_menu_visible = 0;
- switch_channel (this);
+ switch_channel (this, this->channel - 1);
}
break;
- case XINE_EVENT_INPUT_SELECT:
- channel_menu_visible = 0;
- switch_channel (this);
- break;
-
case XINE_EVENT_INPUT_MENU1:
- this->stream->osd_renderer->hide (this->osd, 0);
+ if (this->osd != NULL)
+ this->stream->osd_renderer->hide (this->osd, 0);
channel_menu_visible = 0;
break;
case XINE_EVENT_INPUT_MENU2:
do_record (this);
break;
+
case XINE_EVENT_INPUT_MENU3:
/* zoom for cropped 4:3 in a 16:9 window */
if (!this->zoom_ok) {
this->zoom_ok = 1;
- this->stream->video_out->set_property (this->stream->video_out, VO_PROP_ZOOM_X, 133);
- this->stream->video_out->set_property (this->stream->video_out, VO_PROP_ZOOM_Y, 133);
+ this->stream->video_out->set_property(
+ this->stream->video_out, VO_PROP_ZOOM_X, 133);
+ this->stream->video_out->set_property(
+ this->stream->video_out, VO_PROP_ZOOM_Y, 133);
} else {
this->zoom_ok=0;
- this->stream->video_out->set_property (this->stream->video_out, VO_PROP_ZOOM_X, 100);
- this->stream->video_out->set_property (this->stream->video_out, VO_PROP_ZOOM_Y, 100);
+ this->stream->video_out->set_property(
+ this->stream->video_out, VO_PROP_ZOOM_X, 100);
+ this->stream->video_out->set_property(
+ this->stream->video_out, VO_PROP_ZOOM_Y, 100);
}
break;
+
case XINE_EVENT_INPUT_MENU4:
/* Pause recording.. */
if ((this->record_fd>-1) && (!this->record_paused)) {
@@ -1949,6 +2305,7 @@ static off_t dvb_plugin_get_current_pos (input_plugin_t *this_gen){
static void dvb_plugin_dispose (input_plugin_t *this_gen) {
dvb_input_plugin_t *this = (dvb_input_plugin_t *) this_gen;
+ int i, j;
if (this->fd != -1) {
close(this->fd);
@@ -1969,8 +2326,20 @@ static void dvb_plugin_dispose (input_plugin_t *this_gen) {
if (this->channels)
free (this->channels);
- if(this->eitbuffer)
- free (this->eitbuffer);
+ /* Free the EPG data. */
+ for (i = 0; i < this->num_channels; ++i) {
+ for (j = 0; j < this->channels[i].epg_count; ++j) {
+ if(this->channels[i].epg[j]->description)
+ free(this->channels[i].epg[j]->description);
+ if(this->channels[i].epg[j]->progname)
+ free(this->channels[i].epg[j]->progname);
+ if(this->channels[i].epg[j]->content)
+ free(this->channels[i].epg[j]->content);
+ if(this->channels[i].epg[j])
+ free(this->channels[i].epg[j]);
+ this->channels[i].epg[j] = NULL;
+ }
+ }
if (this->tuner)
tuner_dispose (this->tuner);
@@ -2037,14 +2406,15 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
tuner_t *tuner;
channel_t *channels;
int num_channels;
+ config_values_t *config = this->stream->xine->config;
char str[256];
char *ptr;
int x;
char dummy=0;
xine_cfg_entry_t zoomdvb;
- config_values_t *config = this->stream->xine->config;
- xine_cfg_entry_t lastchannel;
xine_cfg_entry_t adapter;
+ xine_cfg_entry_t lastchannel;
+
xine_config_lookup_entry(this->stream->xine, "input.dvb_adapternum", &adapter);
if (!(tuner = tuner_init(this->class->xine,adapter.num_value))) {
@@ -2126,14 +2496,22 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
if (idx < num_channels) {
this->channel = idx;
} else {
- xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: channel %s not found in channels.conf, defaulting to channel 0\n"), channame);
+ xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: channel %s not found in channels.conf, defaulting.\n"), channame);
this->channel = 0;
}
}
} else {
/* just default to channel 0 */
- xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: invalid channel specification, defaulting to channel 0\n"));
- this->channel = 0;
+ xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: invalid channel specification, defaulting to last viewed channel.\n"));
+ xine_config_lookup_entry(this->class->xine, "media.dvb.remember_channel", &lastchannel);
+ if (lastchannel.num_value) {
+ if (xine_config_lookup_entry(this->class->xine, "media.dvb.last_channel", &lastchannel)){
+ this->channel = lastchannel.num_value -1;
+ }else{
+ xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("input_dvb: invalid channel specification, defaulting to channel 0\n"));
+ this->channel = 0;
+ }
+ }
}
}
@@ -2149,8 +2527,10 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
ptr = this->mrl;
ptr += 7;
channels = xine_xmalloc(sizeof(channel_t));
+ _x_assert(channels != NULL);
if (extract_channel_from_string(channels, ptr, tuner->feinfo.type) < 0) {
free(channels);
+ channels = NULL;
tuner_dispose(tuner);
return 0;
}
@@ -2167,8 +2547,10 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
ptr = this->mrl;
ptr += 7;
channels = xine_xmalloc(sizeof(channel_t));
+ _x_assert(channels != NULL);
if (extract_channel_from_string(channels, ptr, tuner->feinfo.type) < 0) {
free(channels);
+ channels = NULL;
tuner_dispose(tuner);
return 0;
}
@@ -2188,9 +2570,11 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
ptr = this->mrl;
ptr += 7;
channels = xine_xmalloc(sizeof(channel_t));
+ _x_assert(channels != NULL);
if (extract_channel_from_string(channels, ptr, tuner->feinfo.type) < 0)
{
free(channels);
+ channels = NULL;
tuner_dispose(tuner);
return 0;
}
@@ -2227,8 +2611,6 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
this->event_queue = xine_event_new_queue(this->stream);
- this->eitbuffer=xine_xmalloc(2048*this->num_channels);
-
/*
* this osd is used to draw the "recording" sign
*/
@@ -2266,22 +2648,24 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
this->stream->osd_renderer->set_text_palette(this->paused_osd, XINE_TEXTPALETTE_YELLOW_BLACK_TRANSPARENT, OSD_TEXT3);
/*
- * this osd is for displaying Program Information (EIT)
+ * This osd is for displaying Program Information (EIT), i.e., EPG.
*/
- this->proginfo_osd = this->stream->osd_renderer->new_object(this->stream->osd_renderer, 1000, 600);
- this->stream->osd_renderer->set_position(this->proginfo_osd, 10, 10);
+ this->proginfo_osd =
+ this->stream->osd_renderer->new_object(
+ this->stream->osd_renderer, EPG_WIDTH, EPG_HEIGHT);
+
this->stream->osd_renderer->set_font(this->proginfo_osd, "sans", 24);
this->stream->osd_renderer->set_encoding(this->proginfo_osd, NULL);
this->stream->osd_renderer->set_text_palette(this->proginfo_osd, XINE_TEXTPALETTE_WHITE_NONE_TRANSLUCID, OSD_TEXT3);
this->stream->osd_renderer->set_text_palette(this->proginfo_osd, XINE_TEXTPALETTE_YELLOW_BLACK_TRANSPARENT, OSD_TEXT4);
- this->background = this->stream->osd_renderer->new_object(this->stream->osd_renderer, 1000, 600);
- this->stream->osd_renderer->set_position(this->background, 1, 1);
- this->stream->osd_renderer->set_font(this->background, "cetus", 32);
- this->stream->osd_renderer->set_encoding(this->background, NULL);
- this->stream->osd_renderer->set_text_palette(this->background, XINE_TEXTPALETTE_YELLOW_BLACK_TRANSPARENT, OSD_TEXT3);
- this->stream->osd_renderer->filled_rect(this->background, 1, 1, 1000, 600, 4);
- this->displaying=0;
+ this->background =
+ this->stream->osd_renderer->new_object(
+ this->stream->osd_renderer,
+ EPG_WIDTH + EPG_BACKGROUND_MARGIN*2,
+ EPG_HEIGHT + EPG_BACKGROUND_MARGIN*2);
+
+ this->epg_displaying = 0;
/* zoom for 4:3 in a 16:9 window */
config->register_bool(config, "media.dvb.zoom",
0,
@@ -2310,7 +2694,7 @@ static int dvb_plugin_open(input_plugin_t * this_gen)
ts_build_crc32_table(this);
/* Clear all pids, the pmt will tell us which to use */
- for (x = 0; x < MAXFILTERS; x++){
+ for (x = 0; x < MAX_FILTERS; x++){
this->channels[this->channel].pid[x] = NOPID;
}
@@ -2335,6 +2719,8 @@ static input_plugin_t *dvb_class_get_instance (input_class_t *class_gen,
this = (dvb_input_plugin_t *) xine_xmalloc (sizeof(dvb_input_plugin_t));
+ _x_assert(this != NULL);
+
this->stream = stream;
this->mrl = strdup(mrl);
this->class = class;
@@ -2406,7 +2792,9 @@ static char **dvb_class_get_autoplay_list(input_class_t * this_gen,
int default_channel;
xine_cfg_entry_t lastchannel_enable;
xine_cfg_entry_t lastchannel;
-
+
+ _x_assert(tmpbuffer != NULL);
+ _x_assert(foobuffer != NULL);
snprintf(tmpbuffer, BUFSIZE, "%s/.xine/channels.conf", xine_get_homedir());
@@ -2441,6 +2829,8 @@ static char **dvb_class_get_autoplay_list(input_class_t * this_gen,
f=fopen (tmpbuffer,"rb");
channels=xine_xmalloc(sizeof(channel_t)*(nlines+lastchannel_enable.num_value));
+
+ _x_assert(channels != NULL);
while (fgets(str,BUFSIZE,f) && num_channels < nlines+lastchannel_enable.num_value) {
@@ -2451,6 +2841,8 @@ static char **dvb_class_get_autoplay_list(input_class_t * this_gen,
if(class->autoplaylist[num_channels])
free(class->autoplaylist[num_channels]);
class->autoplaylist[num_channels]=xine_xmalloc(128);
+
+ _x_assert(class->autoplaylist[num_channels] != NULL);
class->autoplaylist[num_channels]=strdup(foobuffer);
num_channels++;
@@ -2464,6 +2856,7 @@ static char **dvb_class_get_autoplay_list(input_class_t * this_gen,
if(class->autoplaylist[0])
free(class->autoplaylist[0]);
class->autoplaylist[0]=xine_xmalloc(128);
+ _x_assert(class->autoplaylist[0] != NULL);
class->autoplaylist[0]=strdup(foobuffer);
}
@@ -2484,6 +2877,7 @@ static void *init_class (xine_t *xine, void *data) {
config_values_t *config = xine->config;
this = (dvb_input_class_t *) xine_xmalloc (sizeof (dvb_input_class_t));
+ _x_assert(this != NULL);
this->xine = xine;
@@ -2514,10 +2908,10 @@ static void *init_class (xine_t *xine, void *data) {
/* Enable remembering of last watched channel never show this entry*/
config->register_num(config, "media.dvb.last_channel",
-1,
- _("Remember last DVB channel watched"),
- _("If enabled, xine will remember and "
+ _("Last DVB channel viewed"),
+ _("If enabled above, xine will remember and "
"switch to this channel. "),
- 11, NULL, NULL);
+ 15, NULL, NULL);
config->register_num(config, "input.dvb_adapternum",