diff --git a/data/locale/deutsch.locale b/data/locale/deutsch.locale index a410fcf78..eedb16d9a 100644 --- a/data/locale/deutsch.locale +++ b/data/locale/deutsch.locale @@ -767,6 +767,7 @@ miscsettings.infobar_show_var_hdd Füllstandanzeige (var & hdd) miscsettings.noaviawatchdog AVIA-Watchdog aktivieren miscsettings.noenxwatchdog eNX-Watchdog aktivieren miscsettings.pmtupdate PMT Aktualisierung verwenden +miscsettings.radiotext Radiotext miscsettings.show_infomenu miscsettings.shutdown_count Komplett ausschalten nach miscsettings.shutdown_count_hint1 Bitte warten... diff --git a/data/locale/english.locale b/data/locale/english.locale index 30861b043..1ba50e233 100644 --- a/data/locale/english.locale +++ b/data/locale/english.locale @@ -767,6 +767,7 @@ miscsettings.infobar_show_var_hdd Fill level (var & hdd ) miscsettings.noaviawatchdog enable AVIA watchdog miscsettings.noenxwatchdog enable eNX watchdog miscsettings.pmtupdate enable pmt update +miscsettings.radiotext Radiotext miscsettings.show_infomenu miscsettings.shutdown_count In stabdby, switch off after miscsettings.shutdown_count_hint1 time (in minutes) to switch from standby diff --git a/src/daemonc/Makefile.am b/src/daemonc/Makefile.am index 3bd3d0ec9..5548a6be3 100644 --- a/src/daemonc/Makefile.am +++ b/src/daemonc/Makefile.am @@ -5,6 +5,7 @@ INCLUDES = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/zapit/include \ -I$(top_srcdir)/lib/libeventserver \ + -I$(top_srcdir)/lib/libcoolstream \ -I$(top_srcdir)/lib/libconfigfile \ -I$(top_srcdir)/lib/libnet \ -I$(top_srcdir)/lib/xmltree \ diff --git a/src/driver/Makefile.am b/src/driver/Makefile.am index a7fabb57d..b876f750c 100644 --- a/src/driver/Makefile.am +++ b/src/driver/Makefile.am @@ -33,7 +33,10 @@ libneutrino_driver_a_SOURCES = \ shutdown_count.cpp \ genpsi.c \ streamts.cpp \ - rfmod.cpp + rfmod.cpp \ + radiotools.cpp \ + radiotext.cpp \ + ringbuffer.c if BOXTYPE_COOL libneutrino_driver_a_SOURCES += \ diff --git a/src/driver/radiotext.cpp b/src/driver/radiotext.cpp new file mode 100644 index 000000000..a622c8c87 --- /dev/null +++ b/src/driver/radiotext.cpp @@ -0,0 +1,2674 @@ +/* + $Id: radiotext.cpp,v 1.7 2009/10/31 10:11:02 seife Exp $ + + Neutrino-GUI - DBoxII-Project + + License: GPL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + + ripped from: +*/ + +/* + * radioaudio.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * This is a "plugin" for the Video Disk Recorder (VDR). + * + * Written by: Lars Tegeler + * + * Project's homepage: www.math.uni-paderborn.de/~tegeler/vdr + * + * Latest version available at: URL + * + * See the file COPYING for license information. + * + * Description: + * + * This Plugin display an background image while the vdr is switcht to radio channels. + * + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#ifdef HAVE_TRIPLEDRAGON +#include +#include +#define DMXDEV "/dev/" DEVICE_NAME_DEMUX "1" +#elif HAVE_DVB_API_VERSION < 3 +#include +#define DMXDEV "/dev/dvb/card0/demux0" +#define DVRDEV "/dev/dvb/card0/dvr0" +#define dmx_pes_filter_params dmxPesFilterParams +#define pes_type pesType +#else +#include +#define DMXDEV "/dev/dvb/adapter0/demux0" +#define DVRDEV "/dev/dvb/adapter0/dvr0" +#endif +#endif + +#include +#include +#include +#include + +extern "C" { +#include "ringbuffer.h" +} + +#include "radiotext.h" +#include "radiotools.h" + +rtp_classes rtp_content; + +// RDS rest +bool RDS_PSShow = false; +int RDS_PSIndex = 0; +char RDS_PSText[12][9]; + +// plugin audiorecorder service +bool ARec_Receive = false, ARec_Record = false; + +#if ENABLE_RASS +// ... Gallery (1..999) +#define RASS_GALMAX 999 +bool Rass_Gallery[RASS_GALMAX+1]; +int Rass_GalStart, Rass_GalEnd, Rass_GalCount, Rass_SlideFoto; +#endif + +#define floor +const char *DataDir = "./"; +//cRadioAudio *RadioAudio; +//cRadioTextOsd *RadioTextOsd; +//cRDSReceiver *RDSReceiver; + +// RDS-Chartranslation: 0x80..0xff +unsigned char rds_addchar[128] = { + 0xe1, 0xe0, 0xe9, 0xe8, 0xed, 0xec, 0xf3, 0xf2, + 0xfa, 0xf9, 0xd1, 0xc7, 0x8c, 0xdf, 0x8e, 0x8f, + 0xe2, 0xe4, 0xea, 0xeb, 0xee, 0xef, 0xf4, 0xf6, + 0xfb, 0xfc, 0xf1, 0xe7, 0x9c, 0x9d, 0x9e, 0x9f, + 0xaa, 0xa1, 0xa9, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xa3, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xba, 0xb9, 0xb2, 0xb3, 0xb1, 0xa1, 0xb6, 0xb7, + 0xb5, 0xbf, 0xf7, 0xb0, 0xbc, 0xbd, 0xbe, 0xa7, + 0xc1, 0xc0, 0xc9, 0xc8, 0xcd, 0xcc, 0xd3, 0xd2, + 0xda, 0xd9, 0xca, 0xcb, 0xcc, 0xcd, 0xd0, 0xcf, + 0xc2, 0xc4, 0xca, 0xcb, 0xce, 0xcf, 0xd4, 0xd6, + 0xdb, 0xdc, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xc3, 0xc5, 0xc6, 0xe3, 0xe4, 0xdd, 0xd5, 0xd8, + 0xde, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xf0, + 0xe3, 0xe5, 0xe6, 0xf3, 0xf4, 0xfd, 0xf5, 0xf8, + 0xfe, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static const char *entitystr[5] = { "'", "&", ""e;", ">", "<" }; +static const char *entitychar[5] = { "'", "&", "\"", ">", "<" }; + +char *CRadioText::rds_entitychar(char *text) +{ + int i = 0, l, lof, lre, space; + char *temp; + + while (i < 5) { + if ((temp = strstr(text, entitystr[i])) != NULL) { + if (S_Verbose >= 2) + printf("RText-Entity: %s\n", text); + l = strlen(entitystr[i]); + lof = (temp-text); + if (strlen(text) < RT_MEL) { + lre = strlen(text) - lof - l; + space = 1; + } + else { + lre = RT_MEL - 1 - lof - l; + space = 0; + } + memmove(text+lof, entitychar[i], 1); + memmove(text+lof+1, temp+l, lre); + if (space != 0) + memmove(text+lof+1+lre, " ", l-1); + } + else i++; + } + return text; +} + +char* CRadioText::ptynr2string(int nr) +{ + switch (nr) { + // Source: http://www.ebu.ch/trev_255-beale.pdf + case 0: return tr(const_cast("unknown program type")); + case 1: return tr(const_cast("News")); + case 2: return tr(const_cast("Current affairs")); + case 3: return tr(const_cast("Information")); + case 4: return tr(const_cast("Sport")); + case 5: return tr(const_cast("Education")); + case 6: return tr(const_cast("Drama")); + case 7: return tr(const_cast("Culture")); + case 8: return tr(const_cast("Science")); + case 9: return tr(const_cast("Varied")); + case 10: return tr(const_cast("Pop music")); + case 11: return tr(const_cast("Rock music")); + case 12: return tr(const_cast("M.O.R. music")); + case 13: return tr(const_cast("Light classical")); + case 14: return tr(const_cast("Serious classical")); + case 15: return tr(const_cast("Other music")); + // 16-30 "Spares" + case 31: return tr(const_cast("Alarm")); + default: return const_cast("?"); + } +} + +bool CRadioText::DividePes(char *data, int length, int *substart, int *subend) +{ + int i = *substart; + int found = 0; + while ((i len) + return offset; + offset += pesl; + + /* try to find subpackets in the pes stream */ + int substart = 0; + int subend = 0; + while (DividePes(&data[0], pesl, &substart, &subend)) + { + int inner_offset = subend + 1; +if (inner_offset < 3) fprintf(stderr, "RT %s: inner_offset < 3 (%d)\n", __FUNCTION__, inner_offset); + int rdsl = data[subend - 1]; // RDS DataFieldLength + // RDS DataSync = 0xfd @ end + if (data[subend] == 0xfd && rdsl > 0) { + // print RawData with RDS-Info + if (S_Verbose >= 3) { + printf("\n\nPES-Data(%d/%d): ", pesl, len); + for (int a=inner_offset -rdsl; a subend - 2 - rdsl; i--) { // <-- data reverse, from end to start +if (i < 0) { fprintf(stderr, "RT %s: i < 0 (%d)\n", __FUNCTION__, i); break; } + val = data[i]; + + if (val == 0xfe) { // Start + index = -1; + rt_start = true; + rt_bstuff = false; + if (S_Verbose >= 2) + printf("RDS-Start: "); + } + + if (rt_start) { + if (S_Verbose >= 3) + printf("%02x ", val); + // byte-stuffing reverse: 0xfd00->0xfd, 0xfd01->0xfe, 0xfd02->0xff + if (rt_bstuff) { + switch (val) { + case 0x00: mtext[index] = 0xfd; break; + case 0x01: mtext[index] = 0xfe; break; + case 0x02: mtext[index] = 0xff; break; + default: mtext[++index] = val; // should never be + } + rt_bstuff = false; + if (S_Verbose >= 3) + printf("(Bytestuffing -> %02x) ", mtext[index]); + } + else + mtext[++index] = val; + if (val == 0xfd && index > 0) // stuffing found + rt_bstuff = true; + // early check for used MEC + if (index == 5) { + //mec = val; + switch (val) { + case 0x0a: // RT + have_radiotext = true; + case 0x46: // RTplus-Tags + case 0xda: // RASS + case 0x07: // PTY + case 0x3e: // PTYN + case 0x02: // PS + mec = val; + break; + default: + rt_start = false; + if (S_Verbose >= 2) + printf("(RDS-MEC '%02x' not used -> End)\n", val); + } + } + if (index >= mframel) { // max. rdslength, garbage ? + if (S_Verbose >= 1) + printf("RDS-Error: too long, garbage ?\n"); + rt_start = false; + } + } + + if (rt_start && val == 0xff) { // End + if (S_Verbose >= 2) + printf("(RDS-End)\n"); + rt_start = false; + if (index < 9) { // min. rdslength, garbage ? + if (S_Verbose >= 1) + printf("RDS-Error: too short -> garbage ?\n"); + } + else { + // crc16-check + unsigned short crc16 = crc16_ccitt(mtext, index-3, true); + if (crc16 != (mtext[index-2]<<8)+mtext[index-1]) { + if (S_Verbose >= 1) + printf("RDS-Error: wrong CRC # calc = %04x <> transmit = %02x%02x\n", crc16, mtext[index-2], mtext[index-1]); + } else { + + switch (mec) { + case 0x0a: + case 0x46: + if (S_Verbose >= 2) + printf("(RDS-MEC '%02x') -> RadiotextDecode - %d\n", mec, index); + RadiotextDecode(mtext, index); // Radiotext, RT+ + break; + case 0x07: RT_PTY = mtext[8]; // PTY + RT_MsgShow = true; + if (S_Verbose >= 1) + printf("RDS-PTY set to '%s'\n", ptynr2string(RT_PTY)); + break; + case 0x3e: + if (S_Verbose >= 2) + printf("(RDS-MEC '%02x') -> RDS_PsPtynDecode - %d\n", mec, index); + RDS_PsPtynDecode(true, mtext, index); // PTYN + break; + case 0x02: + if (S_Verbose >= 2) + printf("(RDS-MEC '%02x') -> RDS_PsPtynDecode - %d\n", mec, index); + RDS_PsPtynDecode(false, mtext, index); // PS + break; + case 0xda: +#if ENABLE_RASS + RassDecode(mtext, index); // Rass +#endif + break; + } + } + } + } + } + } + substart = subend; + } + } +} + + +void CRadioText::RadiotextDecode(unsigned char *mtext, int len) +{ + static bool rtp_itoggle = false; + static int rtp_idiffs = 0; + static cTimeMs rtp_itime; + static char plustext[RT_MEL]; + + // byte 1+2 = ADD (10bit SiteAdress + 6bit EncoderAdress) + // byte 3 = SQC (Sequence Counter 0x00 = not used) + int leninfo = mtext[4]; // byte 4 = MFL (Message Field Length) + if (len >= leninfo+7) { // check complete length + + // byte 5 = MEC (Message Element Code, 0x0a for RT, 0x46 for RTplus) + if (mtext[5] == 0x0a) { + // byte 6+7 = DSN+PSN (DataSetNumber+ProgramServiceNumber, + // ignore here, always 0x00 ?) + // byte 8 = MEL (MessageElementLength, max. 64+1 byte @ RT) + if (mtext[8] == 0 || mtext[8] > RT_MEL || mtext[8] > leninfo-4) { + if (S_Verbose >= 1) + printf("RT-Error: Length = 0 or not correct !"); + return; + } + // byte 9 = RT-Status bitcodet (0=AB-flagcontrol, 1-4=Transmission-Number, 5+6=Buffer-Config, + // ingnored, always 0x01 ?) +fprintf(stderr, "MEC=0x%02x DSN=0x%02x PSN=0x%02x MEL=%02d STATUS=0x%02x MFL=%02d\n", mtext[5], mtext[6], mtext[7], mtext[8], mtext[9], mtext[4]); + char temptext[RT_MEL]; + memset(temptext, 0x20, RT_MEL-1); + temptext[RT_MEL - 1] = '\0'; + for (int i = 1, ii = 0; i < mtext[8]; i++) { + if (mtext[9+i] <= 0xfe) + // additional rds-character, see RBDS-Standard, Annex E + temptext[ii++] = (mtext[9+i] >= 0x80) ? rds_addchar[mtext[9+i]-0x80] : mtext[9+i]; + } + memcpy(plustext, temptext, RT_MEL); + rds_entitychar(temptext); + // check repeats + bool repeat = false; + for (int ind = 0; ind < S_RtOsdRows; ind++) { + if (memcmp(RT_Text[ind], temptext, RT_MEL-1) == 0) { + repeat = true; + if (S_Verbose >= 1) + printf("RText-Rep[%d]: %s\n", ind, RT_Text[ind]); + } + } + if (!repeat) { + memcpy(RT_Text[RT_Index], temptext, RT_MEL); + // +Memory + char *temp; + asprintf(&temp, "%s", RT_Text[RT_Index]); + if (++rtp_content.rt_Index >= 2*MAX_RTPC) + rtp_content.rt_Index = 0; + asprintf(&rtp_content.radiotext[rtp_content.rt_Index], "%s", rtrim(temp)); + free(temp); + if (S_Verbose >= 1) + printf("Radiotext[%d]: %s\n", RT_Index, RT_Text[RT_Index]); + RT_Index +=1; if (RT_Index >= S_RtOsdRows) RT_Index = 0; + } + RTP_TToggle = 0x03; // Bit 0/1 = Title/Artist + RT_MsgShow = true; + S_RtOsd = 1; + RT_Info = (RT_Info > 0) ? : 1; + RadioStatusMsg(); + } + + else if (RTP_TToggle > 0 && mtext[5] == 0x46 && S_RtFunc >= 2) { // RTplus tags V2.0, only if RT + if (mtext[6] > leninfo-2 || mtext[6] != 8) { // byte 6 = MEL, only 8 byte for 2 tags + if (S_Verbose >= 1) + printf("RTp-Error: Length not correct !"); + return; + } + + uint rtp_typ[2], rtp_start[2], rtp_len[2]; + // byte 7+8 = ApplicationID, always 0x4bd7 + // byte 9 = Applicationgroup Typecode / PTY ? + // bit 10#4 = Item Togglebit + // bit 10#3 = Item Runningbit + // Tag1: bit 10#2..11#5 = Contenttype, 11#4..12#7 = Startmarker, 12#6..12#1 = Length + rtp_typ[0] = (0x38 & mtext[10]<<3) | mtext[11]>>5; + rtp_start[0] = (0x3e & mtext[11]<<1) | mtext[12]>>7; + rtp_len[0] = 0x3f & mtext[12]>>1; + // Tag2: bit 12#0..13#3 = Contenttype, 13#2..14#5 = Startmarker, 14#4..14#0 = Length(5bit) + rtp_typ[1] = (0x20 & mtext[12]<<5) | mtext[13]>>3; + rtp_start[1] = (0x38 & mtext[13]<<3) | mtext[14]>>5; + rtp_len[1] = 0x1f & mtext[14]; + if (S_Verbose >= 2) + printf("RTplus (tag=Typ/Start/Len): Toggle/Run = %d/%d, tag#1 = %d/%d/%d, tag#2 = %d/%d/%d\n", + (mtext[10]&0x10)>0, (mtext[10]&0x08)>0, rtp_typ[0], rtp_start[0], rtp_len[0], rtp_typ[1], rtp_start[1], rtp_len[1]); + // save info + for (int i = 0; i < 2; i++) { + if (rtp_start[i]+rtp_len[i]+1 >= RT_MEL) { // length-error + if (S_Verbose >= 1) + printf("RTp-Error (tag#%d = Typ/Start/Len): %d/%d/%d (Start+Length > 'RT-MEL' !)\n", + i+1, rtp_typ[i], rtp_start[i], rtp_len[i]); + } else { + char temptext[RT_MEL]; + memset(temptext, 0x20, RT_MEL-1); + memmove(temptext, plustext+rtp_start[i], rtp_len[i]+1); + rds_entitychar(temptext); + // +Memory + memset(rtp_content.temptext, 0x20, RT_MEL-1); + memcpy(rtp_content.temptext, temptext, RT_MEL-1); + switch (rtp_typ[i]) { + case 1: // Item-Title + if ((mtext[10] & 0x08) > 0 && (RTP_TToggle & 0x01) == 0x01) { + RTP_TToggle -= 0x01; + RT_Info = 2; + if (memcmp(RTP_Title, temptext, RT_MEL-1) != 0 || (mtext[10] & 0x10) != RTP_ItemToggle) { + memcpy(RTP_Title, temptext, RT_MEL-1); + if (RT_PlusShow && rtp_itime.Elapsed() > 1000) + rtp_idiffs = (int) rtp_itime.Elapsed()/1000; + if (!rtp_content.item_New) { + RTP_Starttime = time(NULL); + rtp_itime.Set(0); + sprintf(RTP_Artist, "---"); + if (++rtp_content.item_Index >= MAX_RTPC) + rtp_content.item_Index = 0; + rtp_content.item_Start[rtp_content.item_Index] = time(NULL); // todo: replay-mode + rtp_content.item_Artist[rtp_content.item_Index] = NULL; + } + rtp_content.item_New = (!rtp_content.item_New) ? true : false; + if (rtp_content.item_Index >= 0) + asprintf(&rtp_content.item_Title[rtp_content.item_Index], "%s", rtrim(rtp_content.temptext)); + RT_PlusShow = RT_MsgShow = rtp_itoggle = true; + } + } + break; + case 4: // Item-Artist + if ((mtext[10] & 0x08) > 0 && (RTP_TToggle & 0x02) == 0x02) { + RTP_TToggle -= 0x02; + RT_Info = 2; + if (memcmp(RTP_Artist, temptext, RT_MEL-1) != 0 || (mtext[10] & 0x10) != RTP_ItemToggle) { + memcpy(RTP_Artist, temptext, RT_MEL-1); + if (RT_PlusShow && rtp_itime.Elapsed() > 1000) + rtp_idiffs = (int) rtp_itime.Elapsed()/1000; + if (!rtp_content.item_New) { + RTP_Starttime = time(NULL); + rtp_itime.Set(0); + sprintf(RTP_Title, "---"); + if (++rtp_content.item_Index >= MAX_RTPC) + rtp_content.item_Index = 0; + rtp_content.item_Start[rtp_content.item_Index] = time(NULL); // todo: replay-mode + rtp_content.item_Title[rtp_content.item_Index] = NULL; + } + rtp_content.item_New = (!rtp_content.item_New) ? true : false; + if (rtp_content.item_Index >= 0) + asprintf(&rtp_content.item_Artist[rtp_content.item_Index], "%s", rtrim(rtp_content.temptext)); + RT_PlusShow = RT_MsgShow = rtp_itoggle = true; + } + } + break; + case 12: // Info_News + asprintf(&rtp_content.info_News, "%s", rtrim(rtp_content.temptext)); + break; + case 13: // Info_NewsLocal + asprintf(&rtp_content.info_NewsLocal, "%s", rtrim(rtp_content.temptext)); + break; + case 14: // Info_Stockmarket + if (++rtp_content.info_StockIndex >= MAX_RTPC) + rtp_content.info_StockIndex = 0; + asprintf(&rtp_content.info_Stock[rtp_content.info_StockIndex], "%s", rtrim(rtp_content.temptext)); + break; + case 15: // Info_Sport + if (++rtp_content.info_SportIndex >= MAX_RTPC) + rtp_content.info_SportIndex = 0; + asprintf(&rtp_content.info_Sport[rtp_content.info_SportIndex], "%s", rtrim(rtp_content.temptext)); + break; + case 16: // Info_Lottery + if (++rtp_content.info_LotteryIndex >= MAX_RTPC) + rtp_content.info_LotteryIndex = 0; + asprintf(&rtp_content.info_Lottery[rtp_content.info_LotteryIndex], "%s", rtrim(rtp_content.temptext)); + break; + case 24: // Info_DateTime + asprintf(&rtp_content.info_DateTime, "%s", rtrim(rtp_content.temptext)); + break; + case 25: // Info_Weather + if (++rtp_content.info_WeatherIndex >= MAX_RTPC) + rtp_content.info_WeatherIndex = 0; + asprintf(&rtp_content.info_Weather[rtp_content.info_WeatherIndex], "%s", rtrim(rtp_content.temptext)); + break; + case 26: // Info_Traffic + asprintf(&rtp_content.info_Traffic, "%s", rtrim(rtp_content.temptext)); + break; + case 27: // Info_Alarm + asprintf(&rtp_content.info_Alarm, "%s", rtrim(rtp_content.temptext)); + break; + case 28: // Info_Advert + asprintf(&rtp_content.info_Advert, "%s", rtrim(rtp_content.temptext)); + break; + case 29: // Info_Url + asprintf(&rtp_content.info_Url, "%s", rtrim(rtp_content.temptext)); + break; + case 30: // Info_Other + if (++rtp_content.info_OtherIndex >= MAX_RTPC) + rtp_content.info_OtherIndex = 0; + asprintf(&rtp_content.info_Other[rtp_content.info_OtherIndex], "%s", rtrim(rtp_content.temptext)); + break; + case 31: // Programme_Stationname.Long + asprintf(&rtp_content.prog_Station, "%s", rtrim(rtp_content.temptext)); + break; + case 32: // Programme_Now + asprintf(&rtp_content.prog_Now, "%s", rtrim(rtp_content.temptext)); + break; + case 33: // Programme_Next + asprintf(&rtp_content.prog_Next, "%s", rtrim(rtp_content.temptext)); + break; + case 34: // Programme_Part + asprintf(&rtp_content.prog_Part, "%s", rtrim(rtp_content.temptext)); + break; + case 35: // Programme_Host + asprintf(&rtp_content.prog_Host, "%s", rtrim(rtp_content.temptext)); + break; + case 36: // Programme_EditorialStaff + asprintf(&rtp_content.prog_EditStaff, "%s", rtrim(rtp_content.temptext)); + break; + case 38: // Programme_Homepage + asprintf(&rtp_content.prog_Homepage, "%s", rtrim(rtp_content.temptext)); + break; + case 39: // Phone_Hotline + asprintf(&rtp_content.phone_Hotline, "%s", rtrim(rtp_content.temptext)); + break; + case 40: // Phone_Studio + asprintf(&rtp_content.phone_Studio, "%s", rtrim(rtp_content.temptext)); + break; + case 44: // Email_Hotline + asprintf(&rtp_content.email_Hotline, "%s", rtrim(rtp_content.temptext)); + break; + case 45: // Email_Studio + asprintf(&rtp_content.email_Studio, "%s", rtrim(rtp_content.temptext)); + break; + } + } + } + + // Title-end @ no Item-Running' + if ((mtext[10] & 0x08) == 0) { + sprintf(RTP_Title, "---"); + sprintf(RTP_Artist, "---"); + if (RT_PlusShow) { + RT_PlusShow = false; + rtp_itoggle = true; + rtp_idiffs = (int) rtp_itime.Elapsed()/1000; + RTP_Starttime = time(NULL); + } + RT_MsgShow = (RT_Info > 0); + rtp_content.item_New = false; + } + + if (rtp_itoggle) { + if (S_Verbose >= 1) { + struct tm tm_store; + struct tm *ts = localtime_r(&RTP_Starttime, &tm_store); + if (rtp_idiffs > 0) + printf(" StartTime : %02d:%02d:%02d (last Title elapsed = %d s)\n", + ts->tm_hour, ts->tm_min, ts->tm_sec, rtp_idiffs); + else + printf(" StartTime : %02d:%02d:%02d\n", ts->tm_hour, ts->tm_min, ts->tm_sec); + printf(" RTp-Title : %s\n RTp-Artist: %s\n", RTP_Title, RTP_Artist); + } + RTP_ItemToggle = mtext[10] & 0x10; + rtp_itoggle = false; + rtp_idiffs = 0; + RadioStatusMsg(); +// AudioRecorderService(); + } + RTP_TToggle = 0; + } + } + else { + if (S_Verbose >= 1) + printf("RDS-Error: [RTDecode] Length not correct !\n"); + } +} + +void CRadioText::RDS_PsPtynDecode(bool ptyn, unsigned char *mtext, int len) +{ + if (len < 16) return; + + // decode Text + for (int i = 8; i <= 15; i++) { + if (mtext[i] <= 0xfe) { + // additional rds-character, see RBDS-Standard, Annex E + if (!ptyn) + RDS_PSText[RDS_PSIndex][i-8] = (mtext[i] >= 0x80) ? rds_addchar[mtext[i]-0x80] : mtext[i]; + else + RDS_PTYN[i-8] = (mtext[i] >= 0x80) ? rds_addchar[mtext[i]-0x80] : mtext[i]; + } + } + + if (S_Verbose >= 1) { + if (!ptyn) + printf("RDS-PS No= %d, Content[%d]= '%s'\n", mtext[7], RDS_PSIndex, RDS_PSText[RDS_PSIndex]); + else + printf("RDS-PTYN No= %d, Content= '%s'\n", mtext[7], RDS_PTYN); + } + + if (!ptyn) { + RDS_PSIndex += 1; if (RDS_PSIndex >= 12) RDS_PSIndex = 0; + RT_MsgShow = RDS_PSShow = true; + } +} + +void CRadioText::RadioStatusMsg(void) +{ + /* announce text/items for lcdproc & other */ + if (!RT_MsgShow || S_RtMsgItems <= 0) + return; + + if (S_RtMsgItems >= 2) { + char temp[100]; + int ind = (RT_Index == 0) ? S_RtOsdRows - 1 : RT_Index - 1; + strcpy(temp, RT_Text[ind]); + printf("RadioStatusMsg = %s\n", temp); +// cStatus::MsgOsdTextItem(rtrim(temp), false); + } + + if ((S_RtMsgItems == 1 || S_RtMsgItems >= 3) && ((S_RtOsdTags == 1 && RT_PlusShow) || S_RtOsdTags >= 2)) { +// struct tm tm_store; +// struct tm *ts = localtime_r(&RTP_Starttime, &tm_store); +// cStatus::MsgOsdProgramme(mktime(ts), RTP_Title, RTP_Artist, 0, NULL, NULL); + printf("RTP_Title = %s, RTP_Artist = %s\n", RTP_Title, RTP_Artist); + } +} + + +#if ENABLE_RASS +// add of DVB Radio Slides Specification 1.0, 20061228 +void CRadioText::RassDecode(unsigned char *mtext, int len) +{ + static uint splfd = 0, spmax = 0, index = 0; + static uint afiles, slidenumr, slideelem, filemax, fileoffp; + static int filetype, fileoffb; + static bool slideshow = false, slidesave = false, slidecan = false, slidedel = false, start = false; + static uchar daten[65536]; // mpegs-stills defined <= 50kB + FILE *fd; + + // byte 1+2 = ADD (10bit SiteAdress + 6bit EncoderAdress) + // byte 3 = SQC (Sequence Counter 0x00 = not used) + // byte 4 = MFL (Message Field Length), + if (len >= mtext[4]+7) { // check complete length + // byte 5 = MEC (0xda for Rass) + // byte 6 = MEL + if (mtext[6] == 0 || mtext[6] > mtext[4]-2) { + if ((S_Verbose && 0x0f) >= 1) + printf("Rass-Error: Length = 0 or not correct !\n"); + return; + } + // byte 7+8 = Service-ID zugehöriger Datenkanal + // byte 9-11 = Nummer aktuelles Paket, + uint plfd = mtext[11] + mtext[10]*256 + mtext[9]*65536; + // byte 12-14 = Anzahl Pakete, + uint pmax = mtext[14] + mtext[13]*256 + mtext[12]*65536; + + // byte 15+16 = Rass-Kennung = Header, + if (mtext[15] == 0x40 && mtext[16] == 0xda) { // first + // byte 17+18 = Anzahl Dateien im Archiv, + afiles = mtext[18] + mtext[17]*256; + // byte 19+20 = Slide-Nummer, + slidenumr = mtext[20] + mtext[19]*256; + // byte 21+22 = Element-Nummer im Slide, + slideelem = mtext[22] + mtext[21]*256; + // byte 23 = Slide-Steuerbyte, : bit0 = Anzeige, bit1 = Speichern, bit2 = DarfAnzeige bei Senderwechsel, bit3 = Löschen + slideshow = mtext[23] & 0x01; + slidesave = mtext[23] & 0x02; + slidecan = mtext[23] & 0x04; + slidedel = mtext[23] & 0x08; + // byte 24 = Dateiart, : 0=unbekannt/1=MPEG-Still/2=Definition + filetype = mtext[24]; + if (filetype != 1 && filetype != 2) { + if ((S_Verbose && 0x0f) >= 1) + printf("Rass-Error: Filetype unknown !\n"); + return; + } + // byte 25-28 = Dateilänge, + filemax = mtext[28] + mtext[27]*256 + mtext[26]*65536 + mtext[25]*65536*256; + if (filemax >= 65536) { + if ((S_Verbose && 0x0f) >= 1) + printf("Rass-Error: Filesize will be too big !\n"); + return; + } + // byte 29-31 = Dateioffset Paketnr, + fileoffp = mtext[31] + mtext[30]*256 + mtext[29]*65536; + // byte 32 = Dateioffset Bytenr, + fileoffb = mtext[32]; + if (S_Verbose >= 2) + printf("Rass-Header: afiles= %d\n slidenumr= %d, slideelem= %d\n slideshow= %d, -save= %d, -canschow= %d, -delete= %d\n filetype= %d, filemax= %d\n fileoffp= %d, fileoffb= %d\n", + afiles, slidenumr, slideelem, slideshow, slidesave, slidecan, slidedel, filetype, filemax, fileoffp, fileoffb); + + if (fileoffp == 0) { // First + if (S_Verbose >= 2) + printf("Rass-Start@0 ...\n"); + start = true; + index = 0; + for (int i=fileoffb; i < len-2; i++) { + if (index < filemax) + daten[index++] = mtext[i]; + else + start = false; + } + } + splfd = plfd; + } + else if (plfd < pmax && plfd == splfd+1) { // Between + splfd = plfd; + if (!start && fileoffp == plfd) { // Data start, + if (S_Verbose >= 2) + printf("Rass-Start@%d ...\n", fileoffp); + start = true; + index = 0; + } + else + fileoffb = 15; + if (start) { + for (int i=fileoffb; i < len-2; i++) { + if (index < filemax) + daten[index++] = mtext[i]; + else + start = false; + } + } + } + else if (plfd == pmax && plfd == splfd+1) { // Last + fileoffb = 15; + if (start) { + for (int i=fileoffb; i < len-4; i++) { + if (index <= filemax) + daten[index++] = mtext[i]; + else { + start = false; + return; + } + } + if (S_Verbose >= 2) + printf("... Rass-End (%d bytes)\n", index); + } + + if (filemax > 0) { // nothing todo, if 0 byte file + // crc-check with bytes 'len-4/3' + unsigned short crc16 = crc16_ccitt(daten, filemax, false); + if (crc16 != (mtext[len-4]<<8)+mtext[len-3]) { + if ((S_Verbose && 0x0f) >= 1) + printf("Rass-Error: wrong CRC # calc = %04x <> transmit = %02x%02x\n", crc16, mtext[len-4], mtext[len-3]); + start = false; + return; + } + } + + // show & save file ? + if (index == filemax) { + if (slideshow || (slidecan && Rass_Show == -1)) { + if (filetype == 1) { // show only mpeg-still + char *filepath; + asprintf(&filepath, "%s/%s", DataDir, "Rass_show.mpg"); + if ((fd = fopen(filepath, "wb")) != NULL) { + fwrite(daten, 1, filemax, fd); + //fflush(fd); // for test in replaymode + fclose(fd); + Rass_Show = 1; + if (S_Verbose >= 2) + printf("Rass-File: ready for displaying :-)\n"); + } + else + printf("ERROR vdr-radio: writing imagefile failed '%s'", filepath); + free(filepath); + } + } + if (slidesave || slidedel || slidenumr < RASS_GALMAX) { + // lfd. Fotogallery 100.. ??? + if (slidenumr >= 100 && slidenumr < RASS_GALMAX) { + (Rass_SlideFoto < RASS_GALMAX) ? Rass_SlideFoto++ : Rass_SlideFoto = 100; + slidenumr = Rass_SlideFoto; + } + // + char *filepath; + (filetype == 2) ? asprintf(&filepath, "%s/Rass_%d.def", DataDir, slidenumr) + : asprintf(&filepath, "%s/Rass_%d.mpg", DataDir, slidenumr); + if ((fd = fopen(filepath, "wb")) != NULL) { + fwrite(daten, 1, filemax, fd); + fclose(fd); + if (S_Verbose >= 1) + printf("Rass-File: saving '%s'\n", filepath); + // archivemarker mpeg-stills + if (filetype == 1) { + // 0, 1000/1100/1110/1111..9000/9900/9990/9999 + if (slidenumr == 0 || slidenumr > RASS_GALMAX) { + if (slidenumr == 0) { + Rass_Flags[0][0] = !slidedel; + (RT_Info > 0) ? : RT_Info = 0; // open RadioTextOsd for ArchivTip + } + else { + int islide = (int) floor(slidenumr/1000); + for (int i = 3; i >= 0; i--) { + if ((slidenumr % (i==3 ? 1000 : i==2 ? 100 : i==1 ? 10 : 1)) == 0) { + Rass_Flags[islide][3-i] = !slidedel; //true; + break; + } + } + } + } + // gallery + else { + Rass_Gallery[slidenumr] = !slidedel; + if (!slidedel && (int)slidenumr > Rass_GalEnd) + Rass_GalEnd = slidenumr; + if (!slidedel && (Rass_GalStart == 0 || (int)slidenumr < Rass_GalStart)) + Rass_GalStart = slidenumr; + // counter + Rass_GalCount = 0; + for (int i = Rass_GalStart; i <= Rass_GalEnd; i++) { + if (Rass_Gallery[i]) + Rass_GalCount++; + } + Rass_Flags[10][0] = (Rass_GalCount > 0); + } + } + } + else + printf("ERROR vdr-radio: writing image/data-file failed '%s'", filepath); + free(filepath); + } + } + start = false; + splfd = spmax = 0; + } + else { + start = false; + splfd = spmax = 0; + } + } + else { + start = false; + splfd = spmax = 0; + if (S_Verbose >= 1) + printf("RDS-Error: [Rass] Length not correct !\n"); + } +} +#endif + +#if 0 +void cRadioAudio::EnableRadioTextProcessing(const char *Titel, bool replay) +{ + asprintf(&RT_Titel, "%s", Titel); + RT_Replay = replay; + ARec_Receive = ARec_Record = false; + + first_packets = 0; + enabled = true; + imagedelay = 0; + + // Radiotext init + if (S_RtFunc >= 1) { + RT_MsgShow = RT_PlusShow = false; + RT_ReOpen = true; + RT_OsdTO = false; + RT_Index = RT_PTY = RTP_TToggle = 0; + RTP_ItemToggle = 1; + for (int i = 0; i < 5; i++) + memset(RT_Text[i], 0x20, RT_MEL-1); + sprintf(RTP_Title, "---"); + sprintf(RTP_Artist, "---"); + RTP_Starttime = time(NULL); + // + RDS_PSShow = false; + RDS_PSIndex = 0; + for (int i = 0; i < 12; i++) + memset(RDS_PSText[i], 0x20, 8); + } + + // ...Memory + rtp_content.start = time(NULL); + rtp_content.item_New = false; + rtp_content.rt_Index = -1; + rtp_content.item_Index = -1; + rtp_content.info_StockIndex = -1; + rtp_content.info_SportIndex = -1; + rtp_content.info_LotteryIndex = -1; + rtp_content.info_WeatherIndex = -1; + rtp_content.info_OtherIndex = -1; + + for (int i = 0; i < MAX_RTPC; i++) { + rtp_content.radiotext[i] = NULL; + rtp_content.radiotext[MAX_RTPC+i] = NULL; + rtp_content.item_Title[i] = NULL; + rtp_content.item_Artist[i] = NULL; + rtp_content.info_Stock[i] = NULL; + rtp_content.info_Sport[i] = NULL; + rtp_content.info_Lottery[i] = NULL; + rtp_content.info_Weather[i] = NULL; + rtp_content.info_Other[i] = NULL; + } + + rtp_content.info_News = NULL; + rtp_content.info_NewsLocal = NULL; + rtp_content.info_DateTime = NULL; + rtp_content.info_Traffic = NULL; + rtp_content.info_Alarm = NULL; + rtp_content.info_Advert = NULL; + rtp_content.info_Url = NULL; + rtp_content.prog_Station = NULL; + rtp_content.prog_Now = NULL; + rtp_content.prog_Next = NULL; + rtp_content.prog_Part = NULL; + rtp_content.prog_Host = NULL; + rtp_content.prog_EditStaff = NULL; + rtp_content.prog_Homepage = NULL; + rtp_content.phone_Hotline = NULL; + rtp_content.phone_Studio = NULL; + rtp_content.email_Hotline = NULL; + rtp_content.email_Studio = NULL; + + // Rass init + Rass_Show = Rass_Archiv = -1; + for (int i = 0; i <= 10; i++) { + for (int ii = 0; ii < 4; ii++) + Rass_Flags[i][ii] = false; + } + Rass_GalStart = Rass_GalEnd = Rass_GalCount = 0; + for (int i = 0; i < RASS_GALMAX; i++) + Rass_Gallery[i] = false; + Rass_SlideFoto = 99; + + if (S_RtFunc < 1) return; + + // RDS-Receiver for seperate Data-PIDs, only Livemode, hardcoded Astra_19E + Hotbird 13E + int pid = 0; + if (!replay) { + switch (chan->Tid()) { + case 1113: + switch (pid = chan->Apid(0)) { // Astra_19.2E - 12633 GHz + /* case 0x161: pid = 0x229; // radio top40 + break; */ + case 0x400: // Hitradio FFH + case 0x406: // planet radio + case 0x40c: pid += 1; // harmony.ffm + break; + default: return; + } + break; + case 1115: + switch (pid = chan->Apid(0)) { // Astra_19.2E - 12663 GHz + case 0x1bA: pid = 0x21e; // TruckRadio, only NaviData(0xbf) seen + break; + default: return; + } + break; + case 5300: + switch (pid = chan->Apid(0)) { // Hotbird_13E - 11747 GHz, no Radiotext @ moment, only TMC + MECs 25/26 + case 0xdc3: // Radio 1 + case 0xdd3: // Radio 3 + case 0xddb: // Radio 5 + case 0xde3: // Radio Exterior + case 0xdeb: pid += 1; // Radio 4 + break; + default: return; + } + break; + default: return; + } + RDSReceiver = new cRDSReceiver(pid); + rdsdevice = cDevice::ActualDevice(); + rdsdevice->AttachReceiver(RDSReceiver); + } +} + +void cRadioAudio::DisableRadioTextProcessing() +{ + RT_Replay = enabled = false; + + // Radiotext & Rass + RT_Info = -1; + RT_ReOpen = false; + Rass_Show = Rass_Archiv = -1; + Rass_GalStart = Rass_GalEnd = Rass_GalCount = 0; + + if (RadioTextOsd != NULL) + RadioTextOsd->Hide(); + + if (RDSReceiver != NULL) { + rdsdevice->Detach(RDSReceiver); + delete RDSReceiver; + RDSReceiver = NULL; + rdsdevice = NULL; + } +} + + +// --- cRadioTextOsd ------------------------------------------------------ + +cBitmap cRadioTextOsd::rds(rds_xpm); +cBitmap cRadioTextOsd::arec(arec_xpm); +cBitmap cRadioTextOsd::rass(rass_xpm); +cBitmap cRadioTextOsd::index(index_xpm); +cBitmap cRadioTextOsd::marker(marker_xpm); +cBitmap cRadioTextOsd::page1(page1_xpm); +cBitmap cRadioTextOsd::pages2(pages2_xpm); +cBitmap cRadioTextOsd::pages3(pages3_xpm); +cBitmap cRadioTextOsd::pages4(pages4_xpm); +cBitmap cRadioTextOsd::no0(no0_xpm); +cBitmap cRadioTextOsd::no1(no1_xpm); +cBitmap cRadioTextOsd::no2(no2_xpm); +cBitmap cRadioTextOsd::no3(no3_xpm); +cBitmap cRadioTextOsd::no4(no4_xpm); +cBitmap cRadioTextOsd::no5(no5_xpm); +cBitmap cRadioTextOsd::no6(no6_xpm); +cBitmap cRadioTextOsd::no7(no7_xpm); +cBitmap cRadioTextOsd::no8(no8_xpm); +cBitmap cRadioTextOsd::no9(no9_xpm); +cBitmap cRadioTextOsd::bok(bok_xpm); +cBitmap cRadioTextOsd::pageE(pageE_xpm); + +cRadioTextOsd::cRadioTextOsd() +{ + RadioTextOsd = this; + osd = NULL; + qosd = NULL; + rtclosed = rassclosed = false; + RT_ReOpen = false; +} + +cRadioTextOsd::~cRadioTextOsd() +{ + if (Rass_Archiv >= 0) { + if (!RT_Replay) + Rass_Archiv = RassImage(-1, -1, false); + else { + Rass_Archiv = -1; + RadioAudio->SetBackgroundImage(ReplayFile); + } + } + + if (osd != NULL) + delete osd; + if (qosd != NULL) + delete qosd; + RadioTextOsd = NULL; + RT_ReOpen = !RT_OsdTO; + + cRemote::Put(LastKey); +} + +void cRadioTextOsd::Show(void) +{ + LastKey = kNone; + RT_OsdTO = false; +#ifndef VDRP_CLOSEMENU + osdtimer.Set(); +#endif + + ftitel = cFont::GetFont(fontOsd); + ftext = cFont::GetFont(fontSml); + fheight = ftext->Height() + 4; + int rowoffset = (S_RtOsdTitle == 1) ? 1 : 0; + bheight = (S_RtOsdTags >=1 ) ? fheight * (S_RtOsdRows+rowoffset+2) : fheight * (S_RtOsdRows+rowoffset); + (S_RtOsdTitle == 1) ? bheight += 20 : bheight += 12; + + asprintf(&RTp_Titel, "%s - %s", tr("RTplus"), RT_Titel); + + if (S_RtDispl >= 1 && (Rass_Show == -1 || S_RassText >= 2)) { + RT_MsgShow = (RT_Info >= 1); + ShowText(); + } +} + +void cRadioTextOsd::Hide(void) +{ + RTOsdClose(); + RassOsdClose(); +} + +void cRadioTextOsd::RTOsdClose(void) +{ + if (osd != NULL) { + delete osd; + osd = NULL; + } +} +#endif + +#if ENABLE_RASS +int CRadioText::RassImage(int QArchiv, int QKey, bool DirUp) +{ + int i; + + if (QKey >= 0 && QKey <= 9) { + if (QArchiv == 0) + (Rass_Flags[QKey][0]) ? QArchiv = QKey * 1000 : QArchiv = 0; + else if (QArchiv > 0) { + if (floor(QArchiv/1000) == QKey) { + for (i = 3; i >= 0; i--) { +// if (fmod(QArchiv, pow(10, i)) == 0) + if ((QArchiv % (i==3 ? 1000 : i==2 ? 100 : i==1 ? 10 : 1)) == 0) + break; + } + + if (i > 0) { + --i; + QArchiv += QKey * (int) (i==3 ? 1000 : i==2 ? 100 : i==1 ? 10 : 1); + } + else + QArchiv = QKey * 1000; + (Rass_Flags[QKey][3-i]) ? : QArchiv = QKey * 1000; + } + else + (Rass_Flags[QKey][0]) ? QArchiv = QKey * 1000 : QArchiv = 0; + } + } + + // Gallery + else if (QKey > 9 && Rass_GalCount >= 0) { + if (QArchiv < Rass_GalStart || QArchiv > Rass_GalEnd) + QArchiv = Rass_GalStart - 1; + if (DirUp) { + for (i = QArchiv+1; i <= Rass_GalEnd; i++) { + if (Rass_Gallery[i]) + break; + } + QArchiv = (i <= Rass_GalEnd) ? i : Rass_GalStart; + } + else { + for (i = QArchiv-1; i >= Rass_GalStart; i--) { + if (Rass_Gallery[i]) + break; + } + QArchiv = (i >= Rass_GalStart) ? i : Rass_GalEnd; + } + } + + // show mpeg-still + char *image; + if (QArchiv >= 0) + asprintf(&image, "%s/Rass_%d.mpg", DataDir, QArchiv); + else + asprintf(&image, "%s/Rass_show.mpg", DataDir); +// Houdini: SetBackgroundImage() does not accept mpg stills +// frameBuffer->useBackground(frameBuffer->loadBackground(image));// set useBackground true or false +// frameBuffer->paintBackground(); +// RadioAudio->SetBackgroundImage(image); + free(image); + + return QArchiv; +} +#endif + +#if 0 +void cRadioTextOsd::RassOsd(void) +{ + int fh = ftext->Height(); + + if (!qosd && !osd && !Skins.IsOpen() && !cOsd::IsOpen()) { + qosd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop+Setup.OSDHeight - (29+264-6+36)); + tArea Area = {0, 0, 97, 29+264+5, 4}; + qosd->SetAreas(&Area, 1); + } + + if (qosd) { + uint32_t bcolor, fcolor; + int skin = theme_skin(); + + // Logo + bcolor = radioSkin[skin].clrTitleBack; + fcolor = radioSkin[skin].clrTitleText; + qosd->DrawRectangle(0, 1, 97, 29, bcolor); + qosd->DrawBitmap(25, 5, rass, bcolor, fcolor); + + // Body + bcolor = radioSkin[skin].clrBack; + fcolor = radioSkin[skin].clrText; + int offs = 29 + 2; + qosd->DrawRectangle(0, offs, 97, 29+264+5, bcolor); + + // Keys+Index + offs += 4; + qosd->DrawBitmap(4, offs, no0, bcolor, fcolor); + qosd->DrawBitmap(44, offs, index, bcolor, fcolor); + qosd->DrawBitmap(4, 24+offs, no1, bcolor, fcolor); + qosd->DrawBitmap(4, 48+offs, no2, bcolor, fcolor); + qosd->DrawBitmap(4, 72+offs, no3, bcolor, fcolor); + qosd->DrawBitmap(4, 96+offs, no4, bcolor, fcolor); + qosd->DrawBitmap(4, 120+offs, no5, bcolor, fcolor); + qosd->DrawBitmap(4, 144+offs, no6, bcolor, fcolor); + qosd->DrawBitmap(4, 168+offs, no7, bcolor, fcolor); + qosd->DrawBitmap(4, 192+offs, no8, bcolor, fcolor); + qosd->DrawBitmap(4, 216+offs, no9, bcolor, fcolor); + qosd->DrawBitmap(4, 240+offs, bok, bcolor, fcolor); + + // Content + bool mark = false; + for (int i = 1; i <= 9; i++) { + // Pages + if (Rass_Flags[i][0] && Rass_Flags[i][1] && Rass_Flags[i][2] && Rass_Flags[i][3]) + qosd->DrawBitmap(48, (i*24)+offs, pages4, bcolor, fcolor); + else if (Rass_Flags[i][0] && Rass_Flags[i][1] && Rass_Flags[i][2]) + qosd->DrawBitmap(48, (i*24)+offs, pages3, bcolor, fcolor); + else if (Rass_Flags[i][0] && Rass_Flags[i][1]) + qosd->DrawBitmap(48, (i*24)+offs, pages2, bcolor, fcolor); + else if (Rass_Flags[i][0]) + qosd->DrawBitmap(48, (i*24)+offs, page1, bcolor, fcolor); + + // Marker + if (floor(Rass_Archiv/1000) == i) { + qosd->DrawBitmap(28, (i*24)+offs, marker, bcolor, fcolor); + mark = true; + } + } + + // Gallery + if (Rass_GalCount > 0) { + char *temp; + qosd->DrawBitmap(48, 240+offs, pageE, bcolor, fcolor); + asprintf(&temp, "%d", Rass_GalCount); + qosd->DrawText(67, 240+offs-2, temp, fcolor, clrTransparent, ftext, 97, fh); + free(temp); + } + + // Marker gallery/index + if (!mark) { + if (Rass_Archiv > 0 && Rass_Archiv <= RASS_GALMAX) + qosd->DrawBitmap(30, 240+offs, marker, bcolor, fcolor); + else + qosd->DrawBitmap(28, offs, marker, bcolor, fcolor); + } + qosd->Flush(); + } +} + +void cRadioTextOsd::RassOsdTip(void) +{ + int fh = ftext->Height(); + + if (!qosd && !osd && !Skins.IsOpen() && !cOsd::IsOpen()) { + qosd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop+Setup.OSDHeight - (29+(2*fh)-6+36)); + tArea Area = {0, 0, 97, 29+(2*fh)+5, 4}; + qosd->SetAreas(&Area, 1); + } + + if (qosd) { + uint32_t bcolor, fcolor; + int skin = theme_skin(); + + // Title + bcolor = radioSkin[skin].clrTitleBack; + fcolor = radioSkin[skin].clrTitleText; + qosd->DrawRectangle(0, 0, 97, 29, bcolor); + qosd->DrawBitmap(25, 5, rass, bcolor, fcolor); + + // Body + bcolor = radioSkin[skin].clrBack; + fcolor = radioSkin[skin].clrText; + qosd->DrawRectangle(0, 29+2, 97, 29+(2*fh)+5, bcolor); + qosd->DrawText(5, 29+4, tr("Records"), fcolor, clrTransparent, ftext, 97, fh); + qosd->DrawText(5, 29+fh+4, tr("with <0>"), fcolor, clrTransparent, ftext, 97, fh); + qosd->Flush(); + } +} + +void cRadioTextOsd::RassOsdClose(void) +{ + if (qosd != NULL) { + delete qosd; + qosd = NULL; + } +} + +void cRadioTextOsd::RassImgSave(char *size, int pos) +{ + char *infile, *outfile, *cmd; + int filenr = 0, error = 0; + struct tm *ts, tm_store; + time_t t = time(NULL); + ts = localtime_r(&t, &tm_store); + + switch (pos) { + // all from 1-9 + case 1 ... 9: + for (int i = 3; i >= 0; i--) { + filenr += (int) (pos * pow(10, i)); + if (Rass_Flags[pos][3-i]) { + asprintf(&infile, "%s/Rass_%d.mpg", DataDir, filenr); + asprintf(&outfile, "%s/Rass_%s-%04d_%02d%02d%02d%02d.jpg", DataDir, RT_Titel, filenr, + ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min); + asprintf(&cmd, "ffmpeg -i %s -s %s -f mjpeg -y %s", infile, size, outfile); + if ((error = system(cmd))) + i = -1; + } + } + asprintf(&cmd, "%s '%d'", tr("Rass-Image(s) saved from Archiv "), pos); + break; + + // all from gallery + case 10: + for (int i = Rass_GalStart; i <= Rass_GalEnd; i++) { + if (Rass_Gallery[i]) { + asprintf(&infile, "%s/Rass_%d.mpg", DataDir, i); + asprintf(&outfile, "%s/Rass_%s-Gallery%04d_%02d%02d.jpg", DataDir, RT_Titel, i, + ts->tm_mon+1, ts->tm_mday); + asprintf(&cmd, "ffmpeg -i %s -s %s -f mjpeg -y %s", infile, size, outfile); + if ((error = system(cmd))) + i = Rass_GalEnd + 1; + } + } + asprintf(&cmd, "%s", tr("Rass-Image(s) saved from Gallery")); + break; + + // single + default: + asprintf(&infile, "%s/Rass_%d.mpg", DataDir, Rass_Archiv); + asprintf(&outfile, "%s/Rass_%s-%04d_%02d%02d%02d%02d.jpg", DataDir, RT_Titel, Rass_Archiv, + ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min); + asprintf(&cmd, "ffmpeg -i %s -s %s -f mjpeg -y %s", infile, size, outfile); + error = system(cmd); + asprintf(&cmd, "%s: %s", tr("Rass-Image saved"), outfile); + } + free(infile); + + // Info + RassOsdClose(); + if (error) { + asprintf(&cmd, "%s: %s", tr("Rass-Image failed"), outfile); + Skins.Message(mtError, cmd, Setup.OSDMessageTime); + } + else + Skins.Message(mtInfo, cmd, Setup.OSDMessageTime); + + free(outfile); + free(cmd); +} + +void cRadioTextOsd::rtp_print(void) +{ + struct tm tm_store; + time_t t = time(NULL); + + printf("\n>>> RTplus-Memoryclasses @ %s", asctime(localtime_r(&t, &tm_store))); + printf(" on '%s' since %s", RT_Titel, asctime(localtime_r(&rtp_content.start, &tm_store))); + + printf("--- Programme ---\n"); + if (rtp_content.prog_Station != NULL) printf(" Station: %s\n", rtp_content.prog_Station); + if (rtp_content.prog_Now != NULL) printf(" Now: %s\n", rtp_content.prog_Now); + if (rtp_content.prog_Next != NULL) printf(" Next: %s\n", rtp_content.prog_Next); + if (rtp_content.prog_Part != NULL) printf(" Part: %s\n", rtp_content.prog_Part); + if (rtp_content.prog_Host != NULL) printf(" Host: %s\n", rtp_content.prog_Host); + if (rtp_content.prog_EditStaff != NULL) printf(" Ed.Staff: %s\n", rtp_content.prog_EditStaff); + if (rtp_content.prog_Homepage != NULL) printf(" Homepage: %s\n", rtp_content.prog_Homepage); + + printf("--- Interactivity ---\n"); + if (rtp_content.phone_Hotline != NULL) printf(" Phone-Hotline: %s\n", rtp_content.phone_Hotline); + if (rtp_content.phone_Studio != NULL) printf(" Phone-Studio: %s\n", rtp_content.phone_Studio); + if (rtp_content.email_Hotline != NULL) printf(" Email-Hotline: %s\n", rtp_content.email_Hotline); + if (rtp_content.email_Studio != NULL) printf(" Email-Studio: %s\n", rtp_content.email_Studio); + + printf("--- Info ---\n"); + if (rtp_content.info_News != NULL) printf(" News: %s\n", rtp_content.info_News); + if (rtp_content.info_NewsLocal != NULL) printf(" NewsLocal: %s\n", rtp_content.info_NewsLocal); + if (rtp_content.info_DateTime != NULL) printf(" DateTime: %s\n", rtp_content.info_DateTime); + if (rtp_content.info_Traffic != NULL) printf(" Traffic: %s\n", rtp_content.info_Traffic); + if (rtp_content.info_Alarm != NULL) printf(" Alarm: %s\n", rtp_content.info_Alarm); + if (rtp_content.info_Advert != NULL) printf(" Advertisg: %s\n", rtp_content.info_Advert); + if (rtp_content.info_Url != NULL) printf(" Url: %s\n", rtp_content.info_Url); + // no sorting + for (int i = 0; i < MAX_RTPC; i++) + if (rtp_content.info_Stock[i] != NULL) printf(" Stock[%02d]: %s\n", i, rtp_content.info_Stock[i]); + for (int i = 0; i < MAX_RTPC; i++) + if (rtp_content.info_Sport[i] != NULL) printf(" Sport[%02d]: %s\n", i, rtp_content.info_Sport[i]); + for (int i = 0; i < MAX_RTPC; i++) + if (rtp_content.info_Lottery[i] != NULL) printf(" Lottery[%02d]: %s\n", i, rtp_content.info_Lottery[i]); + for (int i = 0; i < MAX_RTPC; i++) + if (rtp_content.info_Weather[i] != NULL) printf(" Weather[%02d]: %s\n", i, rtp_content.info_Weather[i]); + for (int i = 0; i < MAX_RTPC; i++) + if (rtp_content.info_Other[i] != NULL) printf(" Other[%02d]: %s\n", i, rtp_content.info_Other[i]); +/* + printf("--- Item-Playlist ---\n"); + // no sorting + if (rtp_content.item_Index >= 0) { + for (int i = 0; i < MAX_RTPC; i++) { + if (rtp_content.item_Title[i] != NULL && rtp_content.item_Artist[i] != NULL) { + struct tm tm_store; + struct tm *ts = localtime_r(&rtp_content.item_Start[i], &tm_store); + printf(" [%02d] %02d:%02d Title: %s | Artist: %s\n", + i, ts->tm_hour, ts->tm_min, rtp_content.item_Title[i], rtp_content.item_Artist[i]); + } + } + } + + printf("--- Last seen Radiotext ---\n"); + // no sorting + if (rtp_content.rt_Index >= 0) { + for (int i = 0; i < 2*MAX_RTPC; i++) + if (rtp_content.radiotext[i] != NULL) printf(" [%03d] %s\n", i, rtp_content.radiotext[i]); + } +*/ + printf("<<<\n"); +} + +#define rtplog 0 +eOSState cRadioTextOsd::ProcessKey(eKeys Key) +{ + // RTplus Infolog + if (rtplog == 1 && S_Verbose >= 1) { + static int ct = 0; + if (++ct >= 60) { + ct = 0; + rtp_print(); + } + } + + // check end @ replay + if (RT_Replay) { + int rplayCur, rplayTot; + cControl::Control()->GetIndex(rplayCur, rplayTot, false); + if (rplayCur >= rplayTot-1) { + Hide(); + return osEnd; + } + } + + // Timeout or no Info/Rass + if (RT_OsdTO || (RT_OsdTOTemp > 0) || (RT_Info < 0)) { + Hide(); + return osEnd; + } + + eOSState state = cOsdObject::ProcessKey(Key); + if (state != osUnknown) return state; + + // Key pressed ... + if (Key != kNone && Key < k_Release) { + if (osd) { // Radiotext, -plus Osd + switch (Key) { + case kBack: RTOsdClose(); + rtclosed = true; + //rassclosed = false; + break; + case k0: RTOsdClose(); + RTplus_Osd = true; + cRemote::CallPlugin("radio"); + return osEnd; + default: Hide(); + LastKey = (Key == kChanUp || Key == kChanDn) ? kNone : Key; + return osEnd; + } + } + else if (qosd && Rass_Archiv >= 0) { // Rass-Archiv Osd + int i, pos; + pos = (Rass_Archiv > 0 && Rass_Archiv <= RASS_GALMAX) ? 10 : (int) floor(Rass_Archiv/1000); + switch (Key) { + // back to Slideshow + case kBlue: + case kBack: + if (!RT_Replay) + Rass_Archiv = RassImage(-1, 0, false); + else { + Rass_Archiv = -1; + RadioAudio->SetBackgroundImage(ReplayFile); + } + RassOsdClose(); + rassclosed = rtclosed = false; + break; + + // Archiv-Sides + case k0 ... k9: + Rass_Archiv = RassImage(Rass_Archiv, Key-k0, false); + RassOsd(); + break; + + case kOk: + if (Rass_Flags[10][0]) { + Rass_Archiv = RassImage(Rass_Archiv, 10, true); + RassOsd(); + } + break; + + case kLeft: + case kRight: + Rass_Archiv = RassImage(Rass_Archiv, pos, (Key == kRight) ? true : false); + RassOsd(); + break; + case kDown: + (pos == 10) ? i = 0 : i = pos + 1; + while (i != pos) { + if (Rass_Flags[i][0]) { + Rass_Archiv = RassImage(Rass_Archiv, i, true); + RassOsd(); + return osContinue; + } + if (++i > 10) i = 0; + } + break; + + case kUp: + (pos == 0) ? i = 10 : i = pos - 1; + while (i != pos) { + if (Rass_Flags[i][0]) { + Rass_Archiv = RassImage(Rass_Archiv, i, true); + RassOsd(); + return osContinue; + } + if (--i < 0) i = 10; + } + break; + + case kRed: + RassImgSave("1024x576", 0); + break; + + case kGreen: + RassImgSave("1024x576", pos); + break; + + case kYellow: + break; // todo, what ? + + default: + Hide(); + LastKey = (Key == kChanUp || Key == kChanDn) ? kNone : Key; + return osEnd; + } + } + else if (qosd && Rass_Archiv == -1) { // Rass-Slideshow Osd + switch (Key) { + // close + case kBack: + RassOsdClose(); + rassclosed = true; + //rtclosed = false; + break; + + // Archiv-Index + case k0: + if (Rass_Flags[0][0]) { + RassOsdClose(); + Rass_Archiv = RassImage(0, 0, false); + RassOsd(); + } + break; + + default: + Hide(); + LastKey = (Key == kChanUp || Key == kChanDn) ? kNone : Key; + return osEnd; + } + } + else { // no RT && no Rass + Hide(); + LastKey = (Key == kChanUp || Key == kChanDn) ? kNone : Key; + return osEnd; + } + } + // no Key pressed ... +#ifndef VDRP_CLOSEMENU + else if (S_RtOsdTO > 0 && osdtimer.Elapsed()/1000/60 >= (uint)S_RtOsdTO) { + RT_OsdTO = true; + Hide(); + return osEnd; + } +#endif + else if (Rass_Archiv >= 0) + RassOsd(); + else if (RT_MsgShow && !rtclosed && (Rass_Show == -1 || S_RassText >= 2 || rassclosed)) { + RassOsdClose(); + ShowText(); + } + else if (Rass_Flags[0][0] && !rassclosed && (S_RassText < 2 || rtclosed)) { + RTOsdClose(); + RassOsdTip(); + } + + return osContinue; +} + + +// --- cRTplusOsd ------------------------------------------------------ + +cRTplusOsd::cRTplusOsd(void) +:cOsdMenu(RTp_Titel, 3, 12) +{ + RTplus_Osd = false; + + bcount = helpmode = 0; + listtyp[0] = tr("Radiotext"); + listtyp[1] = tr("Playlist"); + listtyp[2] = tr("Sports"); + listtyp[3] = tr("Lottery"); + listtyp[4] = tr("Weather"); + listtyp[5] = tr("Stockmarket"); + listtyp[6] = tr("Other"); + + Load(); + Display(); +} + +cRTplusOsd::~cRTplusOsd() +{ +} + +void cRTplusOsd::Load(void) +{ + char text[80]; + + struct tm tm_store; + struct tm *ts = localtime_r(&rtp_content.start, &tm_store); + snprintf(text, sizeof(text), "%s %02d:%02d", tr("RTplus Memory since"), ts->tm_hour, ts->tm_min); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + + snprintf(text, sizeof(text), "-- %s --", tr("Programme")); + Add(new cOsdItem(hk(text))); + if (rtp_content.prog_Station != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Station"), rtp_content.prog_Station); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_Now != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Now"), rtp_content.prog_Now); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_Part != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("...Part"), rtp_content.prog_Part); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_Next != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Next"), rtp_content.prog_Next); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_Host != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Host"), rtp_content.prog_Host); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_EditStaff != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Edit.Staff"), rtp_content.prog_EditStaff); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.prog_Homepage != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Homepage"), rtp_content.prog_Homepage); + Add(new cOsdItem(hk(text))); + } + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + + snprintf(text, sizeof(text), "-- %s --", tr("Interactivity")); + Add(new cOsdItem(hk(text))); + if (rtp_content.phone_Hotline != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Phone-Hotline"), rtp_content.phone_Hotline); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.phone_Studio != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Phone-Studio"), rtp_content.phone_Studio); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.email_Hotline != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Email-Hotline"), rtp_content.email_Hotline); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.email_Studio != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Email-Studio"), rtp_content.email_Studio); + Add(new cOsdItem(hk(text))); + } + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + + snprintf(text, sizeof(text), "-- %s --", tr("Info")); + Add(new cOsdItem(hk(text))); + if (rtp_content.info_News != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("News"), rtp_content.info_News); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_NewsLocal != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("NewsLocal"), rtp_content.info_NewsLocal); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_DateTime != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("DateTime"), rtp_content.info_DateTime); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_Traffic != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Traffic"), rtp_content.info_Traffic); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_Alarm != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Alarm"), rtp_content.info_Alarm); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_Advert != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Advertising"), rtp_content.info_Advert); + Add(new cOsdItem(hk(text))); + } + if (rtp_content.info_Url != NULL) { + snprintf(text, sizeof(text), "\t%s:\t%s", tr("Url"), rtp_content.info_Url); + Add(new cOsdItem(hk(text))); + } + + for (int i = 0; i <= 6; i++) + btext[i] = NULL; + bcount = 0; + asprintf(&btext[bcount++], "%s", listtyp[0]); + if (rtp_content.item_Index >= 0) + asprintf(&btext[bcount++], "%s", listtyp[1]); + if (rtp_content.info_SportIndex >= 0) + asprintf(&btext[bcount++], "%s", listtyp[2]); + if (rtp_content.info_LotteryIndex >= 0) + asprintf(&btext[bcount++], "%s", listtyp[3]); + if (rtp_content.info_WeatherIndex >= 0) + asprintf(&btext[bcount++], "%s", listtyp[4]); + if (rtp_content.info_StockIndex >= 0) + asprintf(&btext[bcount++], "%s", listtyp[5]); + if (rtp_content.info_OtherIndex >= 0) + asprintf(&btext[bcount++], "%s", listtyp[6]); + + switch (bcount) { + case 4: if (helpmode == 0) + SetHelp(btext[0], btext[1], btext[2], ">>"); + else if (helpmode == 1) + SetHelp("<<", btext[3], NULL, tr("Exit")); + break; + case 5: if (helpmode == 0) + SetHelp(btext[0], btext[1], btext[2], ">>"); + else if (helpmode == 1) + SetHelp("<<", btext[3], btext[4], tr("Exit")); + break; + case 6: if (helpmode == 0) + SetHelp(btext[0], btext[1], btext[2], ">>"); + else if (helpmode == 1) + SetHelp("<<", btext[3], btext[4], ">>"); + else if (helpmode == 2) + SetHelp("<<", btext[5], NULL, tr("Exit")); + break; + case 7: if (helpmode == 0) + SetHelp(btext[0], btext[1], btext[2], ">>"); + else if (helpmode == 1) + SetHelp("<<", btext[3], btext[4], ">>"); + else if (helpmode == 2) + SetHelp("<<", btext[5], btext[6], tr("Exit")); + break; + default: helpmode = 0; + SetHelp(btext[0], btext[1], btext[2], tr("Exit")); + } +} + +void cRTplusOsd::Update(void) +{ + Clear(); + Load(); + Display(); +} + +int cRTplusOsd::rtptyp(char *btext) +{ + for (int i = 0; i <= 6; i++) { + if (strcmp(btext, listtyp[i]) == 0) + return i; + } + + return -1; +} + +void cRTplusOsd::rtp_fileprint(void) +{ + struct tm *ts, tm_store; + char *fname, *fpath; + FILE *fd; + int ind, lfd = 0; + + time_t t = time(NULL); + ts = localtime_r(&t, &tm_store); + asprintf(&fname, "RTplus_%s_%04d-%02d-%02d.%02d.%02d", RT_Titel, ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min); + asprintf(&fpath, "%s/%s", DataDir, fname); + if ((fd = fopen(fpath, "w")) != NULL) { + + fprintf(fd, ">>> RTplus-Memoryclasses @ %s", asctime(localtime_r(&t, &tm_store))); + fprintf(fd, " on '%s' since %s", RT_Titel, asctime(localtime_r(&rtp_content.start, &tm_store))); + + fprintf(fd, "--- Programme ---\n"); + if (rtp_content.prog_Station != NULL) fprintf(fd, " Station: %s\n", rtp_content.prog_Station); + if (rtp_content.prog_Now != NULL) fprintf(fd, " Now: %s\n", rtp_content.prog_Now); + if (rtp_content.prog_Part != NULL) fprintf(fd, " Part: %s\n", rtp_content.prog_Part); + if (rtp_content.prog_Next != NULL) fprintf(fd, " Next: %s\n", rtp_content.prog_Next); + if (rtp_content.prog_Host != NULL) fprintf(fd, " Host: %s\n", rtp_content.prog_Host); + if (rtp_content.prog_EditStaff != NULL) fprintf(fd, " Ed.Staff: %s\n", rtp_content.prog_EditStaff); + if (rtp_content.prog_Homepage != NULL) fprintf(fd, " Homepage: %s\n", rtp_content.prog_Homepage); + + fprintf(fd, "--- Interactivity ---\n"); + if (rtp_content.phone_Hotline != NULL) fprintf(fd, " Phone-Hotline: %s\n", rtp_content.phone_Hotline); + if (rtp_content.phone_Studio != NULL) fprintf(fd, " Phone-Studio: %s\n", rtp_content.phone_Studio); + if (rtp_content.email_Hotline != NULL) fprintf(fd, " Email-Hotline: %s\n", rtp_content.email_Hotline); + if (rtp_content.email_Studio != NULL) fprintf(fd, " Email-Studio: %s\n", rtp_content.email_Studio); + + fprintf(fd, "--- Info ---\n"); + if (rtp_content.info_News != NULL) fprintf(fd, " News: %s\n", rtp_content.info_News); + if (rtp_content.info_NewsLocal != NULL) fprintf(fd, " NewsLocal: %s\n", rtp_content.info_NewsLocal); + if (rtp_content.info_DateTime != NULL) fprintf(fd, " DateTime: %s\n", rtp_content.info_DateTime); + if (rtp_content.info_Traffic != NULL) fprintf(fd, " Traffic: %s\n", rtp_content.info_Traffic); + if (rtp_content.info_Alarm != NULL) fprintf(fd, " Alarm: %s\n", rtp_content.info_Alarm); + if (rtp_content.info_Advert != NULL) fprintf(fd, " Advertisg: %s\n", rtp_content.info_Advert); + if (rtp_content.info_Url != NULL) fprintf(fd, " Url: %s\n", rtp_content.info_Url); + + if (rtp_content.item_Index >= 0) { + fprintf(fd, "--- Item-Playlist ---\n"); + ind = rtp_content.item_Index; + if (ind < (MAX_RTPC-1) && rtp_content.item_Title[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.item_Title[i] != NULL && rtp_content.item_Artist[i] != NULL) { + ts = localtime_r(&rtp_content.item_Start[i], &tm_store); + fprintf(fd, " %02d:%02d Title: '%s' | Artist: '%s'\n", ts->tm_hour, ts->tm_min, rtp_content.item_Title[i], rtp_content.item_Artist[i]); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.item_Title[i] != NULL && rtp_content.item_Artist[i] != NULL) { + ts = localtime_r(&rtp_content.item_Start[i], &tm_store); + fprintf(fd, " %02d:%02d Title: '%s' | Artist: '%s'\n", ts->tm_hour, ts->tm_min, rtp_content.item_Title[i], rtp_content.item_Artist[i]); + } + } + } + + if (rtp_content.info_SportIndex >= 0) { + fprintf(fd, "--- Sports ---\n"); + ind = rtp_content.info_SportIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Sport[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Sport[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Sport[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Sport[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Sport[i]); + } + } + + if (rtp_content.info_LotteryIndex >= 0) { + fprintf(fd, "--- Lottery ---\n"); + ind = rtp_content.info_LotteryIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Lottery[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Lottery[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Lottery[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Lottery[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Lottery[i]); + } + } + + if (rtp_content.info_WeatherIndex >= 0) { + fprintf(fd, "--- Weather ---\n"); + ind = rtp_content.info_WeatherIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Weather[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Weather[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Weather[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Weather[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Weather[i]); + } + } + + if (rtp_content.info_StockIndex >= 0) { + fprintf(fd, "--- Stockmarket ---\n"); + ind = rtp_content.info_StockIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Stock[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Stock[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Stock[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Stock[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Stock[i]); + } + } + + if (rtp_content.info_OtherIndex >= 0) { + fprintf(fd, "--- Other ---\n"); + ind = rtp_content.info_OtherIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Other[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Other[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Other[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Other[i] != NULL) + fprintf(fd, " %02d. %s\n", ++lfd, rtp_content.info_Other[i]); + } + } + + fprintf(fd, "--- Last seen Radiotext ---\n"); + ind = rtp_content.rt_Index; + if (ind < (2*MAX_RTPC-1) && rtp_content.radiotext[ind+1] != NULL) { + for (int i = ind+1; i < 2*MAX_RTPC; i++) { + if (rtp_content.radiotext[i] != NULL) + fprintf(fd, " %03d. %s\n", ++lfd, rtp_content.radiotext[i]); + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.radiotext[i] != NULL) + fprintf(fd, " %03d. %s\n", ++lfd, rtp_content.radiotext[i]); + } + + fprintf(fd, "<<<\n"); + fclose(fd); + + char *infotext; + asprintf(&infotext, "%s: %s", tr("RTplus-File saved"), fname); + Skins.Message(mtInfo, infotext, Setup.OSDMessageTime); + free(infotext); + } + else + printfesyslog("ERROR vdr-radio: writing RTplus-File failed '%s'", fpath); + + free(fpath); + free(fname); +} + +eOSState cRTplusOsd::ProcessKey(eKeys Key) +{ + int typ, ind; + eOSState state = cOsdMenu::ProcessKey(Key); + + if (HasSubMenu()) + return osContinue; + + if (state == osUnknown) { + switch (Key) { + case kBack: + case kOk: + return osEnd; + + case kBlue: + if (bcount >= 4 && helpmode == 0) { + helpmode += 1; + Update(); + } + else if (bcount >= 6 && helpmode == 1) { + helpmode += 1; + Update(); + } + else + return osEnd; + break; + + case k0: + Update(); + break; + + case k8: + rtp_fileprint(); + break; + + case kRed: + if (helpmode == 0) { + if (btext[0] != NULL) + if ((typ = rtptyp(btext[0])) >= 0) + AddSubMenu(new cRTplusList(typ)); + } + else { + helpmode -= 1; + Update(); + } + break; + + case kGreen: + ind = (helpmode*2) + 1; + if (btext[ind] != NULL) { + if ((typ = rtptyp(btext[ind])) >= 0) + AddSubMenu(new cRTplusList(typ)); + } + break; + + case kYellow: + ind = (helpmode*2) + 2; + if (btext[ind] != NULL) { + if ((typ = rtptyp(btext[ind])) >= 0) + AddSubMenu(new cRTplusList(typ)); + } + break; + default: + state = osContinue; + } + } + + static int ct; + if (++ct >= 60) { + ct = 0; + Update(); + } + + return state; +} + + +// --- cRTplusList ------------------------------------------------------ + +cRTplusList::cRTplusList(int Typ) +:cOsdMenu(RTp_Titel, 4) +{ + typ = Typ; + refresh = false; + + Load(); + Display(); +} + +cRTplusList::~cRTplusList() +{ + typ = 0; +} + +void cRTplusList::Load(void) +{ + char text[80]; + struct tm *ts, tm_store; + int ind, lfd = 0; + + ts = localtime_r(&rtp_content.start, &tm_store); + switch (typ) { + case 0: + snprintf(text, sizeof(text), "-- %s (max. %d) --", tr("last seen Radiotext"), 2*MAX_RTPC); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.rt_Index; + if (ind < (2*MAX_RTPC-1) && rtp_content.radiotext[ind+1] != NULL) { + for (int i = ind+1; i < 2*MAX_RTPC; i++) { + if (rtp_content.radiotext[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.radiotext[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.radiotext[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.radiotext[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + + case 1: + SetCols(6, 19, 1); + snprintf(text, sizeof(text), "-- %s --", tr("Playlist")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s\t%s\t\t%s", tr("Time"), tr("Title"), tr("Artist")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.item_Index; + if (ind < (MAX_RTPC-1) && rtp_content.item_Title[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.item_Title[i] != NULL && rtp_content.item_Artist[i] != NULL) { + ts = localtime_r(&rtp_content.item_Start[i], &tm_store); + snprintf(text, sizeof(text), "%02d:%02d\t%s\t\t%s", ts->tm_hour, ts->tm_min, rtp_content.item_Title[i], rtp_content.item_Artist[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.item_Title[i] != NULL && rtp_content.item_Artist[i] != NULL) { + ts = localtime_r(&rtp_content.item_Start[i], &tm_store); + snprintf(text, sizeof(text), "%02d:%02d\t%s\t\t%s", ts->tm_hour, ts->tm_min, rtp_content.item_Title[i], rtp_content.item_Artist[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + + case 2: + snprintf(text, sizeof(text), "-- %s --", tr("Sports")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.info_SportIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Sport[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Sport[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Sport[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Sport[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Sport[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + + case 3: + snprintf(text, sizeof(text), "-- %s --", tr("Lottery")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.info_LotteryIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Lottery[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Lottery[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Lottery[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Lottery[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Lottery[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + + case 4: snprintf(text, sizeof(text), "-- %s --", tr("Weather")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.info_WeatherIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Weather[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Weather[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Weather[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Weather[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Weather[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + case 5: + snprintf(text, sizeof(text), "-- %s --", tr("Stockmarket")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.info_StockIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Stock[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Stock[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Stock[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Stock[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Stock[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + + case 6: + snprintf(text, sizeof(text), "-- %s --", tr("Other")); + Add(new cOsdItem(hk(text))); + snprintf(text, sizeof(text), "%s", " "); + Add(new cOsdItem(hk(text))); + ind = rtp_content.info_OtherIndex; + if (ind < (MAX_RTPC-1) && rtp_content.info_Other[ind+1] != NULL) { + for (int i = ind+1; i < MAX_RTPC; i++) { + if (rtp_content.info_Other[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Other[i]); + Add(new cOsdItem(hk(text))); + } + } + } + for (int i = 0; i <= ind; i++) { + if (rtp_content.info_Other[i] != NULL) { + snprintf(text, sizeof(text), "%d.\t%s", ++lfd, rtp_content.info_Other[i]); + Add(new cOsdItem(hk(text)), refresh); + } + } + break; + } + + SetHelp(NULL, NULL , refresh ? tr("Refresh Off") : tr("Refresh On"), tr("Back")); +} + +void cRTplusList::Update(void) +{ + Clear(); + Load(); + Display(); +} + +eOSState cRTplusList::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case k0: + Update(); + break; + case kYellow: + refresh = (refresh) ? false : true; + Update(); + break; + case kBack: + case kOk: + case kBlue: + return osBack; + default: + state = osContinue; + } + } + + static int ct; + if (refresh) { + if (++ct >= 20) { + ct = 0; + Update(); + } + } + + return state; +} + +//--------------- End ----------------------------------------------------------------- +#endif + + +static int pes_SyncBufferRead (cDemux *audioDemux, ringbuffer_t *buf, u_long *skipped_bytes); + +static bool rtThreadRunning; + +void *RadioTextThread(void *data) +{ + CRadioText *rt = ((CRadioText::s_rt_thread*)data)->rt_object; + int fd = ((CRadioText::s_rt_thread*)data)->fd; +// struct dmx_pes_filter_params flt; + cDemux *audioDemux = rt->audioDemux; +printf("in RadioTextThread fd = %d\n", fd); + +// while (1) + { + bool ret = false; + + printf("Thread Setting PID 0x%x\n", rt->getPid()); + + audioDemux->Stop(); + if (audioDemux->pesFilter(rt->getPid()) >= 0) + { + /* start demux filter */ + if (audioDemux->Start() >= 0) + ret = true; + } + if (!ret) { + perror("RadiotextThread Audiodemuxer"); + perror("DMX_SET_PES_FILTER"); + pthread_exit(NULL); + } +#if 0 + + ioctl(fd, DMX_SET_BUFFER_SIZE, 128*1024); + + flt.pid = rt->getPid(); + flt.pes_type = DMX_PES_OTHER; + flt.flags = DMX_IMMEDIATE_START; +#ifndef HAVE_TRIPLEDRAGON + flt.input = DMX_IN_FRONTEND; + flt.output = DMX_OUT_TAP; +#else + flt.output = OUT_MEMORY; + flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; + flt.unloader.threshold = 64; +#endif + if (ioctl(fd, DMX_SET_PES_FILTER, &flt) < 0) { + perror("DMX_SET_PES_FILTER"); + pthread_exit(NULL); + } +#endif + /* + -- read PES packet for pid + */ + ringbuffer_t *buf_in = ringbuffer_create(0x1FFFF); + char *b; /* ptr to packet start */ + long count = 0; + rtThreadRunning = true; + while(rtThreadRunning) + { + int n; + int offset; + size_t rd; + u_long skipped_bytes = 0; + +//printf("."); fflush(stdout); + // -- Read PES packet (sync Read) + n = pes_SyncBufferRead (audioDemux, buf_in, &skipped_bytes); + + // -- error or eof? + if (n <= 0) { + usleep(10000); /* save CPU if nothing read */ + continue; + } + rd = ringbuffer_get_readpointer(buf_in, &b, n); +/* this can not happen, because pes_SyncBufferRead() returns -1 if ringbuffer is empty + if (rd <= 0) { + continue; + } +*/ + count ++; + // -- skipped Data to get sync byte? + offset = rt->PES_Receive(b, n); +// if (skipped_bytes) { +// printf("!!! %ld bytes skipped to get PS/PES sync!!! read: %d processed: %d\n",skipped_bytes, n, offset); +// } + ringbuffer_read_advance(buf_in, offset); + } + + if (audioDemux->Stop() < 0) { +// if (ioctl(fd, DMX_STOP) < 0) { + perror("DMX_STOP"); + pthread_exit(NULL); + } + ringbuffer_free(buf_in); + } +fprintf(stderr, "RT %s: exit\n", __FUNCTION__); + pthread_exit(NULL); +} + +CRadioText::CRadioText(void) +{ + pid = 0; + dmxfd = -1; + S_Verbose = 0; + S_RtFunc = 1; + S_RtOsd = 1; + S_RtOsdTitle = 1; + S_RtOsdTags = 2; + S_RtOsdPos = 2; + S_RtOsdRows = 3; + S_RtOsdLoop = 1; + S_RtOsdTO = 60; + S_RtSkinColor = false; + S_RtBgCol = 0; + S_RtBgTra = 0xA0; + S_RtFgCol = 1; + S_RtDispl = 1; + S_RtMsgItems = 0; +//int S_RtpMemNo = 25; + RT_Index = 0; + RT_PTY = 0; + + // Radiotext + RTP_ItemToggle = 1; + RTP_TToggle = 0; + RT_PlusShow = false; + RT_Replay = false; + RT_ReOpen = false; + for (int i=0; i<5; i++) strcpy(RT_Text[i], ""); + strcpy(RDS_PTYN, ""); + have_radiotext = false; + audioDemux = NULL; +} + +void CRadioText::radiotext_stop(void) +{ + if (getPid() != 0) { + // this stuff takes a while sometimes - look for a better syncronisation + printf("Stopping RT Thread\n"); + rtThreadRunning = false; + pthread_join(getThread(), NULL); + pid = 0; + have_radiotext = false; + audioDemux->Stop(); + S_RtOsd = 0; + } + +} + +CRadioText::~CRadioText(void) +{ + radiotext_stop(); + pid = 0; +// printf("Deleting RT object\n"); + +// close(dmxfd); + delete audioDemux; + audioDemux = NULL; + dmxfd = -1; +} + + +void CRadioText::setPid(uint inPid) +{ + uint oldPid = pid; + + if (pid != inPid) + { + int rc; + +// printf("setting pid 0x%x\n", inPid); + pid = inPid; + + // open the device if first pid + if (0 == oldPid) + { + if (audioDemux == NULL) { + bool ret = false; + audioDemux = new cDemux(1); + audioDemux->Open(DMX_TP_CHANNEL/*DMX_AUDIO_CHANNEL*/,0,128*1024); + if (audioDemux->pesFilter(pid) >= 0) + { + /* start demux filter */ + if (audioDemux->Start() >= 0) + ret = true; + } + if (!ret) { + perror("Radiotext Audiodemuxer"); + pthread_exit(NULL); + } +// audioDemux->Stop() +#if 0 + dmxfd = open(DMXDEV, O_RDWR|O_NONBLOCK); + if (dmxfd < 0) { + perror(DMXDEV); + pthread_exit(NULL); + } +#endif + } + rt.rt_object = this; + rt.fd = dmxfd; + } + + // Setup-Params +// S_Activate = false; +// S_HMEntry = false; + S_RtFunc = 1; + S_RtOsd = 0; + S_RtOsdTitle = 1; + S_RtOsdTags = 2; + S_RtOsdPos = 2; + S_RtOsdRows = 3; + S_RtOsdLoop = 1; + S_RtOsdTO = 60; + S_RtSkinColor = false; + S_RtBgCol = 0; + S_RtBgTra = 0xA0; + S_RtFgCol = 1; + S_RtDispl = 1; + S_RtMsgItems = 0; +//int S_RtpMemNo = 25; + RT_Index = RT_PTY = 0; + + // Radiotext + RTP_ItemToggle = 1; + RTP_TToggle = 0; + RT_PlusShow = false; + RT_Replay = false; + RT_ReOpen = false; + for (int i=0; i<5; i++) strcpy(RT_Text[i], ""); + strcpy(RDS_PTYN, ""); + +#if ENABLE_RASS + // Rass ... + Rass_Show = -1; // -1=No, 0=Yes, 1=display + Rass_Archiv = -1; // -1=Off, 0=Index, 1000-9990=Slidenr. +#endif + RT_MsgShow = false; // clear entries from old channel + + rc = pthread_create(&threadRT, 0, RadioTextThread, (void *) &rt); + + if (rc) { + printf("failed to create RadioText Thread (rc=%d)\n", rc); + return; + } + } +} + + +// ----------------------------------------------------------- +// following functions are ripped from dvbsnoop: http://dvbsnoop.sourceforge.net + +/* + -- read PES packet (Synced) + -- buffer pre-read bytes for next execution + -- return: len // read()-return code +*/ + +static int pes_SyncBufferRead(cDemux *audioDemux, ringbuffer_t *buf, /*u_long max_len,*/ u_long *skipped_bytes) +{ + ringbuffer_data_t vec; + int rd; + char *ppes; + + // -- simple PES sync... seek for 0x000001 (PES_SYNC_BYTE) + // ISO/IEC 13818-1: + // -- packet_start_code_prefix -- The packet_start_code_prefix is + // -- a 24-bit code. Together with the stream_id that follows it constitutes + // -- a packet start code that identifies the beginning of a packet. + // -- The packet_start_code_prefix is the bit stream + // -- '0000 0000 0000 0000 0000 0001' (0x000001). + + *skipped_bytes = 0; + ringbuffer_get_write_vector(buf, &vec); + if (vec.len == 0) + { + fprintf(stderr, "RT %s: ringbuffer full\n", __FUNCTION__); + /* do not read anything from demux, but continue with sync */ + } + else + { + rd = audioDemux->Read((unsigned char*)vec.buf, vec.len, 5000); + if (rd < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr, "RT %s: read %d errno %d (%m)\n", __FUNCTION__, rd, errno); + return rd; + } + /* if EAGAIN, still process contents of ringbuffer */ + rd = 0; + } + + ringbuffer_write_advance(buf, rd); + } + + rd = ringbuffer_get_readpointer(buf, &ppes, 6); + if (rd < 6) + { +// fprintf(stderr, "RT %s: ringbuffer empty (%d < 6)\n", __FUNCTION__, rd); + return -1; + } + + if ((ppes[0] != 0x00) || (ppes[1] != 0x00) || (ppes[2] != 0x01)) + { + //INFO("async, not 000001: %02x%02x%02x ", ppes[0], ppes[1], ppes[2]); + int deleted = 0; + do { + ringbuffer_read_advance(buf, 1); // remove 1 Byte + rd = ringbuffer_get_readpointer(buf, &ppes, 6); + deleted++; + (*skipped_bytes)++; + //fprintf(stderr, "%d", rd); + if ((ppes[0] == 0x00) || (ppes[1] == 0x00) || (ppes[2] == 0x01)) + { + deleted = 0; + break; + } + } + while (rd == 6); + //fprintf(stderr, "\n"); + if (deleted > 0) + { +// fprintf(stderr, "RT %s: No valid PES signature found. %d Bytes deleted.\n", __FUNCTION__, deleted); + return -1; + } + } + + // -- Sync found! + // -- evaluate packet_id and seek packet end (next sync) + if (ppes[3] >= 0xBC) { // PES system packet with length + unsigned int l; + l = (ppes[4] << 8) + ppes[5]; // PES packet size... + + if (l > 0) { + if (ringbuffer_read_space(buf) >= l + 6) + return (l + 6); + else + return 0; + } else + ringbuffer_read_advance(buf, 6); + } else { + // will resync automatically on next invocation. Enough? + ringbuffer_read_advance(buf, 4); + } + +#if 0 + // -- seek packet end (sync to next packet) + // -- ISO 13818-1 length=0 packets (unbound video streams) + // -- ISO 13818-2 packets + + sync = 0xFFFFFFFF; + while (max_len > 0) { + u_char c; + int n; + + // $$$ TODO: may be optimized + n = pes_rawBufferedRead (fd, buf,1); + if (n < 0) return n; + + c = *buf++; + max_len -= n; + + // -- EOF + if (n == 0) { + return (long) (buf - org_buf); + } + + + // -- next packet found? (sync detected) + sync = (sync << 8) | c; + if ( (sync & 0x00FFFFFF) == 0x000001 ) { + pes_rawBufferedPushByte (*(--buf)); // push back sync bytes + pes_rawBufferedPushByte (*(--buf)); + pes_rawBufferedPushByte (*(--buf)); + return (long) (buf - org_buf); + } + } +#endif + +// fprintf(stderr, "RT %s: unsynced, ret = -1! (this never happens) ppes[3] = 0x%02x\n", __FUNCTION__, ppes[3]); + return -1; // buffer overflow +} diff --git a/src/driver/radiotext.h b/src/driver/radiotext.h new file mode 100644 index 000000000..627ed3beb --- /dev/null +++ b/src/driver/radiotext.h @@ -0,0 +1,298 @@ +/* + $Id: radiotext.h,v 1.4 2009/10/31 10:11:02 seife Exp $ + + Neutrino-GUI - DBoxII-Project + + License: GPL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + + ripped from: +*/ + +/* + * radioaudio.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * This is a "plugin" for the Video Disk Recorder (VDR). + * + * Written by: Lars Tegeler + * + * Project's homepage: www.math.uni-paderborn.de/~tegeler/vdr + * + * Latest version available at: URL + * + * See the file COPYING for license information. + * + * Description: + * + * This Plugin display an background image while the vdr is switcht to radio channels. + * +*/ + +#ifndef __RADIO_AUDIO_H +#define __RADIO_AUDIO_H + +#include +#include + +#if 0 +#include +#include +#include +#include +#include +#include +#endif + +#include + +//#define ENABLE_RASS + +typedef unsigned char uchar; +typedef unsigned int uint; + +extern const char *ConfigDir; +extern const char *DataDir; +extern char *ReplayFile; + + +#if 0 +// RDS-Receiver for seperate Data-Pids +class cRDSReceiver : public cReceiver { +private: + int pid; + bool rt_start; + bool rt_bstuff; +protected: + virtual void Receive(uchar *Data, int Length); +public: + cRDSReceiver(int Pid); + virtual ~cRDSReceiver(void); +}; +#endif + +#define RT_MEL 65 +#define tr(a) a + +class CRadioText { + +public: + typedef struct { + CRadioText *rt_object; + int fd; + } s_rt_thread; + +private: + bool enabled; + bool have_radiotext; + char *imagepath; + bool imageShown; + int imagedelay; + void send_pes_packet(unsigned char *data, int len, int timestamp); + void ShowImage (const char *file); + int first_packets; + + //Radiotext +// cDevice *rdsdevice; + void RadiotextCheckPES(const uchar *Data, int Length); + void RadioStatusMsg(void); + void AudioRecorderService(void); + void RassDecode(uchar *Data, int Length); + bool DividePes(char *data, int length, int *substart, int *subend); + + uint pid; + pthread_t threadRT; + int dmxfd; + +public: + CRadioText(void); + ~CRadioText(void); + int PES_Receive(char *data, int len); + int RassImage(int QArchiv, int QKey, bool DirUp); + void EnableRadioTextProcessing(const char *Titel, bool replay = false); + void DisableRadioTextProcessing(); + void RadiotextDecode(uchar *Data, int Length); + void RDS_PsPtynDecode(bool PTYN, uchar *Data, int Length); + void ShowText(void); + char* ptynr2string(int nr); + char *rds_entitychar(char *text); + + void setPid(uint inPid); + uint getPid(){ return pid; } + int run(void); + int getDMXfd(void) { return dmxfd; } +// s_rt_thread& getThreadParams(void) { return rt; } + pthread_t getThread(void) { return threadRT; } + void radiotext_stop(void); + bool haveRadiotext(void) {return have_radiotext; } + + cDemux *audioDemux; + s_rt_thread rt; + + //Setup-Params + int S_RtFunc; + int S_RtOsd; + int S_RtOsdTitle; + int S_RtOsdTags; + int S_RtOsdPos; + int S_RtOsdRows; + int S_RtOsdLoop; + int S_RtOsdTO; + int S_RtSkinColor; + int S_RtBgCol; + int S_RtBgTra; + int S_RtFgCol; + int S_RtDispl; + int S_RassText; + int S_RtMsgItems; +// uint32_t rt_color[9]; + int S_Verbose; + + // Radiotext + int RTP_ItemToggle, RTP_TToggle; + bool RT_MsgShow, RT_PlusShow; + bool RT_Replay, RT_ReOpen; + char RT_Text[5][RT_MEL]; + char RTP_Artist[RT_MEL], RTP_Title[RT_MEL]; + int RT_Info, RT_Index, RT_PTY; + time_t RTP_Starttime; + bool RT_OsdTO, RTplus_Osd; + int RT_OsdTOTemp; + char RDS_PTYN[9]; + char *RT_Titel, *RTp_Titel; + +#if ENABLE_RASS + // Rass ... + int Rass_Show; // -1=No, 0=Yes, 1=display + int Rass_Archiv; // -1=Off, 0=Index, 1000-9990=Slidenr. + bool Rass_Flags[11][4]; // Slides+Gallery existent +#endif + +}; + +#if 0 +class cRadioTextOsd : public cOsdObject { +private: + cOsd *osd; + cOsd *qosd; + cOsd *qiosd; + const cFont *ftitel; + const cFont *ftext; + int fheight; + int bheight; + eKeys LastKey; + cTimeMs osdtimer; + void rtp_print(void); + bool rtclosed; + bool rassclosed; + static cBitmap rds, arec, rass; + static cBitmap index, marker, page1, pages2, pages3, pages4, pageE; + static cBitmap no0, no1, no2, no3, no4, no5, no6, no7, no8, no9, bok; +public: + cRadioTextOsd(); + ~cRadioTextOsd(); + virtual void Hide(void); + virtual void Show(void); + virtual void ShowText(void); + virtual void RTOsdClose(void); + int RassImage(int QArchiv, int QKey, bool DirUp); + virtual void RassOsd(void); + virtual void RassOsdTip(void); + virtual void RassOsdClose(void); + virtual void RassImgSave(char *size, int pos); + virtual eOSState ProcessKey(eKeys Key); + virtual bool IsInteractive(void) { return false; } +}; + +class cRTplusOsd : public cOsdMenu { +private: + int bcount; + int helpmode; + const char *listtyp[7]; + char *btext[7]; + int rtptyp(char *btext); + void rtp_fileprint(void); +public: + cRTplusOsd(void); + virtual ~cRTplusOsd(); + virtual void Load(void); + virtual void Update(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +class cRTplusList : public cOsdMenu { +private: + int typ; + bool refresh; +public: + cRTplusList(int Typ = 0); + ~cRTplusList(); + virtual void Load(void); + virtual void Update(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif + +// Radiotext-Memory +#define MAX_RTPC 50 +struct rtp_classes { + time_t start; + char temptext[RT_MEL]; + char *radiotext[2*MAX_RTPC]; + int rt_Index; + // Item + bool item_New; + char *item_Title[MAX_RTPC]; // 1 + char *item_Artist[MAX_RTPC]; // 4 + time_t item_Start[MAX_RTPC]; + int item_Index; + // Info + char *info_News; // 12 + char *info_NewsLocal; // 13 + char *info_Stock[MAX_RTPC]; // 14 + int info_StockIndex; + char *info_Sport[MAX_RTPC]; // 15 + int info_SportIndex; + char *info_Lottery[MAX_RTPC]; // 16 + int info_LotteryIndex; + char *info_DateTime; // 24 + char *info_Weather[MAX_RTPC]; // 25 + int info_WeatherIndex; + char *info_Traffic; // 26 + char *info_Alarm; // 27 + char *info_Advert; // 28 + char *info_Url; // 29 + char *info_Other[MAX_RTPC]; // 30 + int info_OtherIndex; + // Programme + char *prog_Station; // 31 + char *prog_Now; // 32 + char *prog_Next; // 33 + char *prog_Part; // 34 + char *prog_Host; // 35 + char *prog_EditStaff; // 36 + char *prog_Homepage; // 38 + // Interactivity + char *phone_Hotline; // 39 + char *phone_Studio; // 40 + char *email_Hotline; // 44 + char *email_Studio; // 45 +// to be continue... +}; + +#endif //__RADIO_AUDIO_H diff --git a/src/driver/radiotools.cpp b/src/driver/radiotools.cpp new file mode 100644 index 000000000..65eafb9ac --- /dev/null +++ b/src/driver/radiotools.cpp @@ -0,0 +1,101 @@ +/* + * radiotools.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * This is a "plugin" for the Video Disk Recorder (VDR). + * + * Written by: Lars Tegeler + * + * Project's homepage: www.math.uni-paderborn.de/~tegeler/vdr + * + * Latest version available at: URL + * + * See the file COPYING for license information. + * + * Description: + * + * This Plugin display an background image while the vdr is switcht to radio channels. + * + * $Id: radiotools.cpp,v 1.1 2009/08/07 07:22:31 rhabarber1848 Exp $ + +*/ + +#include "radiotools.h" +#include +#include +#include + +/* for timetest */ +//#include +//#include +unsigned short crc16_ccitt(unsigned char *daten, int len, bool skipfirst) +{ +/* timetest */ +//struct timeval t; +//unsigned long long tstart = 0; +//if (gettimeofday(&t, NULL) == 0) +// tstart = t.tv_sec*1000000 + t.tv_usec; + + // CRC16-CCITT: x^16 + x^12 + x^5 + 1 + // with start 0xffff and result invers + register unsigned short crc = 0xffff; + + if (skipfirst) *daten++; + while (len--) { + crc = (crc >> 8) | (crc << 8); + crc ^= *daten++; + crc ^= (crc & 0xff) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xff) << 4) << 1; + } + +/* timetest */ +//if (tstart > 0 && gettimeofday(&t, NULL) == 0) +// printf("vdr-radio: crc-calctime = %d usec\n", (int)((t.tv_sec*1000000 + t.tv_usec) - tstart)); + + return ~(crc); +} + +char *rtrim(char *text) +{ + char *s = text + strlen(text) - 1; + while (s >= text && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) + *s-- = 0; + + return text; +} + +// --- cTimeMs --------------------------------------------------------------- + +cTimeMs::cTimeMs(void) +{ + Set(); +} + +#if 0 +uint64_t cTimeMs::Now(void) +{ + struct timeval t; + if (gettimeofday(&t, NULL) == 0) + return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000; + return 0; +} + +void cTimeMs::Set(int Ms) +{ + begin = Now() + Ms; +} + +bool cTimeMs::TimedOut(void) +{ + return Now() >= begin; +} + +uint64_t cTimeMs::Elapsed(void) +{ + return Now() - begin; +} + +#endif +//--------------- End ----------------------------------------------------------------- diff --git a/src/driver/radiotools.h b/src/driver/radiotools.h new file mode 100644 index 000000000..24bf05c30 --- /dev/null +++ b/src/driver/radiotools.h @@ -0,0 +1,46 @@ +/* + * radiotools.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * This is a "plugin" for the Video Disk Recorder (VDR). + * + * Written by: Lars Tegeler + * + * Project's homepage: www.math.uni-paderborn.de/~tegeler/vdr + * + * Latest version available at: URL + * + * See the file COPYING for license information. + * + * Description: + * + * This Plugin display an background image while the vdr is switcht to radio channels. + * + * $Id: radiotools.h,v 1.1 2009/08/07 07:22:31 rhabarber1848 Exp $ + +*/ + +#ifndef __RADIO_TOOLS_H +#define __RADIO_TOOLS_H + + +unsigned short crc16_ccitt(unsigned char *daten, int len, bool skipfirst); + +char *rtrim(char *text); + +typedef long long unsigned int uint64_t; + +class cTimeMs { + private: + uint64_t begin; + + public: + cTimeMs(void); + static uint64_t Now(void); + void Set(int Ms = 0); + bool TimedOut(void); + uint64_t Elapsed(void); +}; + +#endif //__RADIO_TOOLS_H diff --git a/src/driver/ringbuffer.c b/src/driver/ringbuffer.c new file mode 100644 index 000000000..af8d15786 --- /dev/null +++ b/src/driver/ringbuffer.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2000 Paul Davis + * Copyright (C) 2003 Rohan Drape + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. + * This is safe for the case of one read thread and one write thread. + */ + +#include +#include +#include +#include "ringbuffer.h" + + +/* Create a new ringbuffer to hold at least `sz' bytes of data. The + * actual buffer size is rounded up to the next power of two. + */ +ringbuffer_t * ringbuffer_create (int sz) +{ + int power_of_two; + ringbuffer_t *rb; + + rb = malloc (sizeof (ringbuffer_t)); + + for(power_of_two = 1; 1 << power_of_two < sz; power_of_two++) + ; + + rb->size = 1 << power_of_two; + rb->size_mask = rb->size; + rb->size_mask -= 1; + rb->write_ptr = 0; + rb->read_ptr = 0; + rb->buf = malloc (rb->size); + rb->mlocked = 0; + rb->helpbufsize = 1; + rb->helpbuf = malloc (rb->helpbufsize); + + if( rb->buf ) + return rb; + + free( rb ); + return NULL; +} + + +/* Free all data associated with the ringbuffer `rb'. + */ +void ringbuffer_free (ringbuffer_t * rb) +{ + if (rb->mlocked) + munlock (rb->buf, rb->size); + + free (rb->buf); + rb->buf=0; + free (rb->helpbuf); + rb->helpbuf=0; + free (rb); + rb=0; +} + +/* Lock the data block of `rb' using the system call 'mlock'. */ +int ringbuffer_mlock (ringbuffer_t * rb) +{ + if (mlock (rb->buf, rb->size)) + return -1; + + rb->mlocked = 1; + return 0; +} + +/* Reset the read and write pointers to zero. This is not thread + * safe. + */ +void ringbuffer_reset (ringbuffer_t * rb) +{ + rb->read_ptr = 0; + rb->write_ptr = 0; +} + +/* Return the number of bytes available for reading. This is the + * number of bytes in front of the read pointer and behind the write + * pointer. + */ +size_t ringbuffer_read_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return w - r; + else + return (w - r + rb->size) & rb->size_mask; +} + +/* Return the number of bytes available for writing. This is the + * number of bytes in front of the write pointer and behind the read + * pointer. + */ +size_t ringbuffer_write_space (ringbuffer_t * rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + return (r - w) - 1; + else + return rb->size - 1; +} + +/* The copying data reader. Copy at most `cnt' bytes from `rb' to + * `dest'. Returns the actual number of bytes copied. + */ +size_t ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + if ((free_cnt = ringbuffer_read_space (rb)) == 0) + return 0; + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) + { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[rb->read_ptr]), n1); + rb->read_ptr += n1; + rb->read_ptr &= rb->size_mask; + + if (n2) + { + memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); + rb->read_ptr += n2; + rb->read_ptr &= rb->size_mask; + } + + return to_read; +} + +/* The copying data writer. Copy at most `cnt' bytes to `rb' from + * `src'. Returns the actual number of bytes copied. + */ +size_t ringbuffer_write (ringbuffer_t * rb, char *src, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + if ((free_cnt = ringbuffer_write_space (rb)) == 0) + return 0; + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->write_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_write; + n2 = 0; + } + + memcpy (&(rb->buf[rb->write_ptr]), src, n1); + rb->write_ptr += n1; + rb->write_ptr &= rb->size_mask; + + if (n2) + { + memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); + rb->write_ptr += n2; + rb->write_ptr &= rb->size_mask; + } + + return to_write; +} + +/* Advance the read pointer `cnt' places. + */ +void ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->read_ptr += cnt; + rb->read_ptr &= rb->size_mask; +} + +/* Advance the write pointer `cnt' places. + */ +void ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) +{ + rb->write_ptr += cnt; + rb->write_ptr &= rb->size_mask; +} + +/* The non-copying data reader. `vec' is an array of two places. Set + * the values at `vec' to hold the current readable data at `rb'. If + * the readable data is in one segment the second segment has zero + * length. + */ +void ringbuffer_get_read_vector (ringbuffer_t * rb, ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = w - r; + else + free_cnt = (w - r + rb->size) & rb->size_mask; + + cnt2 = r + free_cnt; + + if (cnt2 > rb->size) + { + /* Two part vector: the rest of the buffer after the current write + * ptr, plus some from the start of the buffer. + */ + vec[0].buf = &(rb->buf[r]); + vec[0].len = rb->size - r; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + /* Single part vector: just the rest of the buffer */ + vec[0].buf = &(rb->buf[r]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +/* The non-copying data writer. `vec' is an array of two places. Set + * the values at `vec' to hold the current writeable data at `rb'. If + * the writeable data is in one segment the second segment has zero + * length. + */ +void ringbuffer_get_write_vector (ringbuffer_t * rb, ringbuffer_data_t * vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + free_cnt = (r - w) - 1; + else + free_cnt = rb->size - 1; + + cnt2 = w + free_cnt; + + if (cnt2 > rb->size) + { + /* Two part vector: the rest of the buffer after the current write + * ptr, plus some from the start of the buffer. + */ + vec[0].buf = &(rb->buf[w]); + vec[0].len = rb->size - w; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + vec[0].buf = &(rb->buf[w]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +/* Get read pointer at most `cnt' bytes from `rb' to + `dest'. Returns the actual readable number of bytes . */ +size_t ringbuffer_get_readpointer (ringbuffer_t * rb, char **dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + size_t tmp_read_ptr = rb->read_ptr; + + if ((free_cnt = ringbuffer_read_space (rb)) == 0) + return 0; + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) + { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_read; + n2 = 0; + } + if (n2) + { + if (to_read > rb->helpbufsize) + { + rb->helpbufsize = to_read; + rb->helpbuf = realloc (rb->helpbuf, rb->helpbufsize); + } + memcpy (rb->helpbuf, &(rb->buf[rb->read_ptr]), n1); + tmp_read_ptr += n1; + tmp_read_ptr &= rb->size_mask; + memcpy (rb->helpbuf + n1, &(rb->buf[tmp_read_ptr]), n2); + *dest = rb->helpbuf; + } + else + *dest = &(rb->buf[rb->read_ptr]); + + return to_read; +} + + +/* Get write pointer at most `cnt' bytes to `rb' from + `src'. Returns the actual number of bytes can insert. */ +size_t ringbuffer_get_writepointer (ringbuffer_t * rb, char **src, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + + if ((free_cnt = ringbuffer_write_space (rb)) == 0) + return 0; + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) + return 0; + else + *src = &(rb->buf[rb->write_ptr]); + + return to_write; +} diff --git a/src/driver/ringbuffer.h b/src/driver/ringbuffer.h new file mode 100644 index 000000000..b992e96c5 --- /dev/null +++ b/src/driver/ringbuffer.h @@ -0,0 +1,45 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H + +#include + +typedef struct +{ + char *buf; + size_t len; +} ringbuffer_data_t; + +typedef struct +{ + char *buf; + volatile size_t write_ptr; + volatile size_t read_ptr; + size_t size; + size_t size_mask; + int mlocked; + char *helpbuf; + size_t helpbufsize; +} ringbuffer_t; + +ringbuffer_t *ringbuffer_create(int sz); +void ringbuffer_free(ringbuffer_t *rb); + +int ringbuffer_mlock(ringbuffer_t *rb); +void ringbuffer_reset(ringbuffer_t *rb); + +void ringbuffer_write_advance(ringbuffer_t *rb, size_t cnt); +void ringbuffer_read_advance(ringbuffer_t *rb, size_t cnt); + +size_t ringbuffer_write_space(ringbuffer_t *rb); +size_t ringbuffer_read_space(ringbuffer_t *rb); + +size_t ringbuffer_read(ringbuffer_t *rb, char *dest, size_t cnt); +size_t ringbuffer_write(ringbuffer_t *rb, char *src, size_t cnt); + +void ringbuffer_get_read_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); +void ringbuffer_get_write_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); + +size_t ringbuffer_get_readpointer(ringbuffer_t * rb, char **dest, size_t cnt); +size_t ringbuffer_get_writepointer(ringbuffer_t * rb, char **src, size_t cnt); + +#endif diff --git a/src/global.h b/src/global.h index 8dc9b30c1..4bf5403c7 100644 --- a/src/global.h +++ b/src/global.h @@ -56,6 +56,7 @@ #include "gui/infoviewer.h" #include "gui/eventlist.h" #include "gui/videosettings.h" +#include "driver/radiotext.h" #ifndef NEUTRINO_CPP #define NEUTRINO_CPP extern @@ -97,7 +98,7 @@ NEUTRINO_CPP CLocaleManager *g_Locale; #if HAVE_COOL_HARDWARE NEUTRINO_CPP RFmod *g_RFmod; #endif - NEUTRINO_CPP CVideoSettings *g_videoSettings; +NEUTRINO_CPP CRadioText *g_Radiotext; #endif /* __neutrino_global_h__ */ diff --git a/src/gui/bedit/Makefile.am b/src/gui/bedit/Makefile.am index 8d78d480e..b12653cba 100644 --- a/src/gui/bedit/Makefile.am +++ b/src/gui/bedit/Makefile.am @@ -6,6 +6,7 @@ INCLUDES = \ -I$(top_srcdir)/src/zapit/include \ -I$(top_srcdir)/lib/libeventserver \ -I$(top_srcdir)/lib/libconfigfile \ + -I$(top_srcdir)/lib/libcoolstream \ -I$(top_srcdir)/lib/xmltree \ @FREETYPE_CFLAGS@ \ -I$(top_srcdir)/lib diff --git a/src/gui/channellist.cpp b/src/gui/channellist.cpp index 445ca5d14..4ad1faa24 100644 --- a/src/gui/channellist.cpp +++ b/src/gui/channellist.cpp @@ -1126,6 +1126,13 @@ void CChannelList::zapTo(int pos, bool /* forceStoreToLastChannels */) CZapitChannel* chan = chanlist[pos]; printf("**************************** CChannelList::zapTo me %p %s tuned %d new %d %s -> %llx\n", this, name.c_str(), tuned, pos, chan->name.c_str(), chan->channel_id); if ( pos!=(int)tuned ) { + + if ((g_settings.radiotext_enable) && ((CNeutrinoApp::getInstance()->getMode()) == NeutrinoMessages::mode_radio) && (g_Radiotext)) + { + // stop radiotext PES decoding before zapping + g_Radiotext->radiotext_stop(); + } + tuned = pos; g_RemoteControl->zapTo_ChannelID(chan->channel_id, chan->name, !chan->bAlwaysLocked); // UTF-8 // TODO check is it possible bouquetList is NULL ? diff --git a/src/gui/infoviewer.cpp b/src/gui/infoviewer.cpp index 49facc37a..dde7006de 100644 --- a/src/gui/infoviewer.cpp +++ b/src/gui/infoviewer.cpp @@ -496,6 +496,10 @@ void CInfoViewer::showMovieTitle(const int playState, const std::string Channel, if (!gotTime) gotTime = timeset; + if (g_settings.radiotext_enable && g_Radiotext) { + g_Radiotext->RT_MsgShow = true; + } + int fadeValue; if (fadeIn) { fadeValue = 100; @@ -735,6 +739,14 @@ void CInfoViewer::showTitle (const int ChanNum, const std::string & Channel, con g_Sectionsd->setServiceChanged (channel_id & 0xFFFFFFFFFFFFULL, true); } + // Radiotext + if (CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_radio) + { + if ((g_settings.radiotext_enable) && (!recordModeActive) && (!calledFromNumZap)) + showRadiotext(); + else + showIcon_RadioText(false); + } if (!calledFromNumZap) { loop(fadeValue, show_dot , fadeIn); @@ -811,6 +823,8 @@ void CInfoViewer::loop(int fadeValue, bool show_dot ,bool fadeIn) paintTime (show_dot, false); showRecordIcon (show_dot); show_dot = !show_dot; + if ((g_settings.radiotext_enable) && (CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_radio)) + showRadiotext(); showIcon_16_9(); showIcon_Resolution(); @@ -821,7 +835,10 @@ void CInfoViewer::loop(int fadeValue, bool show_dot ,bool fadeIn) } else if (!fileplay && !CMoviePlayerGui::getInstance().timeshift) { CNeutrinoApp *neutrino = CNeutrinoApp::getInstance (); if ((msg == (neutrino_msg_t) g_settings.key_quickzap_up) || (msg == (neutrino_msg_t) g_settings.key_quickzap_down) || (msg == CRCInput::RC_0) || (msg == NeutrinoMessages::SHOW_INFOBAR)) { - hideIt = false; + if ((g_settings.radiotext_enable) && (CNeutrinoApp::getInstance()->getMode() == NeutrinoMessages::mode_radio)) + hideIt = true; + else + hideIt = false; //hideIt = (g_settings.timing[SNeutrinoSettings::TIMING_INFOBAR] == 0) ? true : false; g_RCInput->postMsg (msg, data); res = messages_return::cancel_info; @@ -973,6 +990,25 @@ void CInfoViewer::showSubchan () } } +void CInfoViewer::showIcon_RadioText(bool /*rt_available*/) const +// painting the icon for radiotext mode +{ +#if 0 + if (showButtonBar) + { + int mode = CNeutrinoApp::getInstance()->getMode(); + std::string rt_icon = "radiotextoff.raw"; + if ((!virtual_zap_mode) && (!recordModeActive) && (mode == NeutrinoMessages::mode_radio)) + { + if (g_settings.radiotext_enable){ + rt_icon = rt_available ? "radiotextget.raw" : "radiotextwait.raw"; + } + } + frameBuffer->paintIcon(rt_icon, BoxEndX - (ICON_LARGE_WIDTH + 2 + ICON_LARGE_WIDTH + 2 + ICON_SMALL_WIDTH + 2 + ICON_SMALL_WIDTH + 6),BoxEndY + (InfoHeightY_Info - ICON_HEIGHT) / 2); + } +#endif +} + void CInfoViewer::showIcon_16_9 () { if ((aspectRatio == 0) || ( g_RemoteControl->current_PIDs.PIDs.vpid == 0 ) || (aspectRatio != videoDecoder->getAspectRatio())) { @@ -1111,6 +1147,145 @@ void CInfoViewer::showMotorMoving (int duration) ShowHintUTF (LOCALE_MESSAGEBOX_INFO, text, g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth (text, true) + 10, duration); // UTF-8 } +void CInfoViewer::killRadiotext() +{ + frameBuffer->paintBackgroundBox(rt_x, rt_y, rt_w, rt_h); +} + +void CInfoViewer::showRadiotext() +{ + char stext[3][100]; + int yoff = 8, ii = 0; + bool RTisIsUTF = false; + + if (g_Radiotext == NULL) return; + showIcon_RadioText(g_Radiotext->haveRadiotext()); + + if (g_Radiotext->S_RtOsd) { + // dimensions of radiotext window + rt_dx = BoxEndX - BoxStartX; + rt_dy = 25; + rt_x = BoxStartX; + rt_y = g_settings.screen_StartY + 10; + rt_h = rt_y + 7 + rt_dy*(g_Radiotext->S_RtOsdRows+1)+SHADOW_OFFSET; + rt_w = rt_x+rt_dx+SHADOW_OFFSET; + + int lines = 0; + for (int i = 0; i < g_Radiotext->S_RtOsdRows; i++) { + if (g_Radiotext->RT_Text[i][0] != '\0') lines++; + } + if (lines == 0) + frameBuffer->paintBackgroundBox(rt_x, rt_y, rt_w, rt_h); + + if (g_Radiotext->RT_MsgShow) { + + if (g_Radiotext->S_RtOsdTitle == 1) { + + // Title + // sprintf(stext[0], g_Radiotext->RT_PTY == 0 ? "%s - %s %s%s" : "%s - %s (%s)%s", + // g_Radiotext->RT_Titel, tr("Radiotext"), g_Radiotext->RT_PTY == 0 ? g_Radiotext->RDS_PTYN : g_Radiotext->ptynr2string(g_Radiotext->RT_PTY), g_Radiotext->RT_MsgShow ? ":" : tr(" [waiting ...]")); + if ((lines) || (g_Radiotext->RT_PTY !=0)) { + sprintf(stext[0], g_Radiotext->RT_PTY == 0 ? "%s %s%s" : "%s (%s)%s", tr("Radiotext"), g_Radiotext->RT_PTY == 0 ? g_Radiotext->RDS_PTYN : g_Radiotext->ptynr2string(g_Radiotext->RT_PTY), ":"); + + // shadow + frameBuffer->paintBoxRel(rt_x+SHADOW_OFFSET, rt_y+SHADOW_OFFSET, rt_dx, rt_dy, COL_INFOBAR_SHADOW_PLUS_0, RADIUS_LARGE, CORNER_TOP); + frameBuffer->paintBoxRel(rt_x, rt_y, rt_dx, rt_dy, COL_INFOBAR_PLUS_0, RADIUS_LARGE, CORNER_TOP); + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(rt_x+10, rt_y+ 30, rt_dx-20, stext[0], COL_INFOBAR, 0, RTisIsUTF); // UTF-8 + } + yoff = 17; + ii = 1; +#if 0 + // RDS- or Rass-Symbol, ARec-Symbol or Bitrate + int inloff = (ftitel->Height() + 9 - 20) / 2; + if (Rass_Flags[0][0]) { + osd->DrawBitmap(Setup.OSDWidth-51, inloff, rass, bcolor, fcolor); + if (ARec_Record) + osd->DrawBitmap(Setup.OSDWidth-107, inloff, arec, bcolor, 0xFFFC1414); // FG=Red + else + inloff = (ftitel->Height() + 9 - ftext->Height()) / 2; + osd->DrawText(4, inloff, RadioAudio->bitrate, fcolor, clrTransparent, ftext, Setup.OSDWidth-59, ftext->Height(), taRight); + } + else { + osd->DrawBitmap(Setup.OSDWidth-84, inloff, rds, bcolor, fcolor); + if (ARec_Record) + osd->DrawBitmap(Setup.OSDWidth-140, inloff, arec, bcolor, 0xFFFC1414); // FG=Red + else + inloff = (ftitel->Height() + 9 - ftext->Height()) / 2; + osd->DrawText(4, inloff, RadioAudio->bitrate, fcolor, clrTransparent, ftext, Setup.OSDWidth-92, ftext->Height(), taRight); + } +#endif + } + // Body + if (lines) { + frameBuffer->paintBoxRel(rt_x+SHADOW_OFFSET, rt_y+rt_dy+SHADOW_OFFSET, rt_dx, 7+rt_dy* g_Radiotext->S_RtOsdRows, COL_INFOBAR_SHADOW_PLUS_0, RADIUS_LARGE, CORNER_BOTTOM); + frameBuffer->paintBoxRel(rt_x, rt_y+rt_dy, rt_dx, 7+rt_dy* g_Radiotext->S_RtOsdRows, COL_INFOBAR_PLUS_0, RADIUS_LARGE, CORNER_BOTTOM); + + // RT-Text roundloop + int ind = (g_Radiotext->RT_Index == 0) ? g_Radiotext->S_RtOsdRows - 1 : g_Radiotext->RT_Index - 1; + int rts_x = rt_x+10; + int rts_y = rt_y+ 30; + int rts_dx = rt_dx-20; + if (g_Radiotext->S_RtOsdLoop == 1) { // latest bottom + for (int i = ind+1; i < g_Radiotext->S_RtOsdRows; i++) + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(rts_x, rts_y + (ii++)*rt_dy, rts_dx, g_Radiotext->RT_Text[i], COL_INFOBAR, 0, RTisIsUTF); // UTF-8 + for (int i = 0; i <= ind; i++) + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(rts_x, rts_y + (ii++)*rt_dy, rts_dx, g_Radiotext->RT_Text[i], COL_INFOBAR, 0, RTisIsUTF); // UTF-8 + } + else { // latest top + for (int i = ind; i >= 0; i--) + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(rts_x, rts_y + (ii++)*rt_dy, rts_dx, g_Radiotext->RT_Text[i], COL_INFOBAR, 0, RTisIsUTF); // UTF-8 + for (int i = g_Radiotext->S_RtOsdRows-1; i > ind; i--) + g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(rts_x, rts_y + (ii++)*rt_dy, rts_dx, g_Radiotext->RT_Text[i], COL_INFOBAR, 0, RTisIsUTF); // UTF-8 + } + } +#if 0 + // + RT-Plus or PS-Text = 2 rows + if ((S_RtOsdTags == 1 && RT_PlusShow) || S_RtOsdTags >= 2) { + if (!RDS_PSShow || !strstr(RTP_Title, "---") || !strstr(RTP_Artist, "---")) { + sprintf(stext[1], "> %s %s", tr("Title :"), RTP_Title); + sprintf(stext[2], "> %s %s", tr("Artist :"), RTP_Artist); + osd->DrawText(4, 6+yoff+fheight*(ii++), stext[1], fcolor, clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height()); + osd->DrawText(4, 3+yoff+fheight*(ii++), stext[2], fcolor, clrTransparent, ftext, Setup.OSDWidth-4, ftext->Height()); + } + else { + char *temp = ""; + int ind = (RDS_PSIndex == 0) ? 11 : RDS_PSIndex - 1; + for (int i = ind+1; i < 12; i++) + asprintf(&temp, "%s%s ", temp, RDS_PSText[i]); + for (int i = 0; i <= ind; i++) + asprintf(&temp, "%s%s ", temp, RDS_PSText[i]); + snprintf(stext[1], 6*9, "%s", temp); + snprintf(stext[2], 6*9, "%s", temp+(6*9)); + free(temp); + osd->DrawText(6, 6+yoff+fheight*ii, "[", fcolor, clrTransparent, ftext, 12, ftext->Height()); + osd->DrawText(Setup.OSDWidth-12, 6+yoff+fheight*ii, "]", fcolor, clrTransparent, ftext, Setup.OSDWidth-6, ftext->Height()); + osd->DrawText(16, 6+yoff+fheight*(ii++), stext[1], fcolor, clrTransparent, ftext, Setup.OSDWidth-16, ftext->Height(), taCenter); + osd->DrawText(6, 3+yoff+fheight*ii, "[", fcolor, clrTransparent, ftext, 12, ftext->Height()); + osd->DrawText(Setup.OSDWidth-12, 3+yoff+fheight*ii, "]", fcolor, clrTransparent, ftext, Setup.OSDWidth-6, ftext->Height()); + osd->DrawText(16, 3+yoff+fheight*(ii++), stext[2], fcolor, clrTransparent, ftext, Setup.OSDWidth-16, ftext->Height(), taCenter); + } + } +#endif + } +#if 0 +// framebuffer can only display raw images + // show mpeg-still + char *image; + if (g_Radiotext->Rass_Archiv >= 0) + asprintf(&image, "%s/Rass_%d.mpg", DataDir, g_Radiotext->Rass_Archiv); + else + asprintf(&image, "%s/Rass_show.mpg", DataDir); + frameBuffer->useBackground(frameBuffer->loadBackground(image));// set useBackground true or false + frameBuffer->paintBackground(); +// RadioAudio->SetBackgroundImage(image); + free(image); +#endif + } + g_Radiotext->RT_MsgShow = false; + +} + + int CInfoViewer::handleMsg (const neutrino_msg_t msg, neutrino_msg_data_t data) { if ((msg == NeutrinoMessages::EVT_CURRENTNEXT_EPG) || (msg == NeutrinoMessages::EVT_NEXTPROGRAM)) { @@ -1164,6 +1339,8 @@ int CInfoViewer::handleMsg (const neutrino_msg_t msg, neutrino_msg_data_t data) if ((*(t_channel_id *) data) == channel_id) { if (is_visible && showButtonBar) showButton_Audio (); + if (g_settings.radiotext_enable && g_Radiotext && ((CNeutrinoApp::getInstance()->getMode()) == NeutrinoMessages::mode_radio)) + g_Radiotext->setPid(g_RemoteControl->current_PIDs.APIDs[g_RemoteControl->current_PIDs.PIDs.selected_apid].pid); } return messages_return::handled; } else if (msg == NeutrinoMessages::EVT_ZAP_GOT_SUBSERVICES) { @@ -1411,6 +1588,7 @@ void CInfoViewer::display_Info(const char *current, const char *next, pb_p = pb_w; timescale->paintProgressBar(BoxEndX - pb_w - SHADOW_OFFSET, ChanNameY - (pb_h + 10) , pb_w, pb_h, pb_p, pb_w, 0, 0, g_settings.progressbar_color ? COL_INFOBAR_SHADOW_PLUS_0 : COL_INFOBAR_PLUS_0, COL_INFOBAR_SHADOW_PLUS_0, "", COL_INFOBAR); +printf("paintProgressBar(%d, %d, %d, %d)\n", BoxEndX - pb_w - SHADOW_OFFSET, ChanNameY - (pb_h + 10) , pb_w, pb_h); } int currTimeW = 0; @@ -1741,7 +1919,12 @@ void CInfoViewer::killTitle() int bottom = BoxEndY + SHADOW_OFFSET + bottom_bar_offset; if (showButtonBar) bottom += InfoHeightY_Info; +printf("killTitle(%d, %d, %d, %d)\n", BoxStartX, BoxStartY, BoxEndX+ SHADOW_OFFSET-BoxStartX, bottom-BoxStartY); frameBuffer->paintBackgroundBox(BoxStartX, BoxStartY, BoxEndX+ SHADOW_OFFSET, bottom); + if (g_settings.radiotext_enable && g_Radiotext) { + g_Radiotext->S_RtOsd = g_Radiotext->haveRadiotext() ? 1 : 0; + killRadiotext(); + } } showButtonBar = false; } diff --git a/src/gui/infoviewer.h b/src/gui/infoviewer.h index 5e5b4b395..33ac3b739 100644 --- a/src/gui/infoviewer.h +++ b/src/gui/infoviewer.h @@ -66,6 +66,14 @@ class CInfoViewer int BoxStartY; int ButtonWidth; + // dimensions of radiotext window + int rt_dx; + int rt_dy; + int rt_x; + int rt_y; + int rt_h; + int rt_w; + std::string ChannelName; int ChanNameX; @@ -125,7 +133,7 @@ class CInfoViewer void showButton_SubServices(); void showIcon_16_9(); - + void showIcon_RadioText(bool rt_available) const; void showIcon_CA_Status(int); void paint_ca_icons(int, char*, int&); void paintCA_bar(int,int); @@ -141,6 +149,8 @@ class CInfoViewer void showLcdPercentOver(); int showChannelLogo(const t_channel_id logo_channel_id, const int channel_number_width); void showSNR(); + void showRadiotext(); + void killRadiotext(); void showInfoFile(); void loop(int fadeValue, bool show_dot ,bool fadeIn); std::string eventname; diff --git a/src/gui/miscsettings_menu.cpp b/src/gui/miscsettings_menu.cpp index 0b67276f4..1de1be8f2 100644 --- a/src/gui/miscsettings_menu.cpp +++ b/src/gui/miscsettings_menu.cpp @@ -227,6 +227,9 @@ void CMiscMenue::showMiscSettingsMenuGeneral(CMenuWidget *ms_general) //standby after boot ms_general->addItem(new CMenuOptionChooser(LOCALE_EXTRA_START_TOSTANDBY, &g_settings.power_standby, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); ms_general->addItem(new CMenuOptionChooser(LOCALE_EXTRA_CACHE_TXT, (int *)&g_settings.cacheTXT, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true)); + CRadiotextNotifier *radiotextNotifier = new CRadiotextNotifier; + ms_general->addItem(new CMenuOptionChooser(LOCALE_MISCSETTINGS_RADIOTEXT, &g_settings.radiotext_enable, OPTIONS_OFF0_ON1_OPTIONS, OPTIONS_OFF0_ON1_OPTION_COUNT, true, radiotextNotifier)); + ms_general->addItem(new CMenuOptionNumberChooser(LOCALE_MISCSETTINGS_ZAPTO_PRE_TIME, &g_settings.zapto_pre_time, true, 0, 10)); //fan speed if (g_info.has_fan) diff --git a/src/gui/widget/progressbar.cpp b/src/gui/widget/progressbar.cpp index 3c00008a2..d8e42b441 100644 --- a/src/gui/widget/progressbar.cpp +++ b/src/gui/widget/progressbar.cpp @@ -82,12 +82,18 @@ void CProgressBar::paintProgressBar ( const int pos_x, upper_labeltext, uppertext_col, iconfile, paintZero); } -void CProgressBar::paintProgressBar2(const int pos_x, const int pos_y, - const int value, const int max_value, - const fb_pixel_t activebar_col, const fb_pixel_t passivebar_col, - const fb_pixel_t frame_col, const fb_pixel_t shadowbar_col, - const char * upper_labeltext, const uint8_t uppertext_col, - const char * iconfile, bool paintZero) +void CProgressBar::paintProgressBar2(const int pos_x, + const int pos_y, + const int value, + const int max_value, + const fb_pixel_t activebar_col, + const fb_pixel_t passivebar_col, + const fb_pixel_t frame_col, + const fb_pixel_t shadowbar_col, + const char * upper_labeltext, + const uint8_t uppertext_col, + const char * iconfile, + bool paintZero) { if (height < 0 || width < 0) { diff --git a/src/gui/widget/progressbar.h b/src/gui/widget/progressbar.h index 30ca624aa..f66cf7e42 100644 --- a/src/gui/widget/progressbar.h +++ b/src/gui/widget/progressbar.h @@ -129,6 +129,8 @@ class CProgressBar const int max_value); void reset() { last_width = -1; } /* force update on next paint */ + + void hide(); }; #endif /* __gui_widget_progressbar_h__ */ diff --git a/src/neutrino.cpp b/src/neutrino.cpp index e7917d5cd..bf1221944 100644 --- a/src/neutrino.cpp +++ b/src/neutrino.cpp @@ -274,6 +274,7 @@ static void initGlobals(void) g_PluginList = NULL; InfoClock = NULL; g_CamHandler = NULL; + g_Radiotext = NULL; } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -468,6 +469,7 @@ int CNeutrinoApp::loadSetup(const char * fname) g_settings.infobar_show_var_hdd = configfile.getBool("infobar_show_var_hdd" , true ); g_settings.show_infomenu = configfile.getInt32("show_infomenu", 0 ); g_settings.infobar_show_res = configfile.getInt32("infobar_show_res", 0 ); + g_settings.radiotext_enable = configfile.getBool("radiotext_enable" , false); //audio g_settings.audio_AnalogMode = configfile.getInt32( "audio_AnalogMode", 0 ); g_settings.audio_DolbyDigital = configfile.getBool("audio_DolbyDigital" , false); @@ -1021,7 +1023,7 @@ void CNeutrinoApp::saveSetup(const char * fname) configfile.setInt32("infobar_show_var_hdd" , g_settings.infobar_show_var_hdd ); configfile.setInt32("show_infomenu" , g_settings.show_infomenu ); configfile.setInt32("infobar_show_res" , g_settings.infobar_show_res ); - + configfile.setBool("radiotext_enable" , g_settings.radiotext_enable); //audio configfile.setInt32( "audio_AnalogMode", g_settings.audio_AnalogMode ); configfile.setBool("audio_DolbyDigital" , g_settings.audio_DolbyDigital ); @@ -3819,6 +3821,11 @@ printf("CNeutrinoApp::setVolume dx %d dy %d\n", dx, dy); void CNeutrinoApp::tvMode( bool rezap ) { if(mode==mode_radio ) { + if (g_settings.radiotext_enable && g_Radiotext) { + delete g_Radiotext; + g_Radiotext = NULL; + } + videoDecoder->StopPicture(); g_RCInput->killTimer(g_InfoViewer->lcdUpdateTimer); g_InfoViewer->lcdUpdateTimer = g_RCInput->addTimer( LCD_UPDATE_TIME_TV_MODE, false ); @@ -4075,6 +4082,11 @@ printf("radioMode: rezap %s\n", rezap ? "yes" : "no"); channelList->zapTo( firstchannel.channelNumber -1 ); } videoDecoder->ShowPicture(DATADIR "/neutrino/icons/radiomode.jpg"); + + if (g_settings.radiotext_enable) { + g_Radiotext = new CRadioText; + } + } void CNeutrinoApp::startNextRecording() diff --git a/src/system/locals.h b/src/system/locals.h index 6b6b15b9c..91c00421d 100644 --- a/src/system/locals.h +++ b/src/system/locals.h @@ -794,6 +794,7 @@ typedef enum LOCALE_MISCSETTINGS_NOAVIAWATCHDOG, LOCALE_MISCSETTINGS_NOENXWATCHDOG, LOCALE_MISCSETTINGS_PMTUPDATE, + LOCALE_MISCSETTINGS_RADIOTEXT, LOCALE_MISCSETTINGS_SHOW_INFOMENU, LOCALE_MISCSETTINGS_SHUTDOWN_COUNT, LOCALE_MISCSETTINGS_SHUTDOWN_COUNT_HINT1, diff --git a/src/system/locals_intern.h b/src/system/locals_intern.h index fceffbf05..3357acb18 100644 --- a/src/system/locals_intern.h +++ b/src/system/locals_intern.h @@ -794,6 +794,7 @@ const char * locale_real_names[] = "miscsettings.noaviawatchdog", "miscsettings.noenxwatchdog", "miscsettings.pmtupdate", + "miscsettings.radiotext", "miscsettings.show_infomenu", "miscsettings.shutdown_count", "miscsettings.shutdown_count_hint1", diff --git a/src/system/setting_helpers.cpp b/src/system/setting_helpers.cpp index 00b97ba4e..fce57b4f5 100644 --- a/src/system/setting_helpers.cpp +++ b/src/system/setting_helpers.cpp @@ -399,6 +399,27 @@ bool CSectionsdConfigNotifier::changeNotify(const neutrino_locale_t, void *) return true; } +bool CRadiotextNotifier::changeNotify(const neutrino_locale_t, void *) +{ + if (g_settings.radiotext_enable) + { + if (g_Radiotext == NULL) + g_Radiotext = new CRadioText; + if (g_Radiotext && ((CNeutrinoApp::getInstance()->getMode()) == NeutrinoMessages::mode_radio)) + g_Radiotext->setPid(g_RemoteControl->current_PIDs.APIDs[g_RemoteControl->current_PIDs.PIDs.selected_apid].pid); + } + else + { + // stop radiotext PES decoding + if (g_Radiotext) + g_Radiotext->radiotext_stop(); + delete g_Radiotext; + g_Radiotext = NULL; + } + + return true; +} + bool CTouchFileNotifier::changeNotify(const neutrino_locale_t, void * data) { if ((*(int *)data) != 0) diff --git a/src/system/setting_helpers.h b/src/system/setting_helpers.h index 141948354..4d46f46a6 100644 --- a/src/system/setting_helpers.h +++ b/src/system/setting_helpers.h @@ -314,4 +314,10 @@ public: bool changeNotify(const neutrino_locale_t, void * data); }; +class CRadiotextNotifier : public CChangeObserver +{ + public: + bool changeNotify(const neutrino_locale_t, void * Data); +}; + #endif diff --git a/src/system/settings.h b/src/system/settings.h index b05c59ed1..32b7896a4 100644 --- a/src/system/settings.h +++ b/src/system/settings.h @@ -102,7 +102,8 @@ struct SNeutrinoSettings int clockrec; int rounded_corners; int ci_standby_reset; - + int radiotext_enable; + //vcr int vcr_AutoSwitch;