From 4f3abbda0fbeb5a377d4043bfaf8c86955bb6087 Mon Sep 17 00:00:00 2001
From: Chris Tallon <chris@vomp.tv>
Date: Wed, 21 Nov 2007 23:37:56 +0000
Subject: [PATCH] Start of new live tv

---
 boxx.cc              |  33 ++-
 boxx.h               |  54 ++--
 colour.cc            |   1 +
 command.cc           |   7 +-
 eventdispatcher.cc   |   2 +-
 objects.mk           |   9 +-
 tcp.cc               |  82 +++---
 tcp.h                |  12 +-
 vchannellist.cc      |  57 ++--
 vdr.cc               | 156 +++++++----
 vdr.h                |  49 +++-
 vdrrequestpacket.cc  |  26 +-
 vdrrequestpacket.h   |   5 +
 vdrresponsepacket.cc |  58 ++--
 vdrresponsepacket.h  |  22 +-
 vepg.cc              |   4 +
 vvideolive.cc        |   6 +-
 vvideolivetv.cc      | 637 +++++++++++++++++++++++++++++++++++++++++++
 vvideolivetv.h       | 114 ++++++++
 19 files changed, 1101 insertions(+), 233 deletions(-)
 create mode 100644 vvideolivetv.cc
 create mode 100644 vvideolivetv.h

diff --git a/boxx.cc b/boxx.cc
index c9fe60a..3b2596e 100644
--- a/boxx.cc
+++ b/boxx.cc
@@ -65,17 +65,6 @@ void Boxx::draw()
   }  
 }
 
-int Boxx::handleCommand(int x)
-{
-  // A similar comment for this? FIXME make a commandreceiver thingy
-  return 0;
-}
-
-void Boxx::processMessage(Message* m)
-{
-  // And this?
-}
-
 void Boxx::setSize(UINT w, UINT h)
 {
   area.w = w;
@@ -150,12 +139,7 @@ void Boxx::blt(Region& r)
   destination y on screen
   */
 
-  if (parent)
-  {
-    printf("parent blt???\n");
-    abort();
-    return;
-  }
+  if (parent) abort(); // if (parent) then this is a child boxx. It can not blt.
 
   // this shouldn't be here
   r.x -= area.x;
@@ -199,6 +183,16 @@ int Boxx::getY()
   return area.y;
 }
 
+int Boxx::getX2()
+{
+  return area.x + area.w;
+}
+
+int Boxx::getY2()
+{
+  return area.y + area.h;
+}
+
 UINT Boxx::getWidth()
 {
   return area.w;
@@ -209,6 +203,11 @@ UINT Boxx::getHeight()
   return area.h;
 }
 
+Region* Boxx::getRegion()
+{
+  return &area;
+}
+
 // Level 1 drawing functions
 
 void Boxx::fillColour(Colour& colour)
diff --git a/boxx.h b/boxx.h
index 1889519..2c741f7 100644
--- a/boxx.h
+++ b/boxx.h
@@ -42,24 +42,41 @@ class Boxx
     Boxx();
     virtual ~Boxx();
 
-    virtual void setSize(UINT w, UINT h);
+    virtual void setSize(UINT w, UINT h);  // virtual? really?
     void setPosition(UINT x, UINT y); // Set position on parent. Even numbers only!!!
     void createBuffer(); // Make this a root view that goes in the BoxStack
-
-    void setGap(UINT gap);
-
-    // Not really for box.. see .cc
     virtual void draw();
-    virtual int handleCommand(int);
-    virtual void processMessage(Message*);
-    virtual bool mouseMove(int x, int y) {return false;};
-    virtual bool mouseLBDOWN(int x, int y) {return false;};
-
+    
+    
+    void setGap(UINT gap);
     void setBackgroundColour(Colour& colour);
     void setVisible(bool isVisible);
-    virtual void deactivateAllControls(){};
 
 
+    // The following are supposed to be abstract functions
+    // However, it is useful to be able to make instances of Boxx
+    // Therefore the following stubs are provided.
+    virtual int handleCommand(int x) { return 0; }
+    virtual void processMessage(Message* m) {}
+    virtual bool mouseMove(int x, int y) { return false; }
+    virtual bool mouseLBDOWN(int x, int y) { return false; }
+    virtual void deactivateAllControls() {}
+
+
+    // Get functions
+    int getScreenX();        // where is it on screen
+    int getScreenY();
+    int getRootBoxOffsetX(); // where is it relative to the top-parent in the boxstack
+    int getRootBoxOffsetY();
+    int getX();              // where is it relative to its parent
+    int getX2();             // .. and the right edge
+    int getY();
+    int getY2();
+    UINT getWidth();
+    UINT getHeight();
+    bool getVisible();
+    Region* getRegion();     // Not to be used for changing the region
+        
     
     // Drawing functions level 1
     void fillColour(Colour& colour);
@@ -80,19 +97,6 @@ class Boxx
 
     int charWidth(char c);
 
-    // Following 4 used by mouse stuff only, except 1 getWidth/getHeight? in orig. vtabsman
-    int getScreenX();
-    int getScreenY();
-    int getRootBoxOffsetX();
-    int getRootBoxOffsetY();
-    int getX();
-    int getY();
-    UINT getWidth();
-    UINT getHeight();
-    bool getVisible();
-
-    
-
     void add(Boxx*); // a boxx has a set of child boxxs
     void remove(Boxx*);
 
@@ -101,7 +105,7 @@ class Boxx
     It's a hack (that should be deprecated?) to allow things like WSymbol to be created, do some drawing on
     the surface and then be deleted again, leaving the drawing present.
     A better design would be to create many WSymbols - one per symbol and leave them created - then the
-    automatic draw code (not written yet) will be able to redraw the symbols without all that code needing
+    automatic draw code will be able to redraw the symbols without all that code needing
     to be in the derived boxx's draw method.
     */    
     void TEMPADD(Boxx* child) { child->setParent(this); }
diff --git a/colour.cc b/colour.cc
index 32719f1..ac803b8 100644
--- a/colour.cc
+++ b/colour.cc
@@ -26,6 +26,7 @@ Real colours
 Colour Colour::BLACK(0, 0, 0);
 Colour Colour::RED(255, 0, 0);
 Colour Colour::GREEN(0, 255, 0);
+Colour Colour::BLUE(0, 0, 255);
 Colour Colour::YELLOW(255, 255, 0);
 Colour Colour::VIDEOBLUE(0, 0, 150);
 Colour Colour::VIEWBACKGROUND(0, 0, 100);
diff --git a/command.cc b/command.cc
index 343832d..528a937 100644
--- a/command.cc
+++ b/command.cc
@@ -371,7 +371,6 @@ void Command::processMessage(Message* m)
         VVideoLive::getInstance()->streamEnd();
         break;
       }
-
       // Also connection_lost comes from player - anywhere else?
       // FIXME OBSELETE >>
 
@@ -879,14 +878,14 @@ void Command::doJustConnected(VConnect* vconnect)
     boxstack->update(vw);
 
     // Enter pre-keys here
-//    handleCommand(Remote::SIX);
+//    handleCommand(Remote::THREE);
 //    handleCommand(Remote::UP);
 //    handleCommand(Remote::PLAY);
 //    handleCommand(Remote::DOWN);
 //    handleCommand(Remote::DOWN);
 //    handleCommand(Remote::DOWN);
-//    handleCommand(Remote::OK);
-//    handleCommand(Remote::OK);
+    handleCommand(Remote::OK);
+    handleCommand(Remote::OK);
 //    handleCommand(Remote::RED);
   }
 }
diff --git a/eventdispatcher.cc b/eventdispatcher.cc
index 08efeb3..0a23f30 100644
--- a/eventdispatcher.cc
+++ b/eventdispatcher.cc
@@ -40,7 +40,7 @@ bool EventDispatcher::edFindAndCall(void* userTag)
 {
   edLock();
   
-  EDReceiver* edr;
+  EDReceiver* edr = NULL;
   EDRL::iterator i;
   for(i = receivers.begin(); i != receivers.end(); i++)
   {
diff --git a/objects.mk b/objects.mk
index b854ba6..e5e33e0 100644
--- a/objects.mk
+++ b/objects.mk
@@ -5,9 +5,9 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o     \
            player.o playerradio.o vfeed.o afeed.o                             \
            demuxer.o demuxervdr.o demuxerts.o stream.o draintarget.o          \
            region.o colour.o boxstack.o boxx.o tbboxx.o                       \
-           vinfo.o vquestion.o vrecordinglist.o vlivebanner.o                 \
+           vinfo.o vquestion.o vrecordinglist.o                               \
            vmute.o vvolume.o vtimerlist.o vtimeredit.o vrecordingmenu.o       \
-           vchannellist.o vwelcome.o vvideolive.o vvideorec.o vepgsettimer.o  \
+           vchannellist.o vwelcome.o vvideorec.o vepgsettimer.o               \
            vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o      \
            vradiorec.o vaudioselector.o vscreensaver.o vopts.o                \
            wselectlist.o wjpeg.o wsymbol.o wbutton.o wtextbox.o wwss.o        \
@@ -16,5 +16,6 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o     \
            remote.o led.o mtd.o video.o audio.o osd.o surface.o               \
            vmedialist.o media.o vpicture.o vpicturebanner.o                   \
            vaudioplayer.o audioplayer.o demuxeraudio.o abstractoption.o       \
-           eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o
-
+           eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o           \
+           vvideolivetv.o                                                     \
+           vvideolive.o vlivebanner.o
diff --git a/tcp.cc b/tcp.cc
index 70f53ee..512a50b 100644
--- a/tcp.cc
+++ b/tcp.cc
@@ -26,11 +26,25 @@
 
 #include "log.h"
 
+#ifndef WIN32
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex)
+#else
+#define MUTEX_LOCK(mutex) WaitForSingleObject(*(mutex), INFINITE )
+#define MUTEX_UNLOCK(mutex) ReleaseMutex(*(mutex))
+#endif
+
 TCP::TCP()
 {
   sock = 0;
   connected = 0;
   timeoutEnabled = 1;
+
+#ifndef WIN32
+  pthread_mutex_init(&mutex, NULL);
+#else
+  mutex=CreateMutex(NULL,FALSE,NULL);
+#endif
 }
 
 TCP::~TCP()
@@ -40,6 +54,10 @@ TCP::~TCP()
     CLOSESOCKET(sock);
     Log::getInstance()->log("TCP", Log::DEBUG, "Have closed");
   }
+
+#ifdef WIN32
+  CloseHandle(mutex);
+#endif  
 }
 
 void TCP::disableTimeout()
@@ -216,7 +234,7 @@ int TCP::isConnected()
   return connected;
 }
 
-int TCP::sendPacket(void* bufR, size_t count)
+int TCP::sendData(void* bufR, size_t count)
 {
   size_t bytes_sent = 0;
   int this_write;
@@ -224,6 +242,8 @@ int TCP::sendPacket(void* bufR, size_t count)
 
   unsigned char* buf = (unsigned char*)bufR;
 
+  MUTEX_LOCK(&mutex);
+
   while (bytes_sent < count)
   {
     do
@@ -238,57 +258,18 @@ int TCP::sendPacket(void* bufR, size_t count)
 #endif
     if (this_write <= 0)
     {
+      MUTEX_UNLOCK(&mutex);
       return(this_write);
     }
     bytes_sent += this_write;
     buf += this_write;
   }
+  
+  MUTEX_UNLOCK(&mutex);
+  
   return(count);
 }
 
-UCHAR* TCP::receivePacket()
-{
-  ULONG packetLength;
-  int success;
-
-  success = readData((UCHAR*)&packetLength, sizeof(int));
-  if (!success) return NULL;
-
-  packetLength = ntohl(packetLength);
-
-  if (packetLength > 500000)
-  {
-    Log::getInstance()->log("TCP", Log::ERR, "Received packet > 500000");
-    return NULL;
-  }
-  if (packetLength == 0)
-  {
-    Log::getInstance()->log("TCP", Log::ERR, "Received packet len = 0");
-    return NULL;
-  }
-
-  UCHAR* buffer = (UCHAR*) malloc(packetLength);
-
-  success = readData(buffer, packetLength);
-
-  if (!success)
-  {
-    Log::getInstance()->log("TCP", Log::ERR, "readData failed");
-    free(buffer);
-    CLOSESOCKET(sock);
-    connected = 0;
-    return NULL;
-  }
-
-  dataLength = packetLength;
-  return buffer;
-}
-
-int TCP::getDataLength()
-{
-  return dataLength;
-}
-
 int TCP::readData(UCHAR* buffer, int totalBytes)
 {
 
@@ -307,31 +288,30 @@ int TCP::readData(UCHAR* buffer, int totalBytes)
   {
     FD_ZERO(&readSet);
     FD_SET(sock, &readSet);
-    timeout.tv_sec = 20;
+    timeout.tv_sec = 2;
     timeout.tv_usec = 0;
     success = select(sock + 1, &readSet, NULL, NULL, passToSelect);
     if (success < 1)
     {
-      Log::getInstance()->log("TCP", Log::ERR, "Error or timeout");
       return 0;  // error, or timeout
     }
 #ifndef WIN32
     thisRead = read(sock, &buffer[bytesRead], totalBytes - bytesRead);
 #else
-    thisRead = recv(sock,(char*) &buffer[bytesRead],totalBytes - bytesRead, 0);
+    thisRead = recv(sock, (char*)&buffer[bytesRead], totalBytes - bytesRead, 0);
 #endif
-//    printf("read %i\n", thisRead);
+    //Log::getInstance()->log("TCP", Log::DEBUG, "Read %i", thisRead);
     if (!thisRead)
     {
       // if read returns 0 then connection is closed
       // in non-blocking mode if read is called with no data available, it returns -1
       // and sets errno to EGAGAIN. but we use select so it wouldn't do that anyway.
       Log::getInstance()->log("TCP", Log::ERR, "Detected connection closed");
+      CLOSESOCKET(sock);
       connected = 0;
       return 0;
     }
     bytesRead += thisRead;
-
     if (bytesRead == totalBytes)
     {
       return 1;
@@ -340,8 +320,8 @@ int TCP::readData(UCHAR* buffer, int totalBytes)
     {
       if (++readTries == 100)
       {
-//        Log::getInstance()->log("TCP", Log::ERR, "Too many reads");
-//        return 0;
+        Log::getInstance()->log("TCP", Log::ERR, "Too many reads");
+        // return 0;
       }
     }
   }
diff --git a/tcp.h b/tcp.h
index 9b2e6be..e666340 100644
--- a/tcp.h
+++ b/tcp.h
@@ -36,6 +36,7 @@
 #include <arpa/inet.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
+#include <pthread.h>
 #else
 #include <winsock2.h>
 #include <errno.h>
@@ -56,11 +57,8 @@ class TCP
     int isConnected();
     void disableTimeout();
 
-    int sendPacket(void *, size_t size);
-    UCHAR* receivePacket();
-    int getDataLength();
-
     int readData(UCHAR* buffer, int totalBytes);
+    int sendData(void *, size_t size);
 
     static void dump(unsigned char* data, ULONG size);
 
@@ -73,6 +71,12 @@ class TCP
     int timeoutEnabled;
     int dataLength;
 
+#ifndef WIN32
+    pthread_mutex_t mutex;
+#else
+    HANDLE mutex;
+#endif
+
     static UCHAR dcc(UCHAR c);
 };
 
diff --git a/vchannellist.cc b/vchannellist.cc
index ae2dd10..f9191b7 100644
--- a/vchannellist.cc
+++ b/vchannellist.cc
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2005 Chris Tallon
+    Copyright 2004-2007 Chris Tallon
 
     This file is part of VOMP.
 
@@ -23,6 +23,7 @@
 #include "remote.h"
 #include "wsymbol.h"
 #include "vvideolive.h"
+#include "vvideolivetv.h"
 #include "colour.h"
 #include "video.h"
 #include "i18n.h"
@@ -218,14 +219,21 @@ int VChannelList::handleCommand(int command)
       Channel* chan = NULL;
       if (chanList) chan = (Channel*)sl.getCurrentOptionData();
       if (chan == NULL) return 2;
-
-      VVideoLive* v = new VVideoLive(chanList, chan->type, this);
-
-      v->draw();
-      boxstack->add(v);
-      boxstack->update(v);
-
-      v->channelChange(VVideoLive::NUMBER, chan->number);
+ 
+      if (chan->type == VDR::VIDEO)
+      {
+        VVideoLiveTV* v = new VVideoLiveTV(chanList, chan->number, this);
+        boxstack->add(v);
+        v->go();
+      }
+      else
+      {
+        VVideoLive* v = new VVideoLive(chanList, chan->type, this);
+        v->draw();
+        boxstack->add(v);
+        boxstack->update(v);
+        v->channelChange(VVideoLive::NUMBER, chan->number);
+      }
 
       return 2;
     }
@@ -267,23 +275,34 @@ void VChannelList::processMessage(Message* m)
   }
   else if (m->message == Message::CHANNEL_CHANGE)
   {
-    bool isinlist = false;
-
+    Channel* chan = NULL;
     for (UINT i = 0; i < chanList->size(); i++)
     {
       if ((*chanList)[i]->number == m->parameter)
       {
-        isinlist = true;
+        chan = (*chanList)[i];
         break;
       }
     }
-    if (!isinlist) return;
-
-    VVideoLive* v = new VVideoLive(chanList, VDR::VIDEO, this); // FIXME - what's wrong with it?
+    if (!chan) return;
 
-    v->draw();
-    boxstack->add(v);
-    boxstack->update(v);
-    v->channelChange(VVideoLive::NUMBER, m->parameter);
+/*
+    if (chan->type == VDR::VIDEO)
+    {
+      VVideoLiveTV* v = new VVideoLiveTV(chanList, this);
+      v->draw();
+      boxstack->add(v);
+      boxstack->update(v);
+      v->channelChange(VVideoLive::NUMBER, chan->number);
+    }
+    else
+    {
+      VVideoLive* v = new VVideoLive(chanList, chan->type, this);
+      v->draw();
+      boxstack->add(v);
+      boxstack->update(v);
+      v->channelChange(VVideoLive::NUMBER, chan->number);
+    }
+*/
   }
 }
diff --git a/vdr.cc b/vdr.cc
index bfb281d..cb9032b 100644
--- a/vdr.cc
+++ b/vdr.cc
@@ -33,15 +33,6 @@
 
 VDR* VDR::instance = NULL;
 
-#ifndef WIN32
-#define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex)
-#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex)
-#else
-#define MUTEX_LOCK(mutex) WaitForSingleObject(*(mutex), INFINITE )
-#define MUTEX_UNLOCK(mutex) ReleaseMutex(*(mutex))
-#endif
-
-
 VDR::VDR()
 {
   if (instance) return;
@@ -49,11 +40,6 @@ VDR::VDR()
   initted = 0;
   findingServer = 0;
   tcp = NULL;
-#ifndef WIN32
-  pthread_mutex_init(&mutex, NULL);
-#else
-  mutex=CreateMutex(NULL,FALSE,NULL);
-#endif
   connected = false;
   maxChannelNumber = 0;
   channelNumberWidth = 1;
@@ -61,9 +47,6 @@ VDR::VDR()
 
 VDR::~VDR()
 {
-#ifdef WIN32
-  CloseHandle(mutex);
-#endif
   instance = NULL;
   if (initted) shutdown();
 }
@@ -200,19 +183,70 @@ void VDR::threadMethod()
 {
   threadSetKillable();
   
-  UCHAR* packet;
-  ULONG packetLength;
+  ULONG channelID;
+  
+  ULONG requestID;
+  ULONG userDataLength;
+  UCHAR* userData;
+
+  ULONG streamID;
+
   VDR_ResponsePacket* vresp;
   
   while(1) 
-  {
-    packet = (UCHAR*)tcp->receivePacket();  // cancellation point
-
-    vresp = new VDR_ResponsePacket();    
-    if (packet)
+  {  
+    if (!tcp->readData((UCHAR*)&channelID, sizeof(ULONG)))
+    {
+      // Error or timeout.
+      Log::getInstance()->log("VDR", Log::DEBUG, "Net read timeout");
+      
+      // Do timeouts
+      
+      continue;      
+    }
+    
+    vresp = new VDR_ResponsePacket();  
+    
+    if (channelID == CHANNEL_REQUEST_RESPONSE)
+    {
+      if (!tcp->readData((UCHAR*)&requestID, sizeof(ULONG))) break;
+      if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break;
+      if (userDataLength > 5000000) break; // how big can these packets get?
+      if (userDataLength > 0)
+      {
+        userData = (UCHAR*)malloc(userDataLength);
+        if (!userData) break;
+        if (!tcp->readData(userData, userDataLength)) break;
+      }
+      else
+      {
+        userData = NULL;
+      }
+      vresp->setResponse(requestID, userData, userDataLength);
+      Log::getInstance()->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
+    }
+    else if (channelID == CHANNEL_STREAM)
+    {
+      if (!tcp->readData((UCHAR*)&streamID, sizeof(ULONG))) break;
+      if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; 
+      if (userDataLength > 0)
+      {
+        userData = (UCHAR*)malloc(userDataLength);
+        if (!userData) break;
+        if (!tcp->readData(userData, userDataLength)) break;
+      }
+      else
+      {
+        userData = NULL;
+      }
+      vresp->setStream(streamID, userData, userDataLength);
+      Log::getInstance()->log("VDR", Log::DEBUG, "Rxd a stream packet, streamID=%lu, len=%lu", streamID, userDataLength);
+    }
+    else
     {
-      packetLength = (ULONG)tcp->getDataLength();
-      vresp->set(packet, packetLength);  
+      Log::getInstance()->log("VDR", Log::ERR, "Rxd a response packet on channel %lu !!", channelID);
+      delete vresp;
+      break;
     }
 
     if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
@@ -238,16 +272,24 @@ bool VDR::ed_cb_find(EDReceiver* edr, void* userTag)
   
   // Is vresp for vdrpr ?
   
-  // Not written yet. will be true
-  if (vdrpr);
-  if (vresp);
-  
-  return true;
+  ULONG packetChannel = vresp->getChannelID();
+  if (vdrpr->receiverChannel != packetChannel) return false;
+
+  if (packetChannel == CHANNEL_REQUEST_RESPONSE)
+  {
+    if (vdrpr->requestSerialNumber == vresp->getRequestID()) return true;
+  }
+  else if (packetChannel == CHANNEL_STREAM)
+  {
+    if (vdrpr->streamID == vresp->getStreamID()) return true;
+  }
+ 
+  return false;
 }
 
 VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
 {
-  logger->log("VDR", Log::DEBUG, "RR");
+  logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode());
 
   if (!connected)
   {
@@ -259,31 +301,29 @@ VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
   // make a VDR_PacketReceiver
   // - init with serial number of request packet
 
-  VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver();
-  vdrpr->receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE;
-  vdrpr->requestSerialNumber = vrp->getSerial();
-  edRegister(vdrpr);
+  VDR_PacketReceiver vdrpr;
+  vdrpr.receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE;
+  vdrpr.requestSerialNumber = vrp->getSerial();
+  edRegister(&vdrpr);
   
   edLock();  
-  if ((ULONG)tcp->sendPacket(vrp->getPtr(), vrp->getLen()) != vrp->getLen())
+  if ((ULONG)tcp->sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen())
   {
     edUnlock();
+    edUnregister(&vdrpr);
     VDR_ResponsePacket* vresp = new VDR_ResponsePacket();
     return vresp; // "no-response" return
   }
 
   // Sleep and block this thread. The sleep unlocks the mutex
   logger->log("VDR", Log::DEBUG, "RR sleep");
-  edSleepThisReceiver(vdrpr);
+  edSleepThisReceiver(&vdrpr);
   logger->log("VDR", Log::DEBUG, "RR unsleep");
     
   // Woken because a response packet has arrived, mutex will be locked
   
   edUnlock();
-  
-  VDR_ResponsePacket* toReturn = vdrpr->save_vresp;
-  delete vdrpr;
-  return toReturn;
+  return vdrpr.save_vresp;
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -304,7 +344,7 @@ bool VDR_PacketReceiver::call(void* userTag)
   if (receiverChannel == VDR::CHANNEL_STREAM)
   {
     // It's a stream packet.
-    streamReceiver->receiveData(NULL, 0);
+    streamReceiver->streamReceive(NULL, 0);
     delete (VDR_ResponsePacket*)userTag;
     return false;
   }
@@ -489,14 +529,34 @@ ChannelList* VDR::getChannelsList(ULONG type)
   return chanList;
 }
 
+
 int VDR::streamChannel(ULONG number)
+{
+  // FIXME radio
+  return 0;
+}
+
+int VDR::streamChannel(ULONG number, StreamReceiver* tstreamReceiver)
 {
   VDR_RequestPacket vrp;
   if (!vrp.init(VDR_STREAMCHANNEL, true, sizeof(ULONG))) return 0;
   if (!vrp.addULONG(number)) return 0;
   
+  
+  VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver(); // FIXME - leaked
+  vdrpr->receiverChannel = VDR::CHANNEL_STREAM;
+  vdrpr->streamID = vrp.getSerial();
+  vdrpr->streamReceiver = tstreamReceiver;
+  edRegister(vdrpr);
+  
   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
-  if (vresp->noResponse()) { delete vresp; return 0; }
+  if (vresp->noResponse())
+  {
+    delete vresp;
+    edUnregister(vdrpr);
+    delete vdrpr;
+    return 0;
+  }
   
   int toReturn = (int)vresp->extractULONG();
   delete vresp;
@@ -546,8 +606,8 @@ UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived, ULON
   }
 
   // Special handling for getblock
-  UCHAR* toReturn = vresp->getBlock_getPacket();
-  *amountReceived = vresp->getLength();
+  UCHAR* toReturn = vresp->getBlock_getUserData();
+  *amountReceived = vresp->getUserDataLength();
   
   delete vresp;
   
@@ -942,9 +1002,9 @@ MediaList* VDR::getMediaList(const char* parent,int mediaType)
     return NULL;
   }
   
-  if (vresp->getLength() < 12)
+  if (vresp->getUserDataLength() < 12)
   {
-    Log::getInstance()->log("VDR", Log::ERR, "receiveMediaList packet too short, expected 12, got %d", vresp->getLength());
+    Log::getInstance()->log("VDR", Log::ERR, "receiveMediaList packet too short, expected 12, got %d", vresp->getUserDataLength());
     delete vresp;
     return NULL;
   }
diff --git a/vdr.h b/vdr.h
index 68f53ed..44a8e86 100644
--- a/vdr.h
+++ b/vdr.h
@@ -79,7 +79,7 @@ class RecMan;
 class StreamReceiver
 {
   public:
-    void receiveData(void*, ULONG) {};
+    virtual void streamReceive(void*, ULONG)=0;
 };
 
 class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc
@@ -87,6 +87,8 @@ class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc
   public:
     virtual bool call(void* userTag);
 
+  friend class VDR;
+  protected:
     ULONG receiverChannel;
     
     // If receiverChannel == 1:
@@ -94,6 +96,7 @@ class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc
     VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse
         
     // If receiverChannel == 2:
+    ULONG streamID;
     StreamReceiver* streamReceiver;
 };
 
@@ -154,6 +157,7 @@ class VDR : public Thread_TYPE, public EventDispatcher
     MarkList*     getMarks(char* fileName);
     int           deleteTimer(RecTimer* delTimer);
     ChannelList*  getChannelsList(ULONG type);
+    int           streamChannel(ULONG number, StreamReceiver*);
     int           streamChannel(ULONG number);
     void          getChannelPids(Channel* channel);
     UCHAR*        getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived);
@@ -201,20 +205,6 @@ class VDR : public Thread_TYPE, public EventDispatcher
     ULONG maxChannelNumber;
     ULONG channelNumberWidth;
 
-#ifndef WIN32
-    pthread_mutex_t mutex;
-#else
-    HANDLE mutex;
-#endif
-
-
-#ifndef WIN32
-    // KIS for now
-    pthread_t waitingRequestThread;
-#else
-    // FIXME - Marten
-#endif
-
     const static ULONG VDR_LOGIN               = 1;
     const static ULONG VDR_GETRECORDINGLIST    = 2;
     const static ULONG VDR_DELETERECORDING     = 3;
@@ -272,4 +262,33 @@ Picture types:
 #define P_FRAME    2
 #define B_FRAME    3
 
+
+
+Packet formats
+
+Packet format for an RR channel request:
+
+4 bytes = channel ID = 1 (request/response channel)
+4 bytes = request ID (from serialNumber)
+4 bytes = opcode
+4 bytes = length of the rest of the packet
+? bytes = rest of packet. depends on packet
+
+
+Packet format for an RR channel response:
+
+4 bytes = channel ID = 1 (request/response channel)
+4 bytes = request ID (from serialNumber)
+4 bytes = length of the rest of the packet
+? bytes = rest of packet. depends on packet
+
+
+Packet format for a stream packet:
+
+4 bytes = channel ID = 2 (stream channel)
+4 bytes = stream ID (from requestID)
+4 bytes = length of the stream data (rest of packet)
+? bytes = stream data
+
 */
+
diff --git a/vdrrequestpacket.cc b/vdrrequestpacket.cc
index 25ce2cb..851d9d8 100644
--- a/vdrrequestpacket.cc
+++ b/vdrrequestpacket.cc
@@ -19,20 +19,13 @@
 */
 
 #include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include "vdrrequestpacket.h"
 
 #include "vdr.h"
 
-/* Packet format for an RR channel request:
-
-4 bytes = channel ID = 1 (request/response channel)
-4 bytes = request ID (from serialNumber)
-4 bytes = opcode
-4 bytes = length of the rest of the packet
-? bytes = rest of packet. depends on packet
-*/
-
 ULONG VDR_RequestPacket::serialNumberCounter = 1;
 
 VDR_RequestPacket::VDR_RequestPacket()
@@ -42,6 +35,8 @@ VDR_RequestPacket::VDR_RequestPacket()
   bufUsed = 0;
   lengthSet = false;
   serialNumber = 0;
+  
+  opcode = 0;
 }
 
 VDR_RequestPacket::~VDR_RequestPacket()
@@ -49,7 +44,7 @@ VDR_RequestPacket::~VDR_RequestPacket()
   free(buffer);
 }
 
-bool VDR_RequestPacket::init(ULONG opcode, bool setUserDataLength, ULONG userDataLength)
+bool VDR_RequestPacket::init(ULONG topcode, bool setUserDataLength, ULONG userDataLength)
 {
   if (buffer) return false;
   
@@ -69,11 +64,12 @@ bool VDR_RequestPacket::init(ULONG opcode, bool setUserDataLength, ULONG userDat
   
   channel = VDR::CHANNEL_REQUEST_RESPONSE;
   serialNumber = serialNumberCounter++;
+  opcode = topcode;
   
   *(ULONG*)&buffer[0] = htonl(channel);
   *(ULONG*)&buffer[4] = htonl(serialNumber);
   *(ULONG*)&buffer[8] = htonl(opcode);
-  *(ULONG*)&buffer[12] = htonl(userDataLength);
+  *(ULONG*)&buffer[userDataLenPos] = htonl(userDataLength);
   bufUsed = headerLength;
 
   return true;
@@ -84,7 +80,7 @@ bool VDR_RequestPacket::copyin(const UCHAR* src, ULONG len)
   if (!checkExtend(len)) return false;
   memcpy(buffer + bufUsed, src, len);
   bufUsed += len;
-  if (!lengthSet) *(ULONG*)&buffer[12] = htonl(bufUsed - headerLength);
+  if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
   return true;
 }
 
@@ -94,7 +90,7 @@ bool VDR_RequestPacket::addString(const char* string)
   if (!checkExtend(len)) return false;
   memcpy(buffer + bufUsed, string, len);
   bufUsed += len;
-  if (!lengthSet) *(ULONG*)&buffer[12] = htonl(bufUsed - headerLength);
+  if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
   return true;
 }
 
@@ -103,7 +99,7 @@ bool VDR_RequestPacket::addULONG(ULONG ul)
   if (!checkExtend(sizeof(ULONG))) return false;
   *(ULONG*)&buffer[bufUsed] = htonl(ul);
   bufUsed += sizeof(ULONG);
-  if (!lengthSet) *(ULONG*)&buffer[12] = htonl(bufUsed - headerLength);
+  if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
   return true;
 }   
 
@@ -112,7 +108,7 @@ bool VDR_RequestPacket::addULLONG(ULLONG ull)
   if (!checkExtend(sizeof(ULLONG))) return false;
   *(ULLONG*)&buffer[bufUsed] = htonll(ull);
   bufUsed += sizeof(ULLONG);
-  if (!lengthSet) *(ULONG*)&buffer[12] = htonl(bufUsed - headerLength);
+  if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
   return true;
 }
 
diff --git a/vdrrequestpacket.h b/vdrrequestpacket.h
index 3642ba6..03ff75a 100644
--- a/vdrrequestpacket.h
+++ b/vdrrequestpacket.h
@@ -44,6 +44,8 @@ class VDR_RequestPacket
     ULONG getChannel() { return channel; }
     ULONG getSerial() { return serialNumber; }
     
+    ULONG getOpcode() { return opcode; }
+    
   private:
     static ULONG serialNumberCounter;
     
@@ -55,9 +57,12 @@ class VDR_RequestPacket
     ULONG channel;
     ULONG serialNumber;
 
+    ULONG opcode;
+
     bool checkExtend(ULONG by);
     
     const static ULONG headerLength = 16;
+    const static ULONG userDataLenPos = 12;
 };
 
 #endif
diff --git a/vdrresponsepacket.cc b/vdrresponsepacket.cc
index 121a587..3f3810e 100644
--- a/vdrresponsepacket.cc
+++ b/vdrresponsepacket.cc
@@ -20,40 +20,52 @@
 
 #include "vdrresponsepacket.h"
 
+#include "vdr.h"
+
 VDR_ResponsePacket::VDR_ResponsePacket()
 {
-  packetLength = 0;
+  userDataLength = 0;
   packetPos = 0;
-  packet = NULL;
+  userData = NULL;
   getBlockRelease = false;
+  
+  channelID = 0;
+  
+  requestID = 0;
+  streamID = 0;
 }
 
 VDR_ResponsePacket::~VDR_ResponsePacket()
 {
   if (getBlockRelease) return; // don't free if it's a getblock
   
-  if (packet) free(packet);
+  if (userData) free(userData);
 }
 
-void VDR_ResponsePacket::set(UCHAR* tpacket, ULONG tpacketLength)
+void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength)
 {
-  packet = tpacket;
-  packetLength = tpacketLength;
+  channelID = VDR::CHANNEL_REQUEST_RESPONSE;
+  requestID = trequestID;
+  userData = tuserData;
+  userDataLength = tuserDataLength;
 }
 
-ULONG VDR_ResponsePacket::getLength()
+void VDR_ResponsePacket::setStream(ULONG tstreamID, UCHAR* tuserData, ULONG tuserDataLength)
 {
-  return packetLength;
+  channelID = VDR::CHANNEL_STREAM;
+  streamID = tstreamID;
+  userData = tuserData;
+  userDataLength = tuserDataLength;
 }
 
 bool VDR_ResponsePacket::end()
 {
-  return (packetPos >= packetLength);
+  return (packetPos >= userDataLength);
 }
 
 int VDR_ResponsePacket::serverError()
 {
-  if ((packetPos == 0) && (packetLength == 4) && !ntohl(*(ULONG*)packet)) return 1;
+  if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1;
   else return 0;
 }
 
@@ -61,49 +73,49 @@ char* VDR_ResponsePacket::extractString()
 {
   if (serverError()) return NULL;
 
-  int length = strlen((char*)&packet[packetPos]);
-  if ((packetPos + length) > packetLength) return NULL;
+  int length = strlen((char*)&userData[packetPos]);
+  if ((packetPos + length) > userDataLength) return NULL;
   char* str = new char[length + 1];
-  strcpy(str, (char*)&packet[packetPos]);
+  strcpy(str, (char*)&userData[packetPos]);
   packetPos += length + 1;
   return str;
 }
 
 UCHAR VDR_ResponsePacket::extractUCHAR()
 {
-  if ((packetPos + sizeof(UCHAR)) > packetLength) return 0;
-  UCHAR uc = packet[packetPos];
+  if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0;
+  UCHAR uc = userData[packetPos];
   packetPos += sizeof(UCHAR);
   return uc;
 }
 
 ULONG VDR_ResponsePacket::extractULONG()
 {
-  if ((packetPos + sizeof(ULONG)) > packetLength) return 0;
-  ULONG ul = ntohl(*(ULONG*)&packet[packetPos]);
+  if ((packetPos + sizeof(ULONG)) > userDataLength) return 0;
+  ULONG ul = ntohl(*(ULONG*)&userData[packetPos]);
   packetPos += sizeof(ULONG);
   return ul;
 }
 
 ULLONG VDR_ResponsePacket::extractULLONG()
 {
-  if ((packetPos + sizeof(ULLONG)) > packetLength) return 0;
-  ULLONG ull = ntohll(*(ULLONG*)&packet[packetPos]);
+  if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0;
+  ULLONG ull = ntohll(*(ULLONG*)&userData[packetPos]);
   packetPos += sizeof(ULLONG);
   return ull;
 }
 
 long VDR_ResponsePacket::extractLONG()
 {
-  if ((packetPos + sizeof(long)) > packetLength) return 0;
-  long l = ntohl(*(long*)&packet[packetPos]);
+  if ((packetPos + sizeof(long)) > userDataLength) return 0;
+  long l = ntohl(*(long*)&userData[packetPos]);
   packetPos += sizeof(long);
   return l;
 }
 
-UCHAR* VDR_ResponsePacket::getBlock_getPacket()
+UCHAR* VDR_ResponsePacket::getBlock_getUserData()
 {
   getBlockRelease = true;
-  return packet;
+  return userData;
 }
 
diff --git a/vdrresponsepacket.h b/vdrresponsepacket.h
index a750551..e37edd6 100644
--- a/vdrresponsepacket.h
+++ b/vdrresponsepacket.h
@@ -33,11 +33,16 @@ class VDR_ResponsePacket
     VDR_ResponsePacket();
     ~VDR_ResponsePacket();
     
-    void set(UCHAR* packet, ULONG packetLength);
+    void setResponse(ULONG requestID, UCHAR* packet, ULONG packetLength);
+    void setStream(ULONG streamID, UCHAR* packet, ULONG packetLength);
      
-    bool noResponse() { return (packet == NULL); };
+    bool noResponse() { return (userData == NULL); };
     int  serverError();
-    ULONG getLength();
+    
+    ULONG getUserDataLength() { return userDataLength; }
+    ULONG getChannelID() { return channelID; }
+    ULONG getRequestID() { return requestID; }
+    ULONG getStreamID() { return streamID; }
     
     char*  extractString();
     UCHAR  extractUCHAR();
@@ -48,13 +53,18 @@ class VDR_ResponsePacket
     bool end();
 
     // Do this a better way?
-    UCHAR* getBlock_getPacket();
+    UCHAR* getBlock_getUserData();
 
   private:
-    UCHAR* packet;
-    ULONG packetLength;
+    UCHAR* userData;
+    ULONG userDataLength;
     ULONG packetPos;
     
+    ULONG channelID;
+    
+    ULONG requestID;
+    ULONG streamID;
+        
     bool getBlockRelease;
 };
 
diff --git a/vepg.cc b/vepg.cc
index d18787e..4982be3 100644
--- a/vepg.cc
+++ b/vepg.cc
@@ -353,9 +353,13 @@ int VEpg::handleCommand(int command)
     case Remote::DF_DOWN:
     case Remote::DOWN:
     { // cursor down the channel list
+      Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
+      
       chanListbox.down();
       drawData();
       boxstack->update(this);
+      Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
+
       return 2;
     }
     case Remote::DF_LEFT:
diff --git a/vvideolive.cc b/vvideolive.cc
index 06e1864..90945be 100644
--- a/vvideolive.cc
+++ b/vvideolive.cc
@@ -497,9 +497,13 @@ void VVideoLive::showEPG()
 
   VEpg* vepg = new VEpg(this, currentChannel, streamType);
   vepg->draw();
-
+  
+  Log::getInstance()->log("VVideoLive", Log::DEBUG, "EPG draw finished");
+  
   boxstack->add(vepg);
   boxstack->update(vepg);
+  
+  Log::getInstance()->log("VVideoLive", Log::DEBUG, "EPG blttd to screen");
 }
 
 void VVideoLive::toggleChopSides()
diff --git a/vvideolivetv.cc b/vvideolivetv.cc
new file mode 100644
index 0000000..e9a387d
--- /dev/null
+++ b/vvideolivetv.cc
@@ -0,0 +1,637 @@
+/*
+    Copyright 2007 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP 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.
+
+    VOMP 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 VOMP; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "vvideolivetv.h"
+
+#include "vchannellist.h"
+#include "video.h"
+//#include "playerlivetv.h"
+#include "channel.h"
+#include "boxstack.h"
+#include "colour.h"
+#include "osd.h"
+#include "command.h"
+#include "i18n.h"
+#include "wtextbox.h"
+#include "remote.h"
+#include "vaudioselector.h"
+#include "colour.h"
+#include "event.h"
+
+VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
+{
+  vdr = VDR::getInstance();
+  boxstack = BoxStack::getInstance();
+  video = Video::getInstance();
+
+  chanList = tchanList;
+  vchannelList = tvchannelList;
+  numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
+
+  currentChannelIndex = 0;
+  previousChannelIndex = 0;
+  osdChannelIndex = 0;
+  keying = 0;
+
+  // Convert channel number to index
+  UINT i;
+  for(i = 0; i < chanList->size(); i++)
+  {
+    if ((*chanList)[i]->number == (UINT)initialChannelNumber)
+    {
+      currentChannelIndex = i;
+      osdChannelIndex = i;
+      break;
+    }
+  }
+
+  eventList = NULL;
+
+  videoMode = video->getMode();
+//  player = new PlayerLiveTV(Command::getInstance(), this);
+//  player->init(chanList);
+
+  setSize(video->getScreenWidth(), video->getScreenHeight());
+  createBuffer();
+  Colour transparent(0, 0, 0, 0);
+  setBackgroundColour(transparent);
+
+  dowss = false;
+  char* optionWSS = vdr->configLoad("General", "WSS");
+  if (optionWSS)
+  {
+    if (strstr(optionWSS, "Yes")) dowss = true;
+    delete[] optionWSS;
+  }
+  Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
+
+  if (dowss)
+  {
+    wss.setFormat(video->getFormat());
+    wss.setWide(true);
+    add(&wss);
+    
+    wssRegion.x = 0;
+    wssRegion.y = 6;
+    wssRegion.w = video->getScreenWidth();
+    wssRegion.h = 2;
+  }
+  
+  Colour osdBack = Colour(50, 50, 50);
+  
+  osd.setBackgroundColour(osdBack);
+  osd.setPosition(0, video->getScreenHeight() - 150);
+  osd.setSize(video->getScreenWidth(), 150);
+  osd.setVisible(false);
+  add(&osd);
+  
+  clock.setBackgroundColour(Colour::BLACK);
+  clock.setPosition(osd.getWidth() - 100, 4);
+  clock.setSize(90, 30);
+  clock.setText("00:00");
+  osd.add(&clock);
+
+  osdChanNum.setBackgroundColour(Colour::BLACK);
+  osdChanNum.setPosition(40, 4);
+  osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
+  osd.add(&osdChanNum);  
+
+  osdChanName.setBackgroundColour(Colour::BLACK);
+  osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
+  osdChanName.setSize(300, 30);
+  osd.add(&osdChanName);
+  
+  boxRed.setBackgroundColour(Colour::RED);
+  boxRed.setPosition(50, 104);
+  boxRed.setSize(18, 16);
+  osd.add(&boxRed);
+
+  boxGreen.setBackgroundColour(Colour::GREEN);
+  boxGreen.setPosition(220, 104);
+  boxGreen.setSize(18, 16);
+  osd.add(&boxGreen);
+
+  boxYellow.setBackgroundColour(Colour::YELLOW);
+  boxYellow.setPosition(390, 104);
+  boxYellow.setSize(18, 16);
+  osd.add(&boxYellow);
+
+  boxBlue.setBackgroundColour(Colour::BLUE);
+  boxBlue.setPosition(560, 104);
+  boxBlue.setSize(18, 16);
+  osd.add(&boxBlue);  
+  
+  textRed.setBackgroundColour(Colour::BLACK);
+  textRed.setPosition(boxRed.getX()+18, 98);
+  textRed.setSize(120, 30);
+  textRed.setText("Summary");
+  osd.add(&textRed);  
+    
+  textGreen.setBackgroundColour(Colour::BLACK);
+  textGreen.setPosition(boxGreen.getX()+18, 98);
+  textGreen.setSize(120, 30);
+  textGreen.setText("Audio");
+  osd.add(&textGreen);  
+    
+  textYellow.setBackgroundColour(Colour::BLACK);
+  textYellow.setPosition(boxYellow.getX()+18, 98);
+  textYellow.setSize(120, 30);
+  textYellow.setText("");
+  osd.add(&textYellow);  
+    
+  textBlue.setBackgroundColour(Colour::BLACK);
+  textBlue.setPosition(boxBlue.getX()+18, 98);
+  textBlue.setSize(90, 30);
+  textBlue.setText("EPG");
+  osd.add(&textBlue);  
+    
+  sl.setPosition(70, 36);
+  sl.setSize(500, 58);
+  sl.setNoLoop();
+  osd.add(&sl);
+}
+
+VVideoLiveTV::~VVideoLiveTV()
+{
+//  delete player;
+  video->setDefaultAspect();
+}
+
+int VVideoLiveTV::handleCommand(int command)
+{
+  switch(command)
+  {
+    case Remote::STOP:
+    case Remote::BACK:
+    case Remote::MENU:
+    {
+      stop();
+      vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
+      return 4;
+    }
+    case Remote::UP:
+    {
+      // New remote only
+      // epg data up
+      sl.up();
+      sl.draw();
+      boxstack->update(this, osd.getRegion());
+      return 2;
+    }
+    case Remote::DOWN:
+    {
+      // New remote only
+      // epg data down
+      sl.down();
+      sl.draw();
+      boxstack->update(this, osd.getRegion());
+      return 2;
+    }
+    case Remote::LEFT:
+    {
+      // New remote only
+      // epg data ch down
+      doLeft();
+      return 2;
+    }
+    case Remote::RIGHT:
+    {
+      // New remote only
+      // epg data ch up
+      doRight();
+      return 2;
+    }
+    case Remote::DF_UP:
+    case Remote::CHANNELUP:
+    {
+      doChanUp();
+      return 2;
+    }
+    case Remote::DF_DOWN:
+    case Remote::CHANNELDOWN:
+    {
+      doChanDown();
+      return 2;
+    }
+    case Remote::PREVCHANNEL:
+    {
+      channelChange(PREVIOUS, 0);
+      return 2;
+    }
+    case Remote::OK:
+    {
+      doOK();
+      return 2;
+    }
+    case Remote::GUIDE:
+    case Remote::RED:
+    {
+      return 2;
+    }
+    case Remote::FULL:
+    case Remote::TV:
+    {
+      toggleChopSides();
+      return 2;
+    }
+
+    case Remote::ZERO:
+    case Remote::ONE:
+    case Remote::TWO:
+    case Remote::THREE:
+    case Remote::FOUR:
+    case Remote::FIVE:
+    case Remote::SIX:
+    case Remote::SEVEN:
+    case Remote::EIGHT:
+    case Remote::NINE:
+    {
+      // key in channel number
+      doKey(command);
+      return 2;
+    }
+
+    case Remote::GREEN:
+    {
+      /*
+      VAudioSelector* vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((Player*)player)->getCurrentAudioChannel());
+      vas->setBackgroundColour(Colour::VIEWBACKGROUND);
+      vas->setPosition(0, getHeight()-200);
+      vas->draw();
+      BoxStack::getInstance()->add(vas);
+      BoxStack::getInstance()->update(vas);        
+      */
+    }
+#ifdef DEV
+    case Remote::YELLOW:
+    {
+    }
+    case Remote::BLUE:
+    {
+    }
+#endif
+  }
+
+  return 1;
+}
+
+void VVideoLiveTV::go()
+{
+  doNowNext();
+  osd.setVisible(true);
+  draw();
+  boxstack->update(this);
+  // set a timer for osd deletion
+
+  // start player  
+}
+
+void VVideoLiveTV::stop()
+{  
+}
+
+void VVideoLiveTV::doNowNext()
+{
+  delData();
+  keying = 0;
+  
+  Channel* currentChannel = (*chanList)[osdChannelIndex];
+
+  char formatChanNum[20];
+  SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
+  osdChanNum.setText(formatChanNum);
+  osdChanName.setText(currentChannel->name);
+
+  eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
+
+  if (!eventList)
+  {
+    sl.addOption(tr("No channel data available"), 0, 1);
+  }
+  else
+  {
+    sort(eventList->begin(), eventList->end(), EventSorter());
+
+    char tempString[300];
+    char tempString2[300];
+    struct tm* btime;
+    Event* event;
+    int eventListSize = eventList->size();
+    for(int i = 0; i < eventListSize; i++)
+    {
+      event = (*eventList)[i];
+
+      //btime = localtime((time_t*)&event->time);
+      time_t etime = event->time;
+      btime = localtime(&etime);
+#ifndef _MSC_VER
+      strftime(tempString2, 299, "%0H:%0M ", btime);
+#else
+      strftime(tempString2, 299, "%H:%M ", btime);
+#endif
+      SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
+      sl.addOption(tempString, (ULONG)event, (i==0));
+    }
+  }
+}
+
+void VVideoLiveTV::delData()
+{
+  if (eventList)
+  {
+    int eventListSize = eventList->size();
+    for(int i = 0; i < eventListSize; i++)
+    {
+      delete (*eventList)[i];
+    }
+    eventList->clear();
+    delete eventList;
+
+  }
+  sl.clear();
+}
+
+void VVideoLiveTV::doOK()
+{
+  if (osd.getVisible())
+  {
+    if (osdChannelIndex == currentChannelIndex)
+    {
+      osd.setVisible(false);
+      draw();
+      boxstack->update(this, osd.getRegion());
+    }
+    else
+    {
+      channelChange(INDEX, osdChannelIndex);
+      doNowNext();
+      draw();
+      boxstack->update(this, osd.getRegion());
+    }
+  }
+  else
+  {
+    osdChannelIndex = currentChannelIndex;
+    doNowNext();
+    osd.setVisible(true);
+    draw();
+    boxstack->update(this, osd.getRegion());
+    // set a timer for deletion of osd
+  }
+}
+
+void VVideoLiveTV::doLeft()
+{
+  if (osd.getVisible())
+  {
+    osdChannelIndex = downChannel(osdChannelIndex);
+  }
+  else
+  {
+    osdChannelIndex = currentChannelIndex;
+    osd.setVisible(true);
+  }    
+
+  doNowNext();
+  draw();
+  boxstack->update(this, osd.getRegion());     
+}
+
+void VVideoLiveTV::doRight()
+{
+  if (osd.getVisible())
+  {
+    osdChannelIndex = upChannel(osdChannelIndex);
+  }
+  else
+  {
+    osdChannelIndex = currentChannelIndex;
+    osd.setVisible(true);
+  }    
+
+  doNowNext();
+  draw();
+  boxstack->update(this, osd.getRegion());  
+}
+
+void VVideoLiveTV::doChanUp()
+{
+  channelChange(OFFSET, UP);
+  osdChannelIndex = currentChannelIndex;
+  doNowNext();
+  draw();
+  boxstack->update(this, osd.getRegion());     
+}
+
+void VVideoLiveTV::doChanDown()
+{
+  channelChange(OFFSET, DOWN);
+  osdChannelIndex = currentChannelIndex;
+  doNowNext();
+  draw();
+  boxstack->update(this, osd.getRegion());   
+}
+
+void VVideoLiveTV::doKey(int command)
+{
+  int i;
+  
+  for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
+  keyingInput[0] = command;
+  keying++;
+
+  char keyingString[numberWidth+1];
+  for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
+  keyingString[numberWidth] = '\0';
+
+  for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
+  
+  if (keying == numberWidth)
+  {
+    UINT newChannel = 0;
+    for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
+    
+    channelChange(NUMBER, newChannel);
+    osdChannelIndex = currentChannelIndex;
+    doNowNext();
+    draw();
+    boxstack->update(this, osd.getRegion());
+  }
+  else
+  {
+    osdChanNum.setText(keyingString);
+    osdChanNum.draw();  
+    boxstack->update(this, osd.getRegion());
+  }
+}
+
+bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
+{
+  UINT newChannel = 0;
+
+  if (changeType == INDEX)
+  {
+    newChannel = newData;
+  }
+  else if (changeType == NUMBER)
+  {
+    UINT i;
+    for(i = 0; i < chanList->size(); i++)
+    {
+      if ((*chanList)[i]->number == (UINT)newData)
+      {
+        newChannel = i;
+        break;
+      }
+    }
+
+    if (i == chanList->size())
+    {
+      // no such channel
+      return false;
+    }
+  }
+  else if (changeType == OFFSET)
+  {
+    if (newData == UP) newChannel = upChannel(currentChannelIndex);
+    else newChannel = downChannel(currentChannelIndex);
+  }
+  else if (changeType == PREVIOUS)
+  {
+    newChannel = previousChannelIndex;
+  }
+  else
+  {
+    return false; // bad input
+  }
+
+  if (newChannel == currentChannelIndex) return true;
+
+  previousChannelIndex = currentChannelIndex;
+  currentChannelIndex = newChannel;
+  
+  Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
+
+  return true;
+//  player->setChannel(currentChannelIndex);
+}
+
+void VVideoLiveTV::processMessage(Message* m)
+{
+  if (m->message == Message::MOUSE_LBDOWN)
+  {
+    BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
+  }
+  else if (m->message == Message::CHANNEL_CHANGE)
+  {
+    channelChange(NUMBER, m->parameter);
+  }
+  else if (m->message == Message::EPG_CLOSE)
+  {
+    video->setMode(videoMode);
+  }
+  else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
+  {
+    Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
+//    ((Player*)player)->setAudioChannel(m->parameter);
+  }
+  else if (m->message == Message::PLAYER_EVENT)
+  {
+/*
+    switch(m->parameter)
+    {
+      case Player::CONNECTION_LOST: // connection lost detected
+      {
+        Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
+        // I can't handle this, send it to command
+        Message* m2 = new Message();
+        m2->to = Command::getInstance();
+        m2->message = Message::CONNECTION_LOST;
+        Command::getInstance()->postMessageNoLock(m2);
+        break;
+      }
+      case Player::STREAM_END:
+      {
+        // I can't handle this, send it to command - improve this
+        Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
+        m2->to = Command::getInstance();
+        m2->message = Message::STREAM_END;
+        Command::getInstance()->postMessageNoLock(m2);
+        break;
+      }
+      case Player::ASPECT43:
+      {
+        if (dowss)
+        {
+          Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
+          wss.setWide(false);
+          wss.draw();
+          BoxStack::getInstance()->update(this, &wssRegion);
+        }
+        break;
+      }
+      case Player::ASPECT169:
+      {
+        if (dowss)
+        {
+          Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
+          wss.setWide(true);
+          wss.draw();
+          BoxStack::getInstance()->update(this, &wssRegion);
+        }
+        break;
+      }
+    }
+    */
+  }
+}
+
+UINT VVideoLiveTV::upChannel(UINT index)
+{
+  if (index == (chanList->size() - 1)) // at the end
+    return 0; // so go to start
+  else
+    return index + 1;
+}
+
+UINT VVideoLiveTV::downChannel(UINT index)
+{
+  if (index == 0) // at the start
+    return chanList->size() - 1; // so go to end
+  else
+    return index - 1;
+}
+
+void VVideoLiveTV::toggleChopSides()
+{
+  if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
+
+  if (videoMode == Video::NORMAL)
+  {
+    videoMode = Video::LETTERBOX;
+    video->setMode(Video::LETTERBOX);
+  }
+  else
+  {
+    videoMode = Video::NORMAL;
+    video->setMode(Video::NORMAL);
+  }
+}
+
diff --git a/vvideolivetv.h b/vvideolivetv.h
new file mode 100644
index 0000000..26a2a79
--- /dev/null
+++ b/vvideolivetv.h
@@ -0,0 +1,114 @@
+/*
+    Copyright 2007 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP 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.
+
+    VOMP 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 VOMP; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef VVIDEOLIVETV_H
+#define VVIDEOLIVETV_H
+
+#include <stdio.h>
+#include <vector>
+#include <math.h>
+
+#include "boxx.h"
+#include "region.h"
+#include "wwss.h"
+#include "vdr.h"
+#include "wtextbox.h"
+#include "wselectlist.h"
+
+class VEpg;
+class VChannelList;
+class Video;
+class VChannelList;
+class BoxStack;
+class WTextbox;
+//class PlayerLiveTV;
+
+class VVideoLiveTV : public Boxx
+{
+  public:
+    VVideoLiveTV(ChannelList* chanList, ULONG initialChannelNumber, VChannelList* vchannelList);
+    ~VVideoLiveTV();
+    int handleCommand(int command);
+    void processMessage(Message* m);
+
+    void go();
+
+    bool channelChange(UCHAR changeType, UINT newData);
+    // changeType = INDEX = (newData is a channel index in the list)
+    //            = NUMBER = (newData is a real channel number)
+    //            = OFFSET = (newData is UP or DOWN)
+
+    const static UCHAR INDEX = 1;
+    const static UCHAR NUMBER = 2;
+    const static UCHAR OFFSET = 3;
+    const static UCHAR PREVIOUS = 4;
+    const static UCHAR UP = 1;
+    const static UCHAR DOWN = 2;
+
+  private:
+    BoxStack* boxstack;
+    VDR* vdr;
+    Video* video;
+//    PlayerLiveTV* player;
+    ChannelList* chanList;
+    VChannelList* vchannelList;
+    EventList* eventList;
+    int numberWidth;
+    int videoMode;
+
+    UINT currentChannelIndex;
+    UINT previousChannelIndex;
+    UINT osdChannelIndex;
+    int keying;
+    int keyingInput[10];
+
+    void stop();
+    UINT upChannel(UINT index);
+    UINT downChannel(UINT index);
+    void toggleChopSides();
+    void delData();
+    void doNowNext();
+    void doOK();
+    void doLeft();
+    void doRight();
+    void doChanUp();
+    void doChanDown();
+    void doKey(int command);
+
+    Wwss wss;
+    Region wssRegion;
+    bool dowss;
+    
+    Boxx osd;
+    WTextbox clock;
+    WTextbox osdChanNum;
+    WTextbox osdChanName;
+    WSelectList sl;
+    Boxx boxRed;
+    Boxx boxGreen;
+    Boxx boxYellow;
+    Boxx boxBlue;
+    WTextbox textRed;
+    WTextbox textGreen;
+    WTextbox textYellow;
+    WTextbox textBlue;
+};
+
+#endif
-- 
2.39.5