Logo Search packages:      
Sourcecode: vdr-plugin-osdteletext version File versions

txtrecv.c

#include "txtrecv.h"
#include "tables.h"
#include "setup.h"

#include <vdr/channels.h>
#include <vdr/device.h>

#include <pthread.h> 
#include <signal.h> 
#include <errno.h>

#define TXTROOT "/vtx"

#ifdef ALTERNATIVE_STORAGE
TextPages teletextPages;
//TextPagesList teletextPagesList;
#endif


cTelePage::cTelePage(int t_pgno, int t_subno, int t_chnum, uchar t_flags, uchar t_lang,int t_mag, char * Dir)
{
 pnum=t_pgno;
 sub=t_subno;
 chnum=t_chnum;
 flags=t_flags;
 lang=t_lang;
 mag=t_mag;
 memset(pagebuf,' ',26*40);
 strcpy(Directory, Dir);
}

cTelePage::~cTelePage() {}

void cTelePage::SetLine(int line, uchar *myptr)
{
 memcpy(pagebuf+40*line,myptr,40);
}

void cTelePage::save()
{
#ifndef ALTERNATIVE_STORAGE
 char Filename[255];
 unsigned char buf;
 FILE* fd;
 sprintf(Filename,"%s/%3x_%02x.vtx",Directory,pnum,sub);
 if ((fd=fopen(Filename,"w"))) {
   fwrite("VTXV4",1,5,fd);
   buf=0x01; fwrite(&buf,1,1,fd);
   buf=mag;  fwrite(&buf,1,1,fd);
   buf=pnum; fwrite(&buf,1,1,fd);
   buf=flags; fwrite(&buf,1,1,fd);
   buf=lang; fwrite(&buf,1,1,fd);
   buf=0x00; fwrite(&buf,1,1,fd);
   buf=0x00; fwrite(&buf,1,1,fd);
   if (!fwrite(pagebuf,1,24*40,fd) && ferror(fd) && errno==ENOSPC) {
      esyslog("Osd-Teletext: Error: No space left on /vtx");
   }
   fclose (fd);
   }
      
#else //ALTERNATIVE_STORAGE
 int intpage=0;
 SET_PAGE(intpage, pnum);
 SET_SUBPAGE(intpage, sub);
 SET_CHANNEL(intpage, chnum);
 
 char* buf=0;
 buf = (char*)malloc((24*40)+12);
 memcpy(buf,"VTXV4",5);
 buf[5]=0x01;
 buf[6]=mag;
 buf[7]=pnum;
 buf[8]=flags;
 buf[9]=lang;
 buf[10]=0x00;
 buf[11]=0x00;
 memcpy(buf+12, pagebuf,24*40);
 TextPages::iterator it;
 it=teletextPages.find(intpage);
 if (it != teletextPages.end() ) {
   free((*it).second);
   (*it).second=buf;
 } else {
   teletextPages.insert(pair<int,char*>(intpage,buf));
   //teletextPagesList.push_back(ret.first);
 } 
#endif //ALTERNATIVE_STORAGE
}


cTxtStatus::cTxtStatus(void)
{
   receiver = NULL;
 
   running=false;
   TPid=0;
   ChNum=0;
   doNotSuspend=false;
   doNotReceive=false;
   //suspended=false;
   sem_init(&semaphore, 0, 1);
}

cTxtStatus::~cTxtStatus()
{
   if (running)
      Cancel(3);
   if (receiver)
      delete receiver;
   sem_destroy(&semaphore);
}

void cTxtStatus::ChannelSwitch(const cDevice *Device, int ChannelNumber)
{
   sem_wait(&semaphore);
   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();
            ChNum=ChannelNumber;
            CheckCreateReceiver();
         }
      }
   }
   sem_post(&semaphore);
}

void cTxtStatus::CheckCreateReceiver() {
   if (!receiver  && TPid && ChNum) {
      cChannel *channel = Channels.GetByNumber(ChNum);
      //primary device a full-featured card
      if (cDevice::PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit)) {
          receiver = new cTxtReceiver(TPid, ChNum);
          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, ChNum);
               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 %d, PID %d", ChNum, 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,int ChNum) : cReceiver(0, -1, 1, TPid), buffer((188+60)*75)
{
 TxtPage=NULL;
 KanalNr=ChNum;
 running=false;
#ifndef ALTERNATIVE_STORAGE
 char ZielDir[255];
 sprintf(ZielDir,"%s/%0d",TXTROOT,ChNum);
 MakeDirs(ZielDir,1);
#else
 //this is far from optimal
 if (teletextPages.size() > MAX_PAGES_IN_MEMORY) {
   for (int i=teletextPages.size()-MAX_PAGES_IN_MEMORY;i>0;i--) {
      TextPages::iterator it=teletextPages.begin();
      free( (*it).second);
      teletextPages.erase(it);
      ++it;
   }
 }
 if (ttSetup.savePages)
   loadPages();
#endif
 // 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;
#ifdef ALTERNATIVE_STORAGE
 if (ttSetup.savePages)
   savePages();
#endif
}

void cTxtReceiver::Receive(uchar *Data, int Length)
{
   int len = Length+60;

   if (!running) {
      running=true;
      Start();
   }
   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) {
               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)
{
 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);    // lower page number
            if (b1 == 0xff) break;
            if (TxtPage) {
              TxtPage->save();
                  delete TxtPage;
              TxtPage=NULL;
            }

            b2 = unham16 (ptr+2);
            b3 = unham16 (ptr+4);
            b4 = unham16 (ptr+6);

            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
            
            char zieldir[255];
            sprintf(zieldir,"%s/%0d",TXTROOT,KanalNr);
            TxtPage = new cTelePage(pgno,subno,KanalNr,flags,lang,mag,zieldir);
            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: {
           }
  }
}

#ifdef ALTERNATIVE_STORAGE
void cTxtReceiver::loadPages() {
  char Filename[255];
  FILE* fd;
  sprintf(Filename,"%s/%0d.vtx",TXTROOT,KanalNr);
  if ((fd=fopen(Filename,"r"))) {
    while ( feof(fd) == 0 ) {
      int pageNr;
      fread( &pageNr,1,4,fd );
      char* buf = 0;
      buf = (char*)malloc((24*40)+12);
      fread( buf,1,(24*40)+12,fd );
      teletextPages.insert(pair<int,char*>(pageNr,buf));
      //teletextPagesList.push_back(pageNr);
    }
  }
}
    

void cTxtReceiver::savePages() {
 char Filename[255];
 FILE* fd;
 sprintf(Filename,"%s/%0d.vtx",TXTROOT,KanalNr);
 TextPages::iterator it;
 it=teletextPages.begin();
 if ((fd=fopen(Filename,"w"))) {
   while (it != teletextPages.end()) {
     fwrite(&(*it).first,1,4,fd);
     fwrite((*it).second,1,(24*40)+12,fd);
     it++;
   }
   fclose (fd);
 }
}

#endif


Generated by  Doxygen 1.6.0   Back to index