diff -ruN vdr-1.4.7-darwin/Makefile vdr-1.4.7/Makefile
--- vdr-1.4.7-darwin/Makefile	2007-03-26 19:34:41.000000000 +0300
+++ vdr-1.4.7/Makefile	2007-03-26 19:39:31.000000000 +0300
@@ -40,6 +40,8 @@
        skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
        timers.o tools.o transfer.o vdr.o videodir.o
 
+OBJS += osdcontroller.o rcontroller.o dvbsub.o vdrttxtsubshooks.o
+
 FIXFONT_ISO8859_1 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
 OSDFONT_ISO8859_1 = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
 SMLFONT_ISO8859_1 = -adobe-helvetica-medium-r-normal--18-*-100-100-p-*-iso8859-1
diff -ruN vdr-1.4.7-darwin/dvbplayer.c vdr-1.4.7/dvbplayer.c
--- vdr-1.4.7-darwin/dvbplayer.c	2006-04-17 15:45:48.000000000 +0300
+++ vdr-1.4.7/dvbplayer.c	2007-03-26 19:39:32.000000000 +0300
@@ -14,6 +14,9 @@
 #include "ringbuffer.h"
 #include "thread.h"
 #include "tools.h"
+#include "rcontroller.h"
+#include "dvbsub.h"
+#include "vdrttxtsubshooks.h"
 
 // --- cBackTrace ------------------------------------------------------------
 
@@ -312,6 +315,39 @@
   firstPacket = true;
 }
 
+static void StripExtendedPackets(uchar *b, int Length)
+{
+  for (int i = 0; i < Length - 6; i++) {
+      if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
+         uchar c = b[i + 3];
+         int l = b[i + 4] * 256 + b[i + 5] + 6;
+         switch (c) {
+           case 0xBD: // dolby
+                if (RecordingPatch::RecordingController.isExtendedPacket(b + i , l)) {
+                   RecordingPatch::RecordingController.Receive( b + i, l );
+                   // continue with deleting the data - otherwise it disturbs DVB replay
+                   int n = l;
+                   for (int j = i; j < Length && n--; j++)
+                       b[j] = 0x00;
+                   }
+                // EBU Teletext data, ETSI EN 300 472
+                else if (b[i + 8] == 0x24 && b[i + 45] >= 0x10 && b[i + 45] < 0x20) {
+                   cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData(&b[i], l);
+                   // continue with deleting the data - otherwise it disturbs DVB replay
+                   int n = l;
+                   for (int j = i; j < Length && n--; j++)
+                       b[j] = 0x00;
+                   }
+                break;
+           default:
+                break;
+           }
+         if (l)
+            i += l - 1; // the loop increments, too!
+         }
+      }
+}
+
 bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
 {
   if (FileNumber > 0)
@@ -497,6 +533,7 @@
                     }
                  }
               if (p) {
+                 StripExtendedPackets(p, pc);
                  int w = PlayPes(p, pc, playMode != pmPlay);
                  if (w > 0) {
                     p += w;
diff -ruN vdr-1.4.7-darwin/dvbsub.c vdr-1.4.7/dvbsub.c
--- vdr-1.4.7-darwin/dvbsub.c	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/dvbsub.c	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,18 @@
+#include "dvbsub.h"
+
+cDvbSubtitlesRecording DvbSubtitlesRecording;
+
+cDvbSubtitlesRecording::cDvbSubtitlesRecording(){ 
+  query=0; 
+}
+void cDvbSubtitlesRecording::Subscribe(iPidQuery* listener){ 
+  query = listener ;
+}
+
+int cDvbSubtitlesRecording::GetPidByChannel( int DevNr, const cChannel* Channel, int Language )
+{ 
+  if (query) 
+    return query->GetPidByChannel( DevNr, Channel,Language );
+  else
+    return 0;
+}
diff -ruN vdr-1.4.7-darwin/dvbsub.h vdr-1.4.7/dvbsub.h
--- vdr-1.4.7-darwin/dvbsub.h	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/dvbsub.h	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,22 @@
+class cChannel;
+class cRecordControl;
+
+class iPidQuery
+{
+public:
+  virtual ~iPidQuery() {};
+  virtual int GetPidByChannel( int DevNr, const cChannel* Channel, int Language ) = 0;
+};
+class cDvbSubtitlesRecording
+{
+public:
+  cDvbSubtitlesRecording();
+  void Subscribe(iPidQuery* listener);
+private:
+  friend class cRecordControl;
+  int GetPidByChannel( int DevNr, const cChannel* Channel, int Language );
+  iPidQuery* query;
+};
+
+extern cDvbSubtitlesRecording DvbSubtitlesRecording;
+
diff -ruN vdr-1.4.7-darwin/menu.c vdr-1.4.7/menu.c
--- vdr-1.4.7-darwin/menu.c	2007-03-26 19:34:42.000000000 +0300
+++ vdr-1.4.7/menu.c	2007-03-26 19:39:32.000000000 +0300
@@ -27,6 +27,8 @@
 #include "themes.h"
 #include "timers.h"
 #include "transfer.h"
+#include "vdrttxtsubshooks.h"
+#include "dvbsub.h"
 #include "videodir.h"
 #ifdef __APPLE__
 #include "darwinutils.h"
@@ -3548,8 +3550,11 @@
   isyslog("record %s", fileName);
   if (MakeDirs(fileName, true)) {
      const cChannel *ch = timer->Channel();
-     recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
+     cTtxtSubsRecorderBase *subsRecorder = cVDRTtxtsubsHookListener::Hook()->NewTtxtSubsRecorder(device, ch);
+     int SubPids[3] = {DvbSubtitlesRecording.GetPidByChannel(device->DeviceNumber(), ch, 1), DvbSubtitlesRecording.GetPidByChannel(device->DeviceNumber(), ch, 2), 0};
+     recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), SubPids, subsRecorder);
      if (device->AttachReceiver(recorder)) {
+        if (subsRecorder) subsRecorder->DeviceAttach();
         Recording.WriteInfo();
         cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
         if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
diff -ruN vdr-1.4.7-darwin/osd.c vdr-1.4.7/osd.c
--- vdr-1.4.7-darwin/osd.c	2007-02-17 18:05:52.000000000 +0200
+++ vdr-1.4.7/osd.c	2007-03-26 19:39:32.000000000 +0300
@@ -14,6 +14,8 @@
 #include <sys/stat.h>
 #include <sys/unistd.h>
 #include "tools.h"
+#include "osdcontroller.h"
+#include "vdrttxtsubshooks.h"
 
 // --- cPalette --------------------------------------------------------------
 
@@ -602,6 +604,7 @@
 // --- cOsd ------------------------------------------------------------------
 
 int cOsd::isOpen = 0;
+bool cOsd::niosd  = false;
 
 cOsd::cOsd(int Left, int Top)
 {
@@ -621,6 +624,9 @@
       delete bitmaps[i];
   delete savedRegion;
   isOpen--;
+  if (!niosd)
+     NonInteractiveOsdPatch::OsdController.Show();
+  cVDRTtxtsubsHookListener::Hook()->ShowOSD();
 }
 
 cBitmap *cOsd::GetBitmap(int Area)
@@ -744,8 +750,13 @@
   osdProvider = NULL;
 }
 
-cOsd *cOsdProvider::NewOsd(int Left, int Top)
+cOsd *cOsdProvider::NewOsd(int Left, int Top, bool dontHide)
 {
+  if (!dontHide)
+     NonInteractiveOsdPatch::OsdController.Hide();
+  cOsd::niosd = dontHide;
+  // OSD_HOOK_2 - Information to Checkpatch.sh
+  cVDRTtxtsubsHookListener::Hook()->HideOSD();
   if (cOsd::IsOpen())
      esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
   else if (osdProvider)
diff -ruN vdr-1.4.7-darwin/osd.c.orig vdr-1.4.7/osd.c.orig
--- vdr-1.4.7-darwin/osd.c.orig	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/osd.c.orig	2007-02-17 18:05:52.000000000 +0200
@@ -0,0 +1,830 @@
+/*
+ * osd.c: Abstract On Screen Display layer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osd.c 1.68 2007/02/17 16:05:52 kls Exp $
+ */
+
+#include "osd.h"
+#include <math.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include "tools.h"
+
+// --- cPalette --------------------------------------------------------------
+
+cPalette::cPalette(int Bpp)
+{
+  SetBpp(Bpp);
+}
+
+void cPalette::Reset(void)
+{
+  numColors = 0;
+  modified = false;
+}
+
+int cPalette::Index(tColor Color)
+{
+  for (int i = 0; i < numColors; i++) {
+      if (color[i] == Color)
+         return i;
+      }
+  if (numColors < maxColors) {
+     color[numColors++] = Color;
+     modified = true;
+     return numColors - 1;
+     }
+  dsyslog("too many different colors used in palette");
+  //TODO: return the index of the "closest" color?
+  return 0;
+}
+
+void cPalette::SetBpp(int Bpp)
+{
+  bpp = Bpp;
+  maxColors = 1 << bpp;
+  Reset();
+}
+
+void cPalette::SetColor(int Index, tColor Color)
+{
+  if (Index < maxColors) {
+     if (numColors <= Index) {
+        numColors = Index + 1;
+        modified = true;
+        }
+     else
+        modified |= color[Index] != Color;
+     color[Index] = Color;
+     }
+}
+
+const tColor *cPalette::Colors(int &NumColors)
+{
+  NumColors = numColors;
+  return numColors ? color : NULL;
+}
+
+void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg)
+{
+  for (int i = 0; i < Palette.numColors; i++) {
+      tColor Color = Palette.color[i];
+      if (ColorFg || ColorBg) {
+         switch (i) {
+           case 0: Color = ColorBg; break;
+           case 1: Color = ColorFg; break;
+           }
+         }
+      int n = Index(Color);
+      if (Indexes)
+         (*Indexes)[i] = n;
+      }
+}
+
+void cPalette::Replace(const cPalette &Palette)
+{
+  for (int i = 0; i < Palette.numColors; i++)
+      SetColor(i, Palette.color[i]);
+  numColors = Palette.numColors;
+}
+
+// --- cBitmap ---------------------------------------------------------------
+
+cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0)
+:cPalette(Bpp)
+{
+  bitmap = NULL;
+  x0 = X0;
+  y0 = Y0;
+  SetSize(Width, Height);
+}
+
+cBitmap::cBitmap(const char *FileName)
+{
+  bitmap = NULL;
+  x0 = 0;
+  y0 = 0;
+  LoadXpm(FileName);
+}
+
+cBitmap::cBitmap(const char *const Xpm[])
+{
+  bitmap = NULL;
+  x0 = 0;
+  y0 = 0;
+  SetXpm(Xpm);
+}
+
+cBitmap::~cBitmap()
+{
+  free(bitmap);
+}
+
+void cBitmap::SetSize(int Width, int Height)
+{
+  if (bitmap && Width == width && Height == height)
+     return;
+  width = Width;
+  height = Height;
+  free(bitmap);
+  bitmap = NULL;
+  dirtyX1 = 0;
+  dirtyY1 = 0;
+  dirtyX2 = width - 1;
+  dirtyY2 = height - 1;
+  if (width > 0 && height > 0) {
+     bitmap = MALLOC(tIndex, width * height);
+     if (bitmap)
+        memset(bitmap, 0x00, width * height);
+     else
+        esyslog("ERROR: can't allocate bitmap!");
+     }
+  else
+     esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height);
+}
+
+bool cBitmap::Contains(int x, int y) const
+{
+  x -= x0;
+  y -= y0;
+  return 0 <= x && x < width && 0 <= y && y < height;
+}
+
+bool cBitmap::Covers(int x1, int y1, int x2, int y2) const
+{
+  x1 -= x0;
+  y1 -= y0;
+  x2 -= x0;
+  y2 -= y0;
+  return x1 <= 0 && y1 <= 0 && x2 >= width - 1 && y2 >= height - 1;
+}
+
+bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const
+{
+  x1 -= x0;
+  y1 -= y0;
+  x2 -= x0;
+  y2 -= y0;
+  return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height);
+}
+
+bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2)
+{
+  if (dirtyX2 >= 0) {
+     x1 = dirtyX1;
+     y1 = dirtyY1;
+     x2 = dirtyX2;
+     y2 = dirtyY2;
+     return true;
+     }
+  return false;
+}
+
+void cBitmap::Clean(void)
+{
+  dirtyX1 = width;
+  dirtyY1 = height;
+  dirtyX2 = -1;
+  dirtyY2 = -1;
+}
+
+bool cBitmap::LoadXpm(const char *FileName)
+{
+  bool Result = false;
+  FILE *f = fopen(FileName, "r");
+  if (f) {
+     char **Xpm = NULL;
+     bool isXpm = false;
+     int lines = 0;
+     int index = 0;
+     char *s;
+     cReadLine ReadLine;
+     while ((s = ReadLine.Read(f)) != NULL) {
+           s = skipspace(s);
+           if (!isXpm) {
+              if (strcmp(s, "/* XPM */") != 0) {
+                 esyslog("ERROR: invalid header in XPM file '%s'", FileName);
+                 break;
+                 }
+              isXpm = true;
+              }
+           else if (*s++ == '"') {
+              if (!lines) {
+                 int w, h, n, c;
+                 if (4 != sscanf(s, "%d %d %d %d", &w, &h, &n, &c)) {
+                    esyslog("ERROR: faulty 'values' line in XPM file '%s'", FileName);
+                    isXpm = false;
+                    break;
+                    }
+                 lines = h + n + 1;
+                 Xpm = MALLOC(char *, lines);
+                 memset(Xpm, 0, lines * sizeof(char*));
+                 }
+              char *q = strchr(s, '"');
+              if (!q) {
+                 esyslog("ERROR: missing quotes in XPM file '%s'", FileName);
+                 isXpm = false;
+                 break;
+                 }
+              *q = 0;
+              if (index < lines)
+                 Xpm[index++] = strdup(s);
+              else {
+                 esyslog("ERROR: too many lines in XPM file '%s'", FileName);
+                 isXpm = false;
+                 break;
+                 }
+              }
+           }
+     if (isXpm) {
+        if (index == lines)
+           Result = SetXpm(Xpm);
+        else
+           esyslog("ERROR: too few lines in XPM file '%s'", FileName);
+        }
+     if (Xpm) {
+        for (int i = 0; i < index; i++)
+            free(Xpm[i]);
+        }
+     free(Xpm);
+     fclose(f);
+     }
+  else
+     esyslog("ERROR: can't open XPM file '%s'", FileName);
+  return Result;
+}
+
+bool cBitmap::SetXpm(const char *const Xpm[], bool IgnoreNone)
+{
+  const char *const *p = Xpm;
+  int w, h, n, c;
+  if (4 != sscanf(*p, "%d %d %d %d", &w, &h, &n, &c)) {
+     esyslog("ERROR: faulty 'values' line in XPM: '%s'", *p);
+     return false;
+     }
+  if (n > MAXNUMCOLORS) {
+     esyslog("ERROR: too many colors in XPM: %d", n);
+     return false;
+     }
+  int b = 0;
+  while (1 << (1 << b) < (IgnoreNone ? n - 1 : n))
+        b++;
+  SetBpp(1 << b);
+  SetSize(w, h);
+  int NoneColorIndex = MAXNUMCOLORS;
+  for (int i = 0; i < n; i++) {
+      const char *s = *++p;
+      if (int(strlen(s)) < c) {
+         esyslog("ERROR: faulty 'colors' line in XPM: '%s'", s);
+         return false;
+         }
+      s = skipspace(s + c);
+      if (*s != 'c') {
+         esyslog("ERROR: unknown color key in XPM: '%c'", *s);
+         return false;
+         }
+      s = skipspace(s + 1);
+      if (strcasecmp(s, "none") == 0) {
+         s = "#00000000";
+         NoneColorIndex = i;
+         if (IgnoreNone)
+            continue;
+         }
+      if (*s != '#') {
+         esyslog("ERROR: unknown color code in XPM: '%c'", *s);
+         return false;
+         }
+      tColor color = strtoul(++s, NULL, 16) | 0xFF000000;
+      SetColor((IgnoreNone && i > NoneColorIndex) ? i - 1 : i, color);
+      }
+  for (int y = 0; y < h; y++) {
+      const char *s = *++p;
+      if (int(strlen(s)) != w * c) {
+         esyslog("ERROR: faulty pixel line in XPM: %d '%s'", y, s);
+         return false;
+         }
+      for (int x = 0; x < w; x++) {
+          for (int i = 0; i <= n; i++) {
+              if (i == n) {
+                 esyslog("ERROR: undefined pixel color in XPM: %d %d '%s'", x, y, s);
+                 return false;
+                 }
+              if (strncmp(Xpm[i + 1], s, c) == 0) {
+                 if (i == NoneColorIndex)
+                    NoneColorIndex = MAXNUMCOLORS;
+                 SetIndex(x, y, (IgnoreNone && i > NoneColorIndex) ? i - 1 : i);
+                 break;
+                 }
+              }
+          s += c;
+          }
+      }
+  if (NoneColorIndex < MAXNUMCOLORS && !IgnoreNone)
+     return SetXpm(Xpm, true);
+  return true;
+}
+
+void cBitmap::SetIndex(int x, int y, tIndex Index)
+{
+  if (bitmap) {
+     if (0 <= x && x < width && 0 <= y && y < height) {
+        if (bitmap[width * y + x] != Index) {
+           bitmap[width * y + x] = Index;
+           if (dirtyX1 > x)  dirtyX1 = x;
+           if (dirtyY1 > y)  dirtyY1 = y;
+           if (dirtyX2 < x)  dirtyX2 = x;
+           if (dirtyY2 < y)  dirtyY2 = y;
+           }
+        }
+     }
+}
+
+void cBitmap::DrawPixel(int x, int y, tColor Color)
+{
+  x -= x0;
+  y -= y0;
+  if (0 <= x && x < width && 0 <= y && y < height)
+     SetIndex(x, y, Index(Color));
+}
+
+void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
+{
+  if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
+     if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
+        Reset();
+     x -= x0;
+     y -= y0;
+     if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
+        Replace(Bitmap);
+        for (int ix = 0; ix < Bitmap.width; ix++) {
+            for (int iy = 0; iy < Bitmap.height; iy++) {
+                if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
+                   SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
+                }
+            }
+        }
+     else {
+        tIndexes Indexes;
+        Take(Bitmap, &Indexes, ColorFg, ColorBg);
+        for (int ix = 0; ix < Bitmap.width; ix++) {
+            for (int iy = 0; iy < Bitmap.height; iy++) {
+                if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
+                   SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
+                }
+            }
+        }
+     }
+}
+
+void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
+{
+  if (bitmap) {
+     int w = Font->Width(s);
+     int h = Font->Height();
+     int limit = 0;
+     if (Width || Height) {
+        int cw = Width ? Width : w;
+        int ch = Height ? Height : h;
+        if (!Intersects(x, y, x + cw - 1, y + ch - 1))
+           return;
+        if (ColorBg != clrTransparent)
+           DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
+        limit = x + cw - x0;
+        if (Width) {
+           if ((Alignment & taLeft) != 0)
+              ;
+           else if ((Alignment & taRight) != 0) {
+              if (w < Width)
+                 x += Width - w;
+              }
+           else { // taCentered
+              if (w < Width)
+                 x += (Width - w) / 2;
+              }
+           }
+        if (Height) {
+           if ((Alignment & taTop) != 0)
+              ;
+           else if ((Alignment & taBottom) != 0) {
+              if (h < Height)
+                 y += Height - h;
+              }
+           else { // taCentered
+              if (h < Height)
+                 y += (Height - h) / 2;
+              }
+           }
+        }
+     else if (!Intersects(x, y, x + w - 1, y + h - 1))
+        return;
+     x -= x0;
+     y -= y0;
+     tIndex fg = Index(ColorFg);
+     tIndex bg = (ColorBg != clrTransparent) ? Index(ColorBg) : 0;
+     while (s && *s) {
+           const cFont::tCharData *CharData = Font->CharData(*s++);
+           if (limit && int(x + CharData->width) > limit)
+              break; // we don't draw partial characters
+           if (int(x + CharData->width) > 0) {
+              for (int row = 0; row < h; row++) {
+                  cFont::tPixelData PixelData = CharData->lines[row];
+                  for (int col = CharData->width; col-- > 0; ) {
+                      if (ColorBg != clrTransparent || (PixelData & 1))
+                         SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg);
+                      PixelData >>= 1;
+                      }
+                  }
+              }
+           x += CharData->width;
+           if (x > width - 1)
+              break;
+           }
+     }
+}
+
+void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
+{
+  if (bitmap && Intersects(x1, y1, x2, y2)) {
+     if (Covers(x1, y1, x2, y2))
+        Reset();
+     x1 -= x0;
+     y1 -= y0;
+     x2 -= x0;
+     y2 -= y0;
+     x1 = max(x1, 0);
+     y1 = max(y1, 0);
+     x2 = min(x2, width - 1);
+     y2 = min(y2, height - 1);
+     tIndex c = Index(Color);
+     for (int y = y1; y <= y2; y++)
+         for (int x = x1; x <= x2; x++)
+             SetIndex(x, y, c);
+     }
+}
+
+void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
+{
+  if (!Intersects(x1, y1, x2, y2))
+     return;
+  // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
+  int rx = x2 - x1;
+  int ry = y2 - y1;
+  int cx = (x1 + x2) / 2;
+  int cy = (y1 + y2) / 2;
+  switch (abs(Quadrants)) {
+    case 0: rx /= 2; ry /= 2; break;
+    case 1: cx = x1; cy = y2; break;
+    case 2: cx = x2; cy = y2; break;
+    case 3: cx = x2; cy = y1; break;
+    case 4: cx = x1; cy = y1; break;
+    case 5: cx = x1;          ry /= 2; break;
+    case 6:          cy = y2; rx /= 2; break;
+    case 7: cx = x2;          ry /= 2; break;
+    case 8:          cy = y1; rx /= 2; break;
+    }
+  int TwoASquare = 2 * rx * rx;
+  int TwoBSquare = 2 * ry * ry;
+  int x = rx;
+  int y = 0;
+  int XChange = ry * ry * (1 - 2 * rx);
+  int YChange = rx * rx;
+  int EllipseError = 0;
+  int StoppingX = TwoBSquare * rx;
+  int StoppingY = 0;
+  while (StoppingX >= StoppingY) {
+        switch (Quadrants) {
+          case  5: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); // no break
+          case  1: DrawRectangle(cx,     cy - y, cx + x, cy - y, Color); break;
+          case  7: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); // no break
+          case  2: DrawRectangle(cx - x, cy - y, cx,     cy - y, Color); break;
+          case  3: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); break;
+          case  4: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); break;
+          case  0:
+          case  6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
+          case  8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
+          case -1: DrawRectangle(cx + x, cy - y, x2,     cy - y, Color); break;
+          case -2: DrawRectangle(x1,     cy - y, cx - x, cy - y, Color); break;
+          case -3: DrawRectangle(x1,     cy + y, cx - x, cy + y, Color); break;
+          case -4: DrawRectangle(cx + x, cy + y, x2,     cy + y, Color); break;
+          }
+        y++;
+        StoppingY += TwoASquare;
+        EllipseError += YChange;
+        YChange += TwoASquare;
+        if (2 * EllipseError + XChange > 0) {
+           x--;
+           StoppingX -= TwoBSquare;
+           EllipseError += XChange;
+           XChange += TwoBSquare;
+           }
+        }
+  x = 0;
+  y = ry;
+  XChange = ry * ry;
+  YChange = rx * rx * (1 - 2 * ry);
+  EllipseError = 0;
+  StoppingX = 0;
+  StoppingY = TwoASquare * ry;
+  while (StoppingX <= StoppingY) {
+        switch (Quadrants) {
+          case  5: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); // no break
+          case  1: DrawRectangle(cx,     cy - y, cx + x, cy - y, Color); break;
+          case  7: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); // no break
+          case  2: DrawRectangle(cx - x, cy - y, cx,     cy - y, Color); break;
+          case  3: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); break;
+          case  4: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); break;
+          case  0:
+          case  6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
+          case  8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
+          case -1: DrawRectangle(cx + x, cy - y, x2,     cy - y, Color); break;
+          case -2: DrawRectangle(x1,     cy - y, cx - x, cy - y, Color); break;
+          case -3: DrawRectangle(x1,     cy + y, cx - x, cy + y, Color); break;
+          case -4: DrawRectangle(cx + x, cy + y, x2,     cy + y, Color); break;
+          }
+        x++;
+        StoppingX += TwoBSquare;
+        EllipseError += XChange;
+        XChange += TwoBSquare;
+        if (2 * EllipseError + YChange > 0) {
+           y--;
+           StoppingY -= TwoASquare;
+           EllipseError += YChange;
+           YChange += TwoASquare;
+           }
+        }
+}
+
+void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
+{
+  // TODO This is just a quick and dirty implementation of a slope drawing
+  // machanism. If somebody can come up with a better solution, let's have it!
+  if (!Intersects(x1, y1, x2, y2))
+     return;
+  bool upper    = Type & 0x01;
+  bool falling  = Type & 0x02;
+  bool vertical = Type & 0x04;
+  if (vertical) {
+     for (int y = y1; y <= y2; y++) {
+         double c = cos((y - y1) * M_PI / (y2 - y1 + 1));
+         if (falling)
+            c = -c;
+         int x = int((x2 - x1 + 1) * c / 2);
+         if (upper && !falling || !upper && falling)
+            DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color);
+         else
+            DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color);
+         }
+     }
+  else {
+     for (int x = x1; x <= x2; x++) {
+         double c = cos((x - x1) * M_PI / (x2 - x1 + 1));
+         if (falling)
+            c = -c;
+         int y = int((y2 - y1 + 1) * c / 2);
+         if (upper)
+            DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color);
+         else
+            DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color);
+         }
+     }
+}
+
+const tIndex *cBitmap::Data(int x, int y)
+{
+  return &bitmap[y * width + x];
+}
+
+// --- cOsd ------------------------------------------------------------------
+
+int cOsd::isOpen = 0;
+
+cOsd::cOsd(int Left, int Top)
+{
+  if (isOpen)
+     esyslog("ERROR: OSD opened without closing previous OSD!");
+  savedRegion = NULL;
+  numBitmaps = 0;
+  left = Left;
+  top = Top;
+  width = height = 0;
+  isOpen++;
+}
+
+cOsd::~cOsd()
+{
+  for (int i = 0; i < numBitmaps; i++)
+      delete bitmaps[i];
+  delete savedRegion;
+  isOpen--;
+}
+
+cBitmap *cOsd::GetBitmap(int Area)
+{
+  return Area < numBitmaps ? bitmaps[Area] : NULL;
+}
+
+eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
+{
+  if (NumAreas > MAXOSDAREAS)
+     return oeTooManyAreas;
+  eOsdError Result = oeOk;
+  for (int i = 0; i < NumAreas; i++) {
+      if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0)
+         return oeWrongAlignment;
+      for (int j = i + 1; j < NumAreas; j++) {
+          if (Areas[i].Intersects(Areas[j])) {
+             Result = oeAreasOverlap;
+             break;
+             }
+          }
+      }
+  return Result;
+}
+
+eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
+{
+  eOsdError Result = oeUnknown;
+  if (numBitmaps == 0) {
+     Result = CanHandleAreas(Areas, NumAreas);
+     if (Result == oeOk) {
+        width = height = 0;
+        for (int i = 0; i < NumAreas; i++) {
+            bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
+            width = max(width, Areas[i].x2 + 1);
+            height = max(height, Areas[i].y2 + 1);
+            }
+        }
+     }
+  if (Result != oeOk)
+     esyslog("ERROR: cOsd::SetAreas returned %d", Result);
+  return Result;
+}
+
+void cOsd::SaveRegion(int x1, int y1, int x2, int y2)
+{
+  delete savedRegion;
+  savedRegion = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
+  for (int i = 0; i < numBitmaps; i++)
+      savedRegion->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
+}
+
+void cOsd::RestoreRegion(void)
+{
+  if (savedRegion) {
+     DrawBitmap(savedRegion->X0(), savedRegion->Y0(), *savedRegion);
+     delete savedRegion;
+     savedRegion = NULL;
+     }
+}
+
+eOsdError cOsd::SetPalette(const cPalette &Palette, int Area)
+{
+  if (Area < numBitmaps)
+     bitmaps[Area]->Take(Palette);
+  return oeUnknown;
+}
+
+void cOsd::DrawPixel(int x, int y, tColor Color)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawPixel(x, y, Color);
+}
+
+void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
+}
+
+void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
+}
+
+void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
+}
+
+void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
+}
+
+void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
+{
+  for (int i = 0; i < numBitmaps; i++)
+      bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
+}
+
+void cOsd::Flush(void)
+{
+}
+
+// --- cOsdProvider ----------------------------------------------------------
+
+cOsdProvider *cOsdProvider::osdProvider = NULL;
+
+cOsdProvider::cOsdProvider(void)
+{
+  delete osdProvider;
+  osdProvider = this;
+}
+
+cOsdProvider::~cOsdProvider()
+{
+  osdProvider = NULL;
+}
+
+cOsd *cOsdProvider::NewOsd(int Left, int Top)
+{
+  if (cOsd::IsOpen())
+     esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
+  else if (osdProvider)
+     return osdProvider->CreateOsd(Left, Top);
+  else
+     esyslog("ERROR: no OSD provider available - using dummy OSD!");
+  return new cOsd(Left, Top); // create a dummy cOsd, so that access won't result in a segfault
+}
+
+void cOsdProvider::Shutdown(void)
+{
+  delete osdProvider;
+  osdProvider = NULL;
+}
+
+// --- cTextScroller ---------------------------------------------------------
+
+cTextScroller::cTextScroller(void)
+{
+  osd = NULL;
+  left = top = width = height = 0;
+  font = NULL;
+  colorFg = 0;
+  colorBg = 0;
+  offset = 0;
+  shown = 0;
+}
+
+cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
+{
+  Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg);
+}
+
+void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
+{
+  osd = Osd;
+  left = Left;
+  top = Top;
+  width = Width;
+  height = Height;
+  font = Font;
+  colorFg = ColorFg;
+  colorBg = ColorBg;
+  offset = 0;
+  textWrapper.Set(Text, Font, Width);
+  shown = min(Total(), height / font->Height());
+  height = shown * font->Height(); // sets height to the actually used height, which may be less than Height
+  DrawText();
+}
+
+void cTextScroller::Reset(void)
+{
+  osd = NULL; // just makes sure it won't draw anything
+}
+
+void cTextScroller::DrawText(void)
+{
+  if (osd) {
+     for (int i = 0; i < shown; i++)
+         osd->DrawText(left, top + i * font->Height(), textWrapper.GetLine(offset + i), colorFg, colorBg, font, width);
+     }
+}
+
+void cTextScroller::Scroll(bool Up, bool Page)
+{
+  if (Up) {
+     if (CanScrollUp()) {
+        offset -= Page ? shown : 1;
+        if (offset < 0)
+           offset = 0;
+        DrawText();
+        }
+     }
+  else {
+     if (CanScrollDown()) {
+        offset += Page ? shown : 1;
+        if (offset + shown > Total())
+           offset = Total() - shown;
+        DrawText();
+        }
+     }
+}
diff -ruN vdr-1.4.7-darwin/osd.h vdr-1.4.7/osd.h
--- vdr-1.4.7-darwin/osd.h	2006-02-26 16:45:05.000000000 +0200
+++ vdr-1.4.7/osd.h	2007-03-26 19:39:32.000000000 +0300
@@ -222,6 +222,8 @@
   cBitmap *bitmaps[MAXOSDAREAS];
   int numBitmaps;
   int left, top, width, height;
+public:
+  static bool niosd;
 protected:
   cOsd(int Left, int Top);
        ///< Initializes the OSD with the given coordinates.
@@ -337,7 +339,7 @@
   cOsdProvider(void);
       //XXX maybe parameter to make this one "sticky"??? (frame-buffer etc.)
   virtual ~cOsdProvider();
-  static cOsd *NewOsd(int Left, int Top);
+  static cOsd *NewOsd(int Left, int Top, bool dontHide = false);
       ///< Returns a pointer to a newly created cOsd object, which will be located
       ///< at the given coordinates. When the cOsd object is no longer needed, the
       ///< caller must delete it. If the OSD is already in use, or there is no OSD
diff -ruN vdr-1.4.7-darwin/osdcontroller.c vdr-1.4.7/osdcontroller.c
--- vdr-1.4.7-darwin/osdcontroller.c	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/osdcontroller.c	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,132 @@
+#include "osdcontroller.h"
+#include "thread.h"
+
+namespace NonInteractiveOsdPatch
+{
+
+  class cListenerListObject : public cListObject
+  {
+  public:
+    cListenerListObject(int priority, cOsdListener* listener);
+    bool operator< (const cListObject &ListObject);
+
+    int iPriority;
+    cOsdListener* iListener;
+  };
+
+  cListenerListObject::cListenerListObject(int priority, cOsdListener* listener)
+    : cListObject(),iPriority(priority), iListener( listener )
+  {
+  }
+  bool cListenerListObject::operator< (const cListObject &ListObject)
+  {
+    
+    return iPriority > ((cListenerListObject *)&ListObject)->iPriority;
+  }
+
+  cOsdController OsdController;
+  
+  cOsdController::cOsdController()
+    : iShowing( false )
+  {
+    iMutex = new cMutex();
+    iListeners = new cList<cListenerListObject>;
+  }
+
+  cOsdController::~cOsdController()
+  {
+    delete iListeners;
+    delete iMutex;
+  }
+
+  bool cOsdController::Subscribe(  int priority, cOsdListener* listener )
+  {
+   
+    cMutexLock( iMutex );
+    if ( !listener )
+      return false;
+    
+    for ( cListenerListObject* llo = iListeners->First();
+	  llo; llo = iListeners->Next(llo))
+    {
+      // check for duplicates
+      if ( llo->iListener == listener )
+	return false;
+    }
+
+    cListenerListObject *lo = new cListenerListObject(priority, listener);
+    iListeners->Add( lo );
+    iListeners->Sort();
+
+    // Give osd to the new listener if it has higher priority
+    // than the current one
+    if ( iShowing && !iCurrent )
+    {
+      listener->Show();
+      iCurrent = lo;
+    }
+    else if ( iShowing && iCurrent && iCurrent->iPriority < priority )
+    {
+      iCurrent->iListener->Hide();
+      ShowHighest();
+
+    }
+    
+    return true;
+  }
+
+  void cOsdController::Unsubscribe( cOsdListener* listener )
+  {
+    cMutexLock( iMutex );
+    if ( !listener )
+      return;
+
+    for ( cListenerListObject* llo = iListeners->First();
+	  llo; llo = iListeners->Next(llo))
+    {
+
+      if ( llo->iListener == listener )
+      {
+	iListeners->Del( llo, true );
+	if ( iShowing && llo == iCurrent )
+	{
+	  listener->Hide();
+	  ShowHighest();
+	}
+	break;
+      }
+    }
+
+  } 
+
+  void cOsdController::Show()
+  {
+    cMutexLock( iMutex );
+    iShowing = true;
+    ShowHighest();
+
+  }
+  
+  void cOsdController::Hide()
+  {
+    cMutexLock( iMutex );
+    iShowing = false;
+    if ( iCurrent )
+      iCurrent->iListener->Hide();
+    iCurrent = NULL;
+  }
+
+  void cOsdController::ShowHighest()
+  {
+
+    cListenerListObject* llo = iListeners->First();
+
+    if ( llo )
+      llo->iListener->Show();
+
+    iCurrent = llo;
+
+  }
+
+
+}
diff -ruN vdr-1.4.7-darwin/osdcontroller.h vdr-1.4.7/osdcontroller.h
--- vdr-1.4.7-darwin/osdcontroller.h	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/osdcontroller.h	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,52 @@
+#ifndef __OSDCONTROLLER_H
+#define __OSDCONTROLLER_H
+
+#include "tools.h"
+
+
+#define MAX_OSD_LISTENERS 10
+
+class cOsdProvider;
+class cOsd;
+class cMutex;
+
+namespace NonInteractiveOsdPatch
+{
+
+  class cOsdListener
+  {
+    
+  public:
+    virtual ~cOsdListener() {};
+    virtual void Show() = 0;
+    virtual void Hide() = 0;
+
+  };
+    
+  class cListenerListObject;
+
+  class cOsdController
+  {
+  public:
+    cOsdController();
+    ~cOsdController();
+    bool Subscribe( int priority, cOsdListener* listener );
+    void Unsubscribe( cOsdListener* listner );
+
+  private:
+    friend class ::cOsdProvider;
+    friend class ::cOsd;
+    void Show();
+    void Hide();
+    void ShowHighest();
+
+    bool iShowing;
+    cMutex* iMutex;
+    cListenerListObject* iCurrent;
+    cList<cListenerListObject>* iListeners;
+
+  };
+  extern cOsdController OsdController;
+
+}
+#endif
diff -ruN vdr-1.4.7-darwin/rcontroller.c vdr-1.4.7/rcontroller.c
--- vdr-1.4.7-darwin/rcontroller.c	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/rcontroller.c	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,82 @@
+#include <stdlib.h>
+#include "rcontroller.h"
+#include <stdio.h>
+#define PRIVATE_STREAM_1 0xBD
+#define PES_EXTENSION_MASK 0x01
+#define PES_EXTENSION2_MASK 0x81
+namespace RecordingPatch {
+
+  cRecordingController RecordingController;
+
+  // Returns the Data Identifier or 0 if not enough data
+  unsigned char GetDataIdentifier( unsigned char* Data, int Length )
+  {
+    if ( Length < 9 )
+      return 0;
+    int hlength = Data[8];
+    if ( Length < 9 + hlength )
+      return 0;
+    return Data[ 9 + hlength - 1 ];
+  }
+
+  cRecordingController::cRecordingController()
+  {
+    listeners = (iRecordingPlugin**)malloc( sizeof(iRecordingPlugin*)*256 );
+    for (int i=0; i < 256; i++)
+      listeners[i] = 0;
+  }
+  
+  cRecordingController::~cRecordingController()
+  {
+    free (listeners);
+  }
+  void cRecordingController::Subscribe(unsigned char DataIdentifier, iRecordingPlugin* plugin)
+  {
+    if ( listeners[ DataIdentifier ] == 0 )
+      listeners[ DataIdentifier ] = plugin;
+  }
+  void cRecordingController::Unsubscribe(unsigned char DataIdentifier, iRecordingPlugin* plugin)
+  {
+    
+    if ( listeners[ DataIdentifier ] != 0 && listeners[ DataIdentifier ] == plugin )
+      listeners[ DataIdentifier ] = 0;
+    
+  }
+
+  bool cRecordingController::isExtendedPacket(unsigned char* Data, int Length)
+  {
+    if ( Length < 9 )
+      return false;
+    if ( Data[0] != 0x00 || Data[1] != 0x00 || Data[2] != 0x01 )
+      return false;
+    if ( Data[3] != PRIVATE_STREAM_1 )
+      return false;
+    
+    if ( !(Data[7] & PES_EXTENSION_MASK) )
+      return false;
+
+    int hlength = Data[8];
+    
+    if ( ( Data[ 9 + hlength - 3 ] & PES_EXTENSION2_MASK ) == 1 
+	 && Data[ 9 + hlength - 2 ] == 0x81)
+      return true;
+
+    return false;
+
+  }
+
+
+  void cRecordingController::Receive(unsigned char* Data, int Length)
+  {
+    if ( isExtendedPacket( Data, Length ) )
+    {
+      unsigned char DataIdentifier = GetDataIdentifier( Data, Length );
+      if ( listeners[ DataIdentifier ] != 0 )
+	listeners[ DataIdentifier ] -> Receive( DataIdentifier, Data, Length );
+    }
+    else
+    {
+    }
+  }
+
+} // namespace
diff -ruN vdr-1.4.7-darwin/rcontroller.h vdr-1.4.7/rcontroller.h
--- vdr-1.4.7-darwin/rcontroller.h	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/rcontroller.h	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,29 @@
+#ifndef __RECORDING_PATCH_OSD_CONTROLLER_H
+#define __RECORDING_PATCH_OSD_CONTROLLER_H
+
+class cDvbPlayer;
+namespace RecordingPatch {
+
+  class iRecordingPlugin
+  {
+  public:
+    virtual ~iRecordingPlugin() {};
+    virtual void Receive(unsigned char DataIdentifier, unsigned char* Data, int Length) = 0;
+  };
+  
+  class cRecordingController
+  {
+  public:
+    cRecordingController();
+    ~cRecordingController();
+    void Subscribe(unsigned char DataIdentifier, iRecordingPlugin* plugin);
+    void Unsubscribe(unsigned char DataIdentifer, iRecordingPlugin* plugin);
+    bool isExtendedPacket(unsigned char* Data, int Length);
+    void Receive(unsigned char* Data, int Length);
+  private:
+    iRecordingPlugin** listeners;
+  };
+  extern cRecordingController RecordingController;
+
+}
+#endif
diff -ruN vdr-1.4.7-darwin/recorder.c vdr-1.4.7/recorder.c
--- vdr-1.4.7-darwin/recorder.c	2006-01-08 13:01:25.000000000 +0200
+++ vdr-1.4.7/recorder.c	2007-03-26 19:39:32.000000000 +0300
@@ -11,6 +11,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <stdint.h>
 
 #define RECORDERBUFSIZE  MEGABYTE(5)
 
@@ -23,6 +24,7 @@
 
 class cFileWriter : public cThread {
 private:
+  cTtxtSubsRecorderBase *ttxtSubsRecorder;
   cRemux *remux;
   cFileName *fileName;
   cIndexFile *index;
@@ -35,13 +37,14 @@
 protected:
   virtual void Action(void);
 public:
-  cFileWriter(const char *FileName, cRemux *Remux);
+  cFileWriter(const char *FileName, cRemux *Remux, cTtxtSubsRecorderBase *tsr);
   virtual ~cFileWriter();
   };
 
-cFileWriter::cFileWriter(const char *FileName, cRemux *Remux)
+cFileWriter::cFileWriter(const char *FileName, cRemux *Remux, cTtxtSubsRecorderBase *tsr)
 :cThread("file writer")
 {
+  ttxtSubsRecorder = tsr;
   fileName = NULL;
   remux = Remux;
   index = NULL;
@@ -64,6 +67,8 @@
   Cancel(3);
   delete index;
   delete fileName;
+  if (ttxtSubsRecorder)
+     delete ttxtSubsRecorder;
 }
 
 bool cFileWriter::RunningLowOnDiskSpace(void)
@@ -108,6 +113,16 @@
                  }
               fileSize += Count;
               remux->Del(Count);
+              // not sure if the pictureType test is needed, but it seems we can get
+              // incomplete pes packets from remux if we are not getting pictures?
+              if (ttxtSubsRecorder && pictureType != NO_PICTURE) {
+                 uint8_t *subsp;
+                 size_t len;
+                 if (ttxtSubsRecorder->GetPacket(&subsp, &len)) {
+                    recordFile->Write(subsp, len);
+                    fileSize += len;
+                    }
+                 }
               }
            else
               break;
@@ -121,7 +136,7 @@
         }
 }
 
-cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
+cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids, cTtxtSubsRecorderBase *tsr)
 :cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
 ,cThread("recording")
 {
@@ -132,7 +147,7 @@
   ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
   ringBuffer->SetTimeouts(0, 100);
   remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, true);
-  writer = new cFileWriter(FileName, remux);
+  writer = new cFileWriter(FileName, remux, tsr);
 }
 
 cRecorder::~cRecorder()
diff -ruN vdr-1.4.7-darwin/recorder.h vdr-1.4.7/recorder.h
--- vdr-1.4.7-darwin/recorder.h	2005-08-13 14:31:18.000000000 +0300
+++ vdr-1.4.7/recorder.h	2007-03-26 19:39:32.000000000 +0300
@@ -15,6 +15,7 @@
 #include "remux.h"
 #include "ringbuffer.h"
 #include "thread.h"
+#include "vdrttxtsubshooks.h"
 
 class cFileWriter;
 
@@ -28,7 +29,7 @@
   virtual void Receive(uchar *Data, int Length);
   virtual void Action(void);
 public:
-  cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
+  cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids, cTtxtSubsRecorderBase *tsr);
                // Creates a new recorder that requires conditional access Ca, has
                // the given Priority and will record the given PIDs into the file FileName.
   virtual ~cRecorder();
diff -ruN vdr-1.4.7-darwin/remux.c vdr-1.4.7/remux.c
--- vdr-1.4.7-darwin/remux.c	2006-12-01 16:46:25.000000000 +0200
+++ vdr-1.4.7/remux.c	2007-03-26 19:39:32.000000000 +0300
@@ -1413,6 +1413,9 @@
 //pts_dts flags
 #define PTS_ONLY         0x80
 
+#define PES_EXTENSION    0x01
+#define PES_EXTENSION2   0x01
+
 #define TS_SIZE        188
 #define PID_MASK_HI    0x1F
 #define CONT_CNT_MASK  0x0F
@@ -1461,6 +1464,8 @@
   int ccErrors;
   int ccCounter;
   cRepacker *repacker;
+  uint8_t dataIdentifier;
+  static uint8_t eHeadr[];
   static uint8_t headr[];
   void store(uint8_t *Data, int Count);
   void reset_ipack(void);
@@ -1468,16 +1473,18 @@
   void write_ipack(const uint8_t *Data, int Count);
   void instant_repack(const uint8_t *Buf, int Count);
 public:
-  cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL);
+  cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL, uint8_t DataIdentifier = 0x00);
+
   ~cTS2PES();
   int Pid(void) { return pid; }
   void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
   void Clear(void);
   };
 
+uint8_t cTS2PES::eHeadr[] = { 0x01, 0x81 }; // extension header
 uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
 
-cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid, uint8_t SubStreamId, cRepacker *Repacker)
+cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid, uint8_t SubStreamId, cRepacker *Repacker, uint8_t DataIdentifier)
 {
   pid = Pid;
   resultBuffer = ResultBuffer;
@@ -1494,6 +1501,7 @@
   tsErrors = 0;
   ccErrors = 0;
   ccCounter = -1;
+  dataIdentifier = DataIdentifier;
 
   if (!(buf = MALLOC(uint8_t, size)))
      esyslog("Not enough memory for ts_transform");
@@ -1551,10 +1559,21 @@
 
   switch (mpeg) {
     case 2:
+         if ( dataIdentifier == 0 ) {
             buf[6] = 0x80;
             buf[7] = 0x00;
             buf[8] = 0x00;
             count = 9;
+            }
+         else {
+            buf[6] = 0x80;
+            buf[7] = 0x01; // pes_extension_flag == 1
+            buf[8] = 0x03; // pes_header_data_length == 3
+            buf[9] = 0x01; // pes_extension_flag_2=1
+            buf[10]= 0x81; // marker_bit=1, pes_extension_data_length=1 
+            buf[11] = dataIdentifier;
+            count = 12;
+            }
             break;
     case 1:
             buf[6] = 0x0F;
@@ -1717,12 +1736,21 @@
           case 7:
                   if (!done && (mpeg == 2 || mpeg1_required > 7)) {
                      flag2 = Buf[c++];
+                     if ( dataIdentifier && (flag2 & PES_EXTENSION) ) {
+                        esyslog("Error: cannot add extension to pes packet. Disabling.");
+                        dataIdentifier = 0;
+                        }
+                     else {
+                        flag2 |= PES_EXTENSION;
+                        }
                      found++;
                      }
                   break;
           case 8:
                   if (!done && (mpeg == 2 || mpeg1_required > 7)) {
                      hlength = Buf[c++];
+                     if ( dataIdentifier )
+                        hlength += 3;
                      found++;
                      if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
                         found = 0; // invalid MPEG1 header
@@ -1766,6 +1794,20 @@
                   return;
                }
 
+            // Write header one byte at a time
+            // Remove from hlength size of our header (3)
+            if ( dataIdentifier ) {
+               while (c < Count && (found < (hlength + 9-3)) && found < plength+6) {
+                     write_ipack(Buf + c, 1);
+                     c++;
+                     found++;
+                     }
+               if (found == (hlength+9-3)) {
+                  write_ipack(eHeadr, 2); 
+                  write_ipack(&dataIdentifier, 1);
+                  }
+               }
+
             while (c < Count && found < plength + 6) {
                   int l = Count - c;
                   if (l + found > plength + 6)
@@ -1888,13 +1930,11 @@
      while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS)
            ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker);
      }
-  /* future...
   if (SPids) {
      int n = 0;
      while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
-           ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x28 + n++);
+           ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x00, NULL, 0x28 + n++);
      }
-  */
 }
 
 cRemux::~cRemux()
diff -ruN vdr-1.4.7-darwin/vdr.c vdr-1.4.7/vdr.c
--- vdr-1.4.7-darwin/vdr.c	2007-03-26 19:34:42.000000000 +0300
+++ vdr-1.4.7/vdr.c	2007-03-26 19:39:32.000000000 +0300
@@ -1000,7 +1000,7 @@
                   }
                else
                   cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
-               if (!Menu && !cOsd::IsOpen())
+               if (!Menu /*&& !cOsd::IsOpen()*/)
                   Menu = cDisplayVolume::Create();
                cDisplayVolume::Process(key);
                key = kNone; // nobody else needs to see these keys
diff -ruN vdr-1.4.7-darwin/vdrttxtsubshooks.c vdr-1.4.7/vdrttxtsubshooks.c
--- vdr-1.4.7-darwin/vdrttxtsubshooks.c	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/vdrttxtsubshooks.c	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,44 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "vdrttxtsubshooks.h"
+
+// XXX Really should be a list...
+static cVDRTtxtsubsHookListener *gListener;
+
+// ------ class cVDRTtxtsubsHookProxy ------
+
+class cVDRTtxtsubsHookProxy : public cVDRTtxtsubsHookListener
+{
+ public:
+  virtual void HideOSD(void) { if(gListener) gListener->HideOSD(); };
+  virtual void ShowOSD(void) { if(gListener) gListener->ShowOSD(); };
+  virtual void PlayerTeletextData(uint8_t *p, int length)
+    { if(gListener) gListener->PlayerTeletextData(p, length); };
+  virtual cTtxtSubsRecorderBase *NewTtxtSubsRecorder(cDevice *dev, const cChannel *ch)
+    { if(gListener) return gListener->NewTtxtSubsRecorder(dev, ch); else return NULL; };
+};
+
+
+// ------ class cVDRTtxtsubsHookListener ------
+
+cVDRTtxtsubsHookListener::~cVDRTtxtsubsHookListener()
+{
+  gListener = 0;
+}
+
+void cVDRTtxtsubsHookListener::HookAttach(void)
+{
+  gListener = this;
+  //printf("cVDRTtxtsubsHookListener::HookAttach\n");
+}
+
+static cVDRTtxtsubsHookProxy gProxy;
+
+cVDRTtxtsubsHookListener *cVDRTtxtsubsHookListener::Hook(void)
+{
+  return &gProxy;
+}
+
diff -ruN vdr-1.4.7-darwin/vdrttxtsubshooks.h vdr-1.4.7/vdrttxtsubshooks.h
--- vdr-1.4.7-darwin/vdrttxtsubshooks.h	1970-01-01 02:00:00.000000000 +0200
+++ vdr-1.4.7/vdrttxtsubshooks.h	2007-03-26 19:39:32.000000000 +0300
@@ -0,0 +1,36 @@
+
+#ifndef __VDRTTXTSUBSHOOKS_H
+#define __VDRTTXTSUBSHOOKS_H
+
+class cDevice;
+class cChannel;
+
+#define VDRTTXTSUBSHOOKS
+
+class cTtxtSubsRecorderBase {
+ public:
+  virtual ~cTtxtSubsRecorderBase() {};
+
+  // returns a PES packet if there is data to add to the recording
+  virtual uint8_t *GetPacket(uint8_t **buf, size_t *len) { return NULL; };
+  virtual void DeviceAttach(void) {};
+};
+
+class cVDRTtxtsubsHookListener {
+ public:
+  cVDRTtxtsubsHookListener(void) {};
+  virtual ~cVDRTtxtsubsHookListener();
+
+  void HookAttach(void);
+  
+  virtual void HideOSD(void) {};
+  virtual void ShowOSD(void) {};
+  virtual void PlayerTeletextData(uint8_t *p, int length) {};
+  virtual cTtxtSubsRecorderBase *NewTtxtSubsRecorder(cDevice *dev, const cChannel *ch)
+    { return NULL; };
+
+  // used by VDR to call hook listeners
+  static cVDRTtxtsubsHookListener *Hook(void);
+};
+
+#endif
