summaryrefslogtreecommitdiff
path: root/txtrecv.c
diff options
context:
space:
mode:
authorTobias Grimm <tobias@e-tobi.loc>2008-12-02 21:00:33 +0100
committerTobias Grimm <tobias@e-tobi.loc>2008-12-02 21:00:33 +0100
commitb451fdb5a36c0f749d63d53165cdf4e84a8f476a (patch)
treeaa3b6548ea52ef133028098c8fbaebbe47ed8a5d /txtrecv.c
downloadvdr-plugin-osdteletext-b451fdb5a36c0f749d63d53165cdf4e84a8f476a.tar.gz
vdr-plugin-osdteletext-b451fdb5a36c0f749d63d53165cdf4e84a8f476a.tar.bz2
Initial commit of version 0.5.1v0.5.1release/v0.5.1
Diffstat (limited to 'txtrecv.c')
-rw-r--r--txtrecv.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/txtrecv.c b/txtrecv.c
new file mode 100644
index 0000000..af76a3a
--- /dev/null
+++ b/txtrecv.c
@@ -0,0 +1,884 @@
+/***************************************************************************
+ * Copyright (c) 2003,2004 by Marcel Wiesweg *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include <dirent.h>
+
+#include "txtrecv.h"
+#include "tables.h"
+#include "setup.h"
+
+#include <vdr/channels.h>
+#include <vdr/device.h>
+#include <vdr/config.h>
+
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+const char *RootDir::root = "/vtx";
+
+void RootDir::setRootDir(const char *newRoot) {
+ root=newRoot;
+}
+
+const char *RootDir::getRootDir() {
+ return root;
+}
+
+int Storage::doCleanUp() {
+ DIR *top=opendir(root);
+ int pagesDeleted=0;
+ if (top) {
+ struct dirent *chandir, path;
+ struct stat chandirstat;
+ char fullPath[PATH_MAX];
+ while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
+ if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
+ continue;
+ snprintf(fullPath, PATH_MAX, "%s/%s", root, chandir->d_name);
+ if (stat(fullPath, &chandirstat)==0) {
+ if (S_ISDIR(chandirstat.st_mode)) {
+ pagesDeleted+=cleanSubDir(fullPath);
+ }
+ }
+ }
+ closedir(top);
+ } else {
+ esyslog("Error opening teletext storage directory \"%s\": %s", root, strerror(errno));
+ }
+ return pagesDeleted;
+}
+
+int Storage::cleanSubDir(const char *dir) {
+ static bool reportedError=false; //avoid filling up syslog
+ DIR *d=opendir(dir);
+ bool hadError=false;
+ int bytesDeleted=0, filesize;
+ if (d) {
+ struct dirent *txtfile, path;
+ struct stat txtfilestat;
+ char fullPath[PATH_MAX];
+ while ( (!readdir_r(d, &path, &txtfile) && txtfile != NULL) ) {
+ int len=strlen(txtfile->d_name);
+ //check that the file end with .vtx to avoid accidents and disasters
+ if (strcmp(txtfile->d_name+len-4, ".vtx")==0) {
+ snprintf(fullPath, PATH_MAX, "%s/%s", dir, txtfile->d_name);
+ stat(fullPath, &txtfilestat);
+ filesize=actualFileSize(txtfilestat.st_size);
+ int ret=unlink(fullPath);
+ if (ret==0)
+ bytesDeleted+=filesize;
+ else
+ hadError=ret;
+ }
+ }
+ closedir(d);
+ rmdir(dir);
+ } else {
+ if (!reportedError) {
+ esyslog("OSD-Teletext: Error opening teletext storage subdirectory \"%s\": %s", dir, strerror(errno));
+ reportedError=true;
+ }
+ }
+
+ if (hadError && !reportedError) {
+ esyslog("OSD-Teletext: Error removing teletext storage subdirectory \"%s\": %s", dir, strerror(hadError));
+ reportedError=true;
+ }
+ return bytesDeleted;
+}
+
+Storage *Storage::s_self = 0;
+Storage::StorageSystem Storage::system = Storage::StorageSystemPacked;
+
+Storage::Storage() {
+ s_self=this;
+ byteCount=0;
+ currentDir=0;
+ storageOption=-1;
+ failedFreeSpace=false;
+}
+
+Storage::~Storage() {
+}
+
+void Storage::setSystem(StorageSystem s) {
+ system=s;
+}
+
+Storage *Storage::instance() {
+ if (!s_self) {
+ switch (system) {
+ case StorageSystemLegacy:
+ s_self=new LegacyStorage();
+ break;
+ case StorageSystemPacked:
+ default:
+ s_self=new PackedStorage();
+ break;
+ }
+ }
+ return s_self;
+}
+
+void Storage::setMaxStorage(int maxMB) {
+ storageOption=maxMB;
+}
+
+void Storage::init() {
+ cleanUp();
+ initMaxStorage(storageOption);
+}
+
+void Storage::freeSpace() {
+ //there might be a situation where only the current directory is left and
+ //occupies the whole space. We cannot delete anything. Don't waste time scanning.
+ if (failedFreeSpace)
+ return;
+
+ //printf("freeSpace()\n");
+ time_t min=time(0);
+ char minDir[PATH_MAX];
+ char fullPath[PATH_MAX];
+ int haveDir=0;
+ DIR *top=opendir(getRootDir());
+ if (top) {
+ struct dirent *chandir, path;
+ struct stat chandirstat;
+ while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) {
+ if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0)
+ continue;
+ snprintf(fullPath, PATH_MAX, "%s/%s", getRootDir(), chandir->d_name);
+ if (stat(fullPath, &chandirstat)==0) {
+ if (S_ISDIR(chandirstat.st_mode)) {
+ if (chandirstat.st_ctime < min && strcmp(fullPath, currentDir)) {
+ min=chandirstat.st_ctime;
+ strcpy(minDir, fullPath);
+ haveDir++;
+ }
+ }
+ }
+ }
+ closedir(top);
+
+ //if haveDir, only current directory present, which must not be deleted
+ if (haveDir>=2)
+ byteCount-=cleanSubDir(minDir);
+ else
+ failedFreeSpace=true;
+ }
+}
+
+bool Storage::exists(const char* file) {
+ struct stat s;
+ return (stat(file, &s)==0);
+}
+
+void Storage::getFilename(char *buffer, int bufLength, PageID page) {
+ snprintf(buffer, bufLength, "%s/%s/%03x_%02x.vtx", getRootDir(),
+#if VDRVERSNUM >= 10318
+ *page.channel.ToString(),
+#else
+ page.channel.ToString(),
+#endif
+ page.page, page.subPage);
+}
+
+void Storage::prepareDirectory(tChannelID chan) {
+ free(currentDir);
+ asprintf(&currentDir, "%s/%s", root,
+#if VDRVERSNUM >= 10318
+ *chan.ToString()
+#else
+ chan.ToString()
+#endif
+ );
+ MakeDirs(currentDir, 1);
+ failedFreeSpace=false;
+}
+
+#define TELETEXT_PAGESIZE 972
+
+LegacyStorage::LegacyStorage() {
+ maxBytes=0;
+ fsBlockSize=1;
+ pageBytes=TELETEXT_PAGESIZE;
+}
+
+LegacyStorage::~LegacyStorage() {
+}
+
+/*
+static inline int FilesForMegabytes(double MB, int blocksize) {
+ double pageBytes;
+ if (TELETEXT_PAGESIZE<=blocksize)
+ pageBytes=blocksize;
+ else
+ pageBytes=((TELETEXT_PAGESIZE/blocksize)+1)*blocksize;
+ //reserve 10% for directories
+ return (int)( (1024.0 * 1024.0 * (MB-MB*0.1)) / pageBytes );
+}*/
+
+int LegacyStorage::actualFileSize(int netFileSize) {
+ if (netFileSize<=0)
+ return 0;
+ if (netFileSize<=fsBlockSize)
+ return fsBlockSize;
+ else
+ return ((netFileSize/fsBlockSize)+1)*fsBlockSize;
+}
+
+//max==0 means unlimited, max==-1 means a reasonable default value shall be calculated
+void LegacyStorage::initMaxStorage(int maxMB) {
+
+ struct statfs fs;
+ if (statfs(getRootDir(), &fs)!=0) {
+ esyslog("OSD-Teletext: Error statfs'ing root directory \"%s\": %s, cache size uncontrolled", getRootDir(), strerror(errno));
+ return;
+ }
+ fsBlockSize=fs.f_bsize;
+
+ pageBytes=actualFileSize(TELETEXT_PAGESIZE);
+
+ if (maxMB>=0) {
+ if (maxMB<3) {
+ esyslog("OSD-Teletext: Request to use at most %d MB for caching. This is not enough, using 3 MB", maxMB);
+ maxMB=3;
+ }
+ maxBytes=MEGABYTE(maxMB);
+ } else if (maxMB==-1) {
+ //calculate a default value
+ double blocksPerMeg = 1024.0 * 1024.0 / fs.f_bsize;
+ double capacityMB=fs.f_blocks / blocksPerMeg;
+ double freeMB=(fs.f_bavail / blocksPerMeg);
+ if (capacityMB<=50 || freeMB<50) {
+ //small (<=50MB) filesystems as root dir are assumed to be dedicated for use as a teletext cache
+ //for others, the maximum default size is set to 50 MB
+ maxBytes=MEGABYTE((int)freeMB);
+ //maxPages= FilesForMegabytes(freeMB, fs.f_bsize);
+ if (freeMB<3.0) {
+ esyslog("OSD-Teletext: Less than %.1f MB free on filesystem of root directory \"%s\"!", freeMB, getRootDir());
+ maxBytes=MEGABYTE(3);
+ }
+ } else {
+ //the maximum default size is set to 50 MB
+ maxBytes=MEGABYTE(50);
+ }
+ //printf("Set maxBytes to %ld, %.2f %.2f\n", maxBytes, capacityMB, freeMB);
+ }
+}
+
+void LegacyStorage::cleanUp() {
+ byteCount -= Storage::doCleanUp();
+}
+
+void LegacyStorage::registerFile(PageID page) {
+ //pageBytes is already effective size
+ if ( maxBytes && (byteCount+=pageBytes)>maxBytes )
+ freeSpace();
+}
+
+StorageHandle LegacyStorage::openForReading(PageID page, bool countAsAccess) {
+ //the countAsAccess argument was intended for use in a LRU cache, currently unused
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ StorageHandle ret=(StorageHandle)open(filename, O_RDONLY);
+ return ret;
+}
+
+StorageHandle LegacyStorage::openForWriting(PageID page) {
+ static bool wroteError=false;
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ bool existed=exists(filename);
+ //first try
+ StorageHandle fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd) {
+ if (!existed)
+ registerFile(page);
+ return fd;
+ }
+ //no space on disk? make some space available
+ if (errno == ENOSPC)
+ freeSpace();
+ //second try
+ fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (!fd && !wroteError) {
+ //report error to syslog - once!
+ wroteError=true;
+ esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
+ }
+ //make sure newly created files are counted
+ if (fd && !existed)
+ registerFile(page);
+ return fd;
+}
+
+ssize_t LegacyStorage::write(const void *ptr, size_t size, StorageHandle stream) {
+ ssize_t written;
+ if (!(written=::write((int)stream, ptr, size)) ) {
+ switch (errno) {
+ case ENOSPC:
+ freeSpace();
+ return ::write((int)stream, ptr, size);
+ case EINTR:
+ esyslog("OSD-Teletext: EINTR while writing. Please contact the author and tell him this happened.");
+ break;
+ default:
+ break;
+ }
+ }
+ return written;
+}
+
+
+
+PackedStorage::PackedStorage() {
+}
+#define TOC_SIZE 8
+//The file structure is simple:
+// TOC_SIZE*PageAddress contains the numbers of the following pages
+// TOC_SIZE*TELETEXT_PAGESIZE contains the page data
+//and the same again.
+bool PackedStorage::seekTo(PageID page, int desc, bool create) {
+ lseek(desc, 0, SEEK_SET);
+ PageAddress addr[TOC_SIZE];
+
+ while (::read(desc, addr, sizeof(addr)) == sizeof(addr)) {
+ lseek(desc, 0, SEEK_CUR);
+ for (int index=0; index<TOC_SIZE; index++) {
+ if (addr[index]==page) {
+ lseek(desc, index*TELETEXT_PAGESIZE, SEEK_CUR);
+ return true;
+ } else if (addr[index].page==0) {
+ //0 means: no more pages follow
+ if (create) {
+ //rewind what was read
+ lseek(desc, -(sizeof(addr)), SEEK_CUR);
+ //update index
+ addr[index]=page;
+ if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
+ return false;
+ //seek to data position
+ lseek(desc, TELETEXT_PAGESIZE*index, SEEK_CUR);
+ return true;
+ } else
+ return false;
+ }
+ }
+
+ //seek over data area
+ lseek(desc, TELETEXT_PAGESIZE*TOC_SIZE, SEEK_CUR);
+ }
+
+ int oldSize=actualFileSize(lseek(desc, 0, SEEK_CUR));
+ if (create) {
+ //create a new set of a TOC and a TOC_SIZE*TELETEXT_PAGESIZE data area
+ memset(addr, 0, sizeof(addr));
+ //first entry is our page
+ addr[0]=page;
+ if (::write(desc, addr, sizeof(addr)) != sizeof(addr))
+ return false;
+ //seek beyond end of file
+ lseek(desc, (TELETEXT_PAGESIZE*TOC_SIZE)-1, SEEK_CUR);
+ //write one byte to enlarge the file to the sought position
+ char c=1;
+ if (::write(desc, &c, 1) != 1)
+ return false;
+ //Now, calculate new file size
+ byteCount += ( actualFileSize(lseek(desc, 0, SEEK_CUR)) - oldSize );
+ //seek to beginning of data, which is requested
+ lseek(desc, -(TELETEXT_PAGESIZE*TOC_SIZE), SEEK_CUR);
+ return true;
+ } else
+ return false;
+}
+
+void PackedStorage::getFilename(char *buffer, int bufLength, PageID page) {
+ //This is a different scheme: page 576_07 will have the name 570s.vtx, the same as e.g. 571_01 or 575_00
+ //Think of "the five hundred seventies"
+ snprintf(buffer, bufLength, "%s/%s/%03xs.vtx", getRootDir(),
+#if VDRVERSNUM >= 10318
+ *page.channel.ToString(),
+#else
+ page.channel.ToString(),
+#endif
+ (page.page & 0xFF0));
+}
+
+StorageHandle PackedStorage::openForWriting(PageID page) {
+ static bool wroteError=false;
+ char filename[PATH_MAX];
+ getFilename(filename, sizeof(filename), page);
+ //first try
+ int desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (desc != -1) {
+ if (!seekTo(page, desc, true)) {
+ ::close(desc);
+ return StorageHandle();
+ }
+ if ( maxBytes && byteCount>maxBytes )
+ freeSpace();
+ return (StorageHandle)desc;
+ }
+ //no space on disk? make some space available
+ if (errno == ENOSPC)
+ freeSpace();
+ //second try
+ desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (desc==-1 && !wroteError) {
+ //report error to syslog - once!
+ wroteError=true;
+ esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno));
+ }
+
+ if (desc==-1)
+ return StorageHandle();
+ else if (!seekTo(page, desc, true)) {
+ ::close(desc);
+ return StorageHandle();
+ }
+
+ if ( maxBytes && byteCount>maxBytes )
+ freeSpace();
+ return (StorageHandle)desc;
+}
+
+StorageHandle PackedStorage::openForReading(PageID page, bool countAsAccess) {
+ int desc;
+ if ( (desc=(int)LegacyStorage::openForReading(page, false))!= -1 ) {
+ if (!seekTo(page, desc, false)) {
+ //this is not an error condition here, may and shall happen!
+ ::close(desc);
+ } else {
+ return (StorageHandle)desc;
+ }
+ }
+ return StorageHandle();
+}
+
+
+
+
+cTelePage::cTelePage(PageID t_page, uchar t_flags, uchar t_lang,int t_mag)
+ : mag(t_mag), flags(t_flags), lang(t_lang), page(t_page)
+{
+ memset(pagebuf,' ',26*40);
+}
+
+cTelePage::~cTelePage() {
+}
+
+void cTelePage::SetLine(int line, uchar *myptr)
+{
+ memcpy(pagebuf+40*line,myptr,40);
+}
+
+void cTelePage::save()
+{
+ Storage *s=Storage::instance();
+ unsigned char buf;
+ StorageHandle fd;
+ if ( (fd=s->openForWriting(page)) ) {
+ s->write("VTXV4",5,fd);
+ buf=0x01; s->write(&buf,1,fd);
+ buf=mag; s->write(&buf,1,fd);
+ buf=page.page; s->write(&buf,1,fd);
+ buf=flags; s->write(&buf,1,fd);
+ buf=lang; s->write(&buf,1,fd);
+ buf=0x00; s->write(&buf,1,fd);
+ buf=0x00; s->write(&buf,1,fd);
+ s->write(pagebuf,24*40,fd);
+ s->close(fd);
+ }
+}
+
+
+cTxtStatus::cTxtStatus(void)
+{
+ receiver = NULL;
+
+ //running=false;
+ TPid=0;
+ /*doNotSuspend=false;
+ doNotReceive=false;*/
+ //suspended=false;
+}
+
+cTxtStatus::~cTxtStatus()
+{
+ /*if (running)
+ Cancel(3);*/
+ if (receiver)
+ delete receiver;
+}
+
+void cTxtStatus::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ if (Device->IsPrimaryDevice()) {
+
+/*#ifdef OSDTELETEXT_REINSERTION_PATCH
+ if (ttSetup.suspendReceiving) {
+ if (!running)
+ Start();
+ } else if (running) { //setup option changed, apply
+ running=false;
+ Cancel(3);
+ }
+#endif*/
+
+ CheckDeleteReceiver();
+
+ if (ChannelNumber) {
+ cChannel *channel = Channels.GetByNumber(ChannelNumber);
+ if (channel && channel->Tpid()) {
+/*#ifdef OSDTELETEXT_REINSERTION_PATCH
+ cMutexLock MutexLock(&mutex);
+ count=0; //reset 20 second intervall
+ condVar.Broadcast();
+ //other thread is locked on the mutex until the end of this function!
+#endif */
+ TPid=channel->Tpid();
+ chan=channel->GetChannelID();
+ CheckCreateReceiver();
+ }
+ }
+ }
+}
+
+void cTxtStatus::CheckCreateReceiver() {
+ if (!receiver && TPid ) {
+ cChannel *channel = Channels.GetByChannelID(chan);
+ if (!channel)
+ return;
+ //primary device a full-featured card
+ if (cDevice::PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit)) {
+ receiver = new cTxtReceiver(TPid, chan);
+ cDevice::PrimaryDevice()->AttachReceiver(receiver);
+ //dsyslog("OSDTeletext: Created teletext receiver for channel %d, PID %d on primary device", ChNum, TPid);
+ //primary device a DXR3 or similar
+ } else {
+ int devNum = cDevice::NumDevices();
+ bool bFound = false;
+ cDevice* pDevice = 0;
+ for (int i = 0; i < devNum && !bFound; ++i) {
+ pDevice = cDevice::GetDevice(i);
+ if (pDevice && pDevice->ProvidesChannel(channel, Setup.PrimaryLimit) && pDevice->Receiving(true)) {
+ bFound = true;
+ receiver = new cTxtReceiver(TPid, chan);
+ pDevice->AttachReceiver(receiver);
+ //dsyslog("OSDTeletext: Created teletext receiver for channel %d, PID %d on device %d", ChNum, TPid, i);
+ }
+ }
+ if (!bFound) //can this happen?
+ esyslog("OSDTeletext: Did not find appropriate device for teletext receiver for channel %s, PID %d", channel->Name(), TPid);
+ }
+ }
+}
+
+void cTxtStatus::CheckDeleteReceiver() {
+ if (receiver) {
+ //dsyslog("OSDTeletext: Deleted teletext receiver");
+ delete receiver;
+/*#ifdef OSDTELETEXT_REINSERTION_PATCH
+ //the patch only makes sense if primary device is a DVB card, so no handling for DXR3
+ cDevice::PrimaryDevice()->ReinsertTeletextPid(TPid);
+#endif*/
+ receiver = NULL;
+ }
+}
+
+/*
+//only used for suspending the receiver, if selected by user in setup
+void cTxtStatus::Action() {
+#ifdef OSDTELETEXT_REINSERTION_PATCH
+ running=true;
+
+ dsyslog("OSDTeletext waiting thread started with pid %d", getpid());
+
+ count=0;
+
+
+ while (running) {
+ cMutexLock MutexLock(&mutex);
+
+ if (doNotSuspend) {
+ CheckCreateReceiver();
+ count=0;
+ } else if (doNotReceive) {
+ CheckDeleteReceiver();
+ count=0;
+ } else {
+ count++;
+ if (count <= 20)
+ CheckCreateReceiver();
+ else if (count < 20+5*60)
+ CheckDeleteReceiver();
+ else
+ count=0; //if count=20+5*60
+ }
+
+ condVar.TimedWait(mutex, 1000); //one second
+
+ }
+
+ running=false;
+ dsyslog("OSDTeletext waiting thread ended");
+
+#endif
+}
+
+//only has an effect when suspending is enabled:
+//prevents receiving from suspension when argument is true
+//reenables suspension when argument is false,
+// but does not necessarily suspend immediately, that is the task of ForceSuspending,
+// in contrast to which it does not make any sense if suspending is
+// not enabled.
+//In clear words: When the plugin is in use, it calls the function
+//with onOrOff=true so that data is received continously during the
+//TeletextBrowser object's lifetime. When it is destroyed, it releases
+//this constraint by calling onOrOff=false.
+void cTxtStatus::ForceReceiving(bool onOrOff) {
+#ifdef OSDTELETEXT_REINSERTION_PATCH
+ if (!running)
+ return;
+ if (onOrOff && !doNotSuspend) {
+ cMutexLock MutexLock(&mutex);
+ doNotSuspend=true;
+ condVar.Broadcast();
+ } else if (!onOrOff && doNotSuspend) {
+ cMutexLock MutexLock(&mutex);
+ doNotSuspend=false;
+ condVar.Broadcast();
+ }
+#endif
+}
+
+//opposite as above:
+//allows to switch off receiving
+void cTxtStatus::ForceSuspending(bool onOrOff) {
+#ifdef OSDTELETEXT_REINSERTION_PATCH
+ if (!running) { //thread is not running, suspend anyway
+ if (onOrOff) {
+ CheckDeleteReceiver();
+ } else {
+ CheckCreateReceiver();
+ }
+ } else {
+ doNotSuspend=false; //ForceReceive may have been called before
+ if (onOrOff && !doNotReceive) {
+ cMutexLock MutexLock(&mutex);
+ doNotReceive=true;
+ condVar.Broadcast();
+ } else if (!onOrOff && doNotReceive) {
+ cMutexLock MutexLock(&mutex);
+ doNotReceive=false;
+ condVar.Broadcast();
+ }
+ }
+#endif
+}
+*/
+
+cTxtReceiver::cTxtReceiver(int TPid, tChannelID chan)
+#if VDRVERSNUM >= 10319
+ : cReceiver(0, -1, TPid),
+#else
+ : cReceiver(0, -1, 1, TPid),
+#endif
+ chan(chan), TxtPage(0), buffer((188+60)*75), running(false)
+{
+ Storage::instance()->prepareDirectory(chan);
+ // 10 ms timeout on getting TS frames
+ buffer.SetTimeouts(0, 10);
+}
+
+
+cTxtReceiver::~cTxtReceiver()
+{
+ Detach();
+ if (running) {
+ running=false;
+ buffer.Signal();
+ Cancel(2);
+ }
+ buffer.Clear();
+ delete TxtPage;
+}
+
+void cTxtReceiver::Activate(bool On)
+{
+ if (On) {
+ if (!running) {
+ running=true;
+ Start();
+ }
+ }
+ else if (running) {
+ running = false;
+ buffer.Signal();
+ Cancel(2);
+ }
+}
+
+void cTxtReceiver::Receive(uchar *Data, int Length)
+{
+ int len = Length+60;
+
+ if (!buffer.Check(len)) {
+ // Buffer overrun
+ buffer.Signal();
+ return;
+ }
+ cFrame *frame=new cFrame(Data, len);
+ if (frame && !buffer.Put(frame)) {
+ // Buffer overrun
+ delete frame;
+ buffer.Signal();
+ }
+}
+
+void cTxtReceiver::Action() {
+
+ while (running) {
+ cFrame *frame=buffer.Get();
+ if (frame) {
+ uchar *Datai=frame->Data();
+
+ for (int i=0; i < 4; i++) {
+ if (Datai[4+i*46]==2 || Datai[4+i*46]==3) {
+ for (int j=(8+i*46);j<(50+i*46);j++)
+ Datai[j]=invtab[Datai[j]];
+ DecodeTXT(&Datai[i*46]);
+ }
+ }
+
+ buffer.Drop(frame);
+ } else
+ buffer.Wait();
+ }
+
+ buffer.Clear();
+ running=false;
+}
+
+uchar cTxtReceiver::unham16 (uchar *p)
+{
+ unsigned short c1,c2;
+ c1=unhamtab[p[0]];
+ c2=unhamtab[p[1]];
+ return (c1 & 0x0F) | (c2 & 0x0F) *16;
+}
+
+void cTxtReceiver::DecodeTXT(uchar* TXT_buf)
+{
+ // Format of buffer:
+ // 0x00-0x04 ?
+ // 0x05-0x06 Clock Run-In?
+ // 0x07 Framing Code?
+ // 0x08 Magazine number (100-digit of page number)
+ // 0x09 Line number
+ // 0x0A..0x31 Line data
+ // Line 0 only:
+ // 0x0A 10-digit of page number
+ // 0x0B 1-digit of page number
+ // 0x0C Sub-Code bits 0..3
+ // 0x0D Sub-Code bits 4..6 + C4 flag
+ // 0x0E Sub-Code bits 8..11
+ // 0x0F Sub-Code bits 12..13 + C5,C6 flag
+ // 0x10 C7-C10 flags
+ // 0x11 C11-C14 flags
+ //
+ // Flags:
+ // C4 - Erase last page, new page transmitted
+ // C5 - News flash, boxed display
+ // C6 - Subtitle, boxed display
+ // C7 - Suppress Header, dont show line 0
+ // C8 - Update, page has changed
+ // C9 - Interrupt Sequence, page number is out of order
+ // C10 - Inhibit Display
+ // C11 - Magazine Serial mode
+ // C12-C14 - Language selection, lower 3 bits
+
+
+ int hdr,mag,mag8,line;
+ uchar *ptr;
+ uchar flags,lang;
+
+ hdr = unham16 (&TXT_buf[0x8]);
+ mag = hdr & 0x07;
+ mag8 = mag ?: 8;
+ line = (hdr>>3) & 0x1f;
+ ptr = &TXT_buf[10];
+
+ switch (line) {
+ case 0:
+ {
+ unsigned char b1, b2, b3, b4;
+ int pgno, subno;
+ b1 = unham16 (ptr);
+ // Page no, 10- and 1-digit
+
+ if (b1 == 0xff) break;
+ if (TxtPage) {
+ TxtPage->save();
+ delete TxtPage;
+ TxtPage=NULL;
+ }
+
+ b2 = unham16 (ptr+2); // Sub-code 0..6 + C4
+ b3 = unham16 (ptr+4); // Sub-code 8..13 + C5,C6
+ b4 = unham16 (ptr+6); // C7..C14
+
+ // flags:
+ // 0x80 C4 - Erase page
+ // 0x40 C5 - News flash
+ // 0x20 C6 - Subtitle
+ // 0x10 C7 - Suppress Header
+ // 0x08 C8 - Update
+ // 0x04 C9 - Interrupt Sequence
+ // 0x02 C9 (Bug?)
+ // 0x01 C11 - Magazine Serial mode
+ flags=b2 & 0x80;
+ flags|=(b3&0x40)|((b3>>2)&0x20); //??????
+ flags|=((b4<<4)&0x10)|((b4<<2)&0x08)|(b4&0x04)|((b4>>1)&0x02)|((b4>>4)&0x01);
+ lang=((b4>>5) & 0x07);
+
+ pgno = mag8 * 256 + b1;
+ subno = (b2 + b3 * 256) & 0x3f7f; // Sub Page Number
+
+ TxtPage = new cTelePage(PageID(chan, pgno, subno), flags, lang, mag);
+ TxtPage->SetLine((int)line,(uchar *)ptr);
+ break;
+ }
+ case 1 ... 25:
+ {
+ if (TxtPage) TxtPage->SetLine((int)line,(uchar *)ptr);
+ break;
+ }
+ /*case 23:
+ {
+ if (TxtPage) {
+ TxtPage->save();
+ delete TxtPage;
+ TxtPage=NULL;
+ }
+ break;
+ }*/
+ default:
+ break;
+ }
+}
+
+