]> git.vomp.tv Git - vompclient.git/commitdiff
DVB Subtitles
authorMark Calderbank <mark@vomp.tv>
Sat, 29 Nov 2008 23:31:35 +0000 (23:31 +0000)
committerMark Calderbank <mark@vomp.tv>
Sat, 29 Nov 2008 23:31:35 +0000 (23:31 +0000)
21 files changed:
bitmap.cc [new file with mode: 0644]
bitmap.h [new file with mode: 0644]
boxx.cc
boxx.h
demuxer.cc
demuxer.h
demuxervdr.cc
demuxervdr.h
dvbsubtitles.cc [new file with mode: 0644]
dvbsubtitles.h [new file with mode: 0644]
objects.mk
osdreceiver.h [new file with mode: 0644]
player.cc
player.h
surface.h
surfacemvp.cc
surfacemvp.h
surfacewin.cc
surfacewin.h
vvideorec.cc
vvideorec.h

diff --git a/bitmap.cc b/bitmap.cc
new file mode 100644 (file)
index 0000000..29e62b6
--- /dev/null
+++ b/bitmap.cc
@@ -0,0 +1,125 @@
+/*
+    Copyright 2008 Mark Calderbank
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+#include "bitmap.h"
+
+Palette::Palette(UCHAR tBpp)
+{
+  numColours = 0;
+  setBpp(tBpp);
+}
+
+void Palette::argb2yrba(ULONG argb, UCHAR& y, UCHAR& cr, UCHAR& cb, UCHAR& a)
+{
+  a = ((argb & 0xFF000000) >> 24);
+  int r = (argb & 0x00FF0000) >> 16;
+  int g = (argb & 0x0000FF00) >> 8;
+  int b = (argb & 0x000000FF);
+  y  = (1052*r + 2065*g + 401*b + 4096*16 + 2048) / 4096;
+  cr = (1799*r - 1508*g - 291*b + 4096*128 + 2048) / 4096;
+  cb = (-608*r - 1191*g + 1799*b + 4096*128 + 2048) / 4096;
+}
+
+ULONG Palette::yrba2argb(UCHAR y, UCHAR cr, UCHAR cb, UCHAR a)
+{
+  int r, g, b;
+  r = (4769*(y-16) + 6537*(cr-128) + 2048) / 4096;
+  g = (4769*(y-16) - 3329*(cr-128) - 1604*(cb-128) + 2048) / 4096;
+  b = (4769*(y-16) + 8263*(cb-128) + 2048) / 4096;
+  if (r < 0) r = 0; if (r > 255) r = 255;
+  if (g < 0) g = 0; if (g > 255) g = 255;
+  if (b < 0) b = 0; if (b > 255) b = 255;
+  return (a << 24) + (r << 16) + (g << 8) + b;
+}
+
+void Palette::setBpp(UCHAR tBpp)
+{
+  bpp = tBpp;
+  if (bpp > MAX_DEPTH) bpp = MAX_DEPTH;
+  maxColours = 1 << bpp;
+  if (numColours > maxColours) numColours = maxColours;
+  colour.resize(maxColours,0xFF000000);
+  Y.resize(maxColours,16);
+  Cr.resize(maxColours,128);
+  Cb.resize(maxColours,128);
+  A.resize(maxColours,255);
+}
+
+void Palette::setColour(UCHAR index, ULONG tColour)
+{
+  if (index >= maxColours) return;
+  if (index >= numColours) numColours = index + 1;
+  colour[index] = tColour;
+  argb2yrba(tColour, Y[index], Cr[index], Cb[index], A[index]);
+}
+
+void Palette::setYCrCbA(UCHAR index, UCHAR tY, UCHAR tCr, UCHAR tCb, UCHAR tA)
+{
+  if (index >= maxColours) return;
+  if (index >= numColours) numColours = index + 1;
+  Y[index] = tY; Cr[index] = tCr; Cb[index] = tCb; A[index] = tA;
+  colour[index] = yrba2argb(tY, tCr, tCb, tA);
+}
+
+Bitmap::Bitmap(UINT tWidth, UINT tHeight, UCHAR tBpp)
+{
+  setSize(tWidth, tHeight);
+  palette.setBpp(tBpp);
+}
+
+void Bitmap::setSize(UINT tWidth, UINT tHeight)
+{
+  if (width == tWidth && height == tHeight) return;
+  width = tWidth; height = tHeight;
+  if (width == 0 || height == 0)
+    bitmap.clear();
+  else
+    bitmap.assign(width * height, 0);
+}
+
+bool Bitmap::setIndex(UINT x, UINT y, UCHAR index)
+{
+  if (x <= width && y <= height)
+  {
+    bitmap[x + y*width] = index;
+    return true;
+  }
+  else return false;
+}
+
+UCHAR Bitmap::getIndex(UINT x, UINT y) const
+{
+  if (x > width || y > height)
+    return 0;
+  else
+    return bitmap[x + y*width];
+}
+
+ULONG Bitmap::getColour(UINT x, UINT y) const
+{
+  if (x > width || y > height)
+    return 0;
+  else
+    return palette.getColour(bitmap[x + y*width]);
+}
+
+void Bitmap::setAllIndices(UCHAR index)
+{
+  bitmap.assign(width * height, index);
+}
diff --git a/bitmap.h b/bitmap.h
new file mode 100644 (file)
index 0000000..654775f
--- /dev/null
+++ b/bitmap.h
@@ -0,0 +1,73 @@
+/*
+    Copyright 2008 Mark Calderbank
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include "defines.h"
+#include <vector>
+
+class Palette
+{
+  public:
+    Palette(UCHAR tBpp = 8);
+    UCHAR getBpp() const { return bpp; }
+    void reset() { numColours = 0; }
+    void setBpp(UCHAR tBpp);
+    ULONG getColour(UCHAR index) const {
+      return index < maxColours ? colour[index] : 0; }
+    void setColour(UCHAR index, ULONG tColour);
+    void setYCrCbA(UCHAR index, UCHAR tY, UCHAR tCr, UCHAR tCb, UCHAR tA);
+    const std::vector<ULONG>& getColourVector() const { return colour; }
+    const std::vector<UCHAR>& getYVector() const { return Y; }
+    const std::vector<UCHAR>& getCrVector() const { return Cr; }
+    const std::vector<UCHAR>& getCbVector() const { return Cb; }
+    const std::vector<UCHAR>& getAVector() const { return A; }
+  private:
+    const static UINT MAX_DEPTH = 8;
+    std::vector<ULONG> colour;
+    std::vector<UCHAR> Y;
+    std::vector<UCHAR> Cr;
+    std::vector<UCHAR> Cb;
+    std::vector<UCHAR> A;
+    UCHAR bpp;
+    UINT maxColours, numColours;
+    void argb2yrba(ULONG argb, UCHAR& y, UCHAR& cr, UCHAR& cb, UCHAR& a);
+    ULONG yrba2argb(UCHAR y, UCHAR cr, UCHAR cb, UCHAR a);
+};
+
+class Bitmap
+{
+  private:
+    std::vector<UCHAR> bitmap;
+    UINT width, height;
+  public:
+    Bitmap(UINT tWidth = 0, UINT tHeight = 0, UCHAR tBpp = 8);
+    Palette palette;
+    UINT getWidth() const { return width; }
+    UINT getHeight() const { return height; }
+    UCHAR getIndex(UINT x, UINT y) const;
+    ULONG getColour(UINT x, UINT y) const;
+    const std::vector<UCHAR> & rawData() const { return bitmap; }
+    void setSize(UINT tWidth, UINT tHeight);
+    bool setIndex(UINT x, UINT y, UCHAR index);
+    void setAllIndices(UCHAR index);
+}; 
+
+#endif
diff --git a/boxx.cc b/boxx.cc
index c46af63c48bc46a6ff45ed29b4f55c2365de8867..af6ce48fb81a2a85941dd8852e671a82de128024 100644 (file)
--- a/boxx.cc
+++ b/boxx.cc
@@ -19,7 +19,7 @@
 */
 
 #include "boxx.h"
-
+#include "bitmap.h"
 #include "log.h"
 
 char Boxx::numBoxxes = 0;
@@ -360,6 +360,12 @@ void Boxx::drawPixel(UINT x, UINT y, const Colour& colour)
   }
 }
 
+void Boxx::drawBitmap(UINT x, UINT y, const Bitmap& bm)
+{
+  if (parent) parent->drawBitmap(area.x + x, area.y + y, bm);
+  else surface->drawBitmap(x, y, bm);
+}
+
 void Boxx::startFastDraw()
 {
     if (parent) parent->startFastDraw();
diff --git a/boxx.h b/boxx.h
index f9da2e1bfd2a69b1db422f6ad6afccd16c72162e..439fa54f38c905e2c50c7fae6ed3bf78dcc61863 100644 (file)
--- a/boxx.h
+++ b/boxx.h
@@ -36,6 +36,8 @@ using namespace std;
 #include "surfacemvp.h"
 #endif
 
+class Bitmap;
+
 class Boxx
 {
   public:
@@ -101,6 +103,7 @@ class Boxx
     void drawTextRJ(const char* text, int x, int y, const Colour& colour);
     void drawTextCentre(const char* text, int x, int y, const Colour& colour);
     void drawPixel(UINT x, UINT y, const Colour& colour);
+    void drawBitmap(UINT x, UINT y, const Bitmap& bm);
 
     /* This is for system which need a locking of the drawing surface to speed up drawing */
     void startFastDraw();
index b75d2563b8605a01f316e1b24bd5d686b76771bb..931f33f0ed0da40138836a60c7715c802c6023a7 100644 (file)
@@ -159,7 +159,7 @@ Demuxer* Demuxer::getInstance()
   return instance;
 }
 
-int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, ULONG demuxMemoryV, ULONG demuxMemoryA)
+int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, ULONG demuxMemoryV, ULONG demuxMemoryA, DVBSubtitles* tsubtitles)
 {
   if (!initted)
   {
@@ -175,6 +175,7 @@ int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, U
 
   reset();
   initted = true;
+  subtitles = tsubtitles;
   callback = tcallback;
   return 1;
 }
index 7994b966d4735983ed37b6808f8b9bb45aacec4b..ebfccaa5643cc2afa4983dd23bf4978ac1df6293 100644 (file)
--- a/demuxer.h
+++ b/demuxer.h
@@ -34,6 +34,7 @@ however, no code was copied verbatim.
 #include "stream.h"
 #include "defines.h"
 
+class DVBSubtitles;
 class Callback;
 class DrainTarget;
 
@@ -75,7 +76,7 @@ class Demuxer
     Demuxer();
     virtual ~Demuxer();
     static Demuxer* getInstance();
-    int init(Callback* callback, DrainTarget* audio, DrainTarget* video, ULONG demuxMemoryV, ULONG demuxMemoryA);
+    int init(Callback* callback, DrainTarget* audio, DrainTarget* video, ULONG demuxMemoryV, ULONG demuxMemoryA, DVBSubtitles* tsubtitles = NULL);
     virtual void reset();
     virtual void flush();
     void flushAudio();
@@ -162,6 +163,7 @@ class Demuxer
     static Demuxer* instance;
     Stream videostream;
     Stream audiostream;
+    DVBSubtitles* subtitles;
     int shutdown();
     bool initted;
     bool vid_seeking;
index 0a69cf3708f6a755a8d106c2787311b3867ad111..4ff96980aecdfe4a2018249c694ec0e8a5f385f5 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright 2005-2007 Mark Calderbank
+    Copyright 2005-2008 Mark Calderbank
 
     This file is part of VOMP.
 
     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
 #include "demuxervdr.h"
-
 #include "video.h"
+#include "dvbsubtitles.h"
 #include "log.h"
 
 #ifndef WIN32
@@ -56,6 +56,7 @@ DemuxerVDR::DemuxerVDR()
 {
   frameCounting = false;
   packetCounting = false;
+  subtitlePacketPosition = 0;
 }
 
 void DemuxerVDR::reset()
@@ -70,6 +71,7 @@ void DemuxerVDR::flush()
 {
   state = 0;
   submitting = false;
+  subtitlePacketPosition = 0;
   Demuxer::flush();
 }
 
@@ -288,10 +290,6 @@ ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts)
     pts_map.erase(iter, pts_map.end());
   }
   pts_map_mutex.Unlock();
-  if (total > 1 && actual == 1)
-Log::getInstance()->log("Demuxer", Log::DEBUG, "DELETED REFERENCES");
-  if (actual > 1)
-Log::getInstance()->log("Demuxer", Log::DEBUG, "STILL USING OLD REF");
 
   if (difference == (1LL<<33))
     return 0; // We cannot make sense of the pts
@@ -299,9 +297,67 @@ Log::getInstance()->log("Demuxer", Log::DEBUG, "STILL USING OLD REF");
     return ref_frame + difference * Video::getInstance()->getFPS() / 90000;
 }
 
+void DemuxerVDR::dealWithSubtitlePacket()
+{
+  const std::vector<UCHAR>& data = packet.getData();
+  UINT packetSize = packet.getSize();
+  if (packetSize < 9) return;
+  UINT payloadOffset = data[8] + 9;
+  if (packetSize < payloadOffset + 5) return;
+  if (data[payloadOffset + 3] == 0x00)
+  { // Not a continuation packet
+    if (packetSize < payloadOffset + 7) return;
+    subtitlePacket.init(data[3]);
+    subtitlePacket.write(&data[6], payloadOffset - 6);
+    subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);
+    subtitlePacketPosition = payloadOffset + 2;
+  }
+  else
+  { // Continuation packet
+    if (subtitlePacketPosition == 0) return;
+    subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);
+  }
+  
+  const std::vector<UCHAR>& sub_data = subtitlePacket.getData();
+  UINT subSize = subtitlePacket.getSize();
+  while (subtitlePacketPosition < subSize)
+  {
+    if (sub_data[subtitlePacketPosition] == 0xFF)
+    {
+      subtitles->put(subtitlePacket);
+      subtitlePacketPosition = 0; // Wait for next non-continuation packet
+      break;
+    }
+    if (sub_data[subtitlePacketPosition] != 0x0F)
+    {
+      subtitlePacketPosition = 0; // Wait for next non-continuation packet
+      break;
+    }
+    if (subSize < subtitlePacketPosition + 6) break;
+    UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8)
+                        + sub_data[subtitlePacketPosition + 5];
+    subtitlePacketPosition += segmentLength + 6;
+  }
+}
+
 void DemuxerVDR::parseVDRPacketDetails()
 {
-  parsePacketDetails(packet);
+  if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8)
+  {
+    UINT payload_begin = packet[8] + 9;
+    if (packet.getSize() > payload_begin)
+    {
+      UCHAR substream_type = packet[payload_begin] & 0xF0;
+      if (substream_type == 0x20) // Subtitles
+      {
+        dealWithSubtitlePacket();
+      }
+      else // Not subtitles
+        parsePacketDetails(packet);
+    }
+  }
+  else // Not a private packet*/
+    parsePacketDetails(packet);
 
   if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&
                         packet.getPacketType() <= PESTYPE_AUDMAX)
index d64e9e5fb5e6fe416b4ed1661dcf95afa9388c70..5efa71a480224c870cc08282f7fc2faf2efbc289 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright 2005-2007 Mark Calderbank
+    Copyright 2005-2008 Mark Calderbank
 
     This file is part of VOMP.
 
@@ -14,8 +14,8 @@
     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
 #ifndef DEMUXERVDR_H
@@ -45,6 +45,8 @@ class DemuxerVDR : public Demuxer
     bool submitting;
     int packetLength;
     PESPacket packet;
+    PESPacket subtitlePacket;
+    UINT subtitlePacketPosition;
 
     ULONG frameNumber, packetNumber;
     bool frameCounting, packetCounting;
@@ -54,6 +56,7 @@ class DemuxerVDR : public Demuxer
     Mutex pts_map_mutex;
 
     void parseVDRPacketDetails();
+    void dealWithSubtitlePacket();
 };
 
 #endif
diff --git a/dvbsubtitles.cc b/dvbsubtitles.cc
new file mode 100644 (file)
index 0000000..32836d9
--- /dev/null
@@ -0,0 +1,938 @@
+/*
+    Copyright 2008 Mark Calderbank
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+/*  Sections of code and general methods adopted from VDR version 1.6.0
+ *  dvbsubtitle.c (GPL version 2 or later)
+ *  Original author: Marco Schluessler <marco@lordzodiac.de>
+ *  With some input from the "subtitle plugin"
+ *  by Pekka Virtanen <pekka.virtanen@sci.fi>
+*/
+
+/*  Reference ETSI EN 300 743 V1.3.1 (2006-11) */
+
+#include "dvbsubtitles.h"
+#include "demuxer.h"
+#include "osdreceiver.h"
+#include "video.h"
+#include "log.h"
+
+DVBSubtitleCLUT::DVBSubtitleCLUT()
+: version(0xFF),
+  palette2(2),
+  palette4(4),
+  palette8(8)
+{
+  for (int i = 0; i < 4; ++i) palette2.setColour(i, defaultPalette2[i]);
+  for (int i = 0; i < 16; ++i) palette4.setColour(i, defaultPalette4[i]);
+  for (int i = 0; i < 256; ++i) palette8.setColour(i, defaultPalette8[i]);
+}
+
+const ULONG DVBSubtitleCLUT::defaultPalette2[4] = {
+  0x00000000, 0xFFFFFFFF, 0xFF000000, 0xFF808080
+};
+
+const ULONG DVBSubtitleCLUT::defaultPalette4[16] = {
+  0x00000000, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00,
+  0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF,
+  0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000,
+  0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080
+};
+
+const ULONG DVBSubtitleCLUT::defaultPalette8[256] = {
+  0x00000000, 0x40FF0000, 0x4000FF00, 0x40FFFF00, // 0000 0000
+  0x400000FF, 0x40FF00FF, 0x4000FFFF, 0x40FFFFFF, // 0000 0100
+  0x80000000, 0x80550000, 0x80005500, 0x80555500, // 0000 1000
+  0x80000055, 0x80550055, 0x80005555, 0x80555555, // 0000 1100
+  0xFFAA0000, 0xFFFF0000, 0xFFAA5500, 0xFFFF5500, // 0001 0000
+  0xFFAA0055, 0xFFFF0055, 0xFFAA5555, 0xFFFF5555, // 0001 0100
+  0x80AA0000, 0x80FF0000, 0x80AA5500, 0x80FF5500, // 0001 1000
+  0x80AA0055, 0x80FF0055, 0x80AA5555, 0x80FF5555, // 0001 1100
+  0xFF00AA00, 0xFF55AA00, 0xFF00FF00, 0xFF55FF00, // 0010 0000
+  0xFF00AA55, 0xFF55AA55, 0xFF00FF55, 0xFF55FF55, // 0010 0100
+  0x8000AA00, 0x8055AA00, 0x8000FF00, 0x8055FF00, // 0010 1000
+  0x8000AA55, 0x8055AA55, 0x8000FF55, 0x8055FF55, // 0010 1100
+  0xFFAAAA00, 0xFFFFAA00, 0xFFAAFF00, 0xFFFFFF00, // 0011 0000
+  0xFFAAAA55, 0xFFFFAA55, 0xFFAAFF55, 0xFFFFFF55, // 0011 0100
+  0x80AAAA00, 0x80FFAA00, 0x80AAFF00, 0x80FFFF00, // 0011 1000
+  0x80AAAA55, 0x80FFAA55, 0x80AAFF55, 0x80FFFF55, // 0011 1100
+  0xFF0000AA, 0xFF5500AA, 0xFF0055AA, 0xFF5555AA, // 0100 0000
+  0xFF0000FF, 0xFF5500FF, 0xFF0055FF, 0xFF5555FF, // 0100 0100
+  0x800000AA, 0x805500AA, 0x800055AA, 0x805555AA, // 0100 1000
+  0x800000FF, 0x805500FF, 0x800055FF, 0x805555FF, // 0100 1100
+  0xFFAA00AA, 0xFFFF00AA, 0xFFAA55AA, 0xFFFF55AA, // 0101 0000
+  0xFFAA00FF, 0xFFFF00FF, 0xFFAA55FF, 0xFFFF55FF, // 0101 0100
+  0x80AA00AA, 0x80FF00AA, 0x80AA55AA, 0x80FF55AA, // 0101 1000
+  0x80AA00FF, 0x80FF00FF, 0x80AA55FF, 0x80FF55FF, // 0101 1100
+  0xFF00AAAA, 0xFF55AAAA, 0xFF00FFAA, 0xFF55FFAA, // 0110 0000
+  0xFF00AAFF, 0xFF55AAFF, 0xFF00FFFF, 0xFF55FFFF, // 0110 0100
+  0x8000AAAA, 0x8055AAAA, 0x8000FFAA, 0x8055FFAA, // 0110 1000
+  0x8000AAFF, 0x8055AAFF, 0x8000FFFF, 0x8055FFFF, // 0110 1100
+  0xFFAAAAAA, 0xFFFFAAAA, 0xFFAAFFAA, 0xFFFFFFAA, // 0111 0000
+  0xFFAAAAFF, 0xFFFFAAFF, 0xFFAAFFFF, 0xFFFFFFFF, // 0111 0100
+  0x80AAAAAA, 0x80FFAAAA, 0x80AAFFAA, 0x80FFFFAA, // 0111 1000
+  0x80AAAAFF, 0x80FFAAFF, 0x80AAFFFF, 0x80FFFFFF, // 0111 1100
+  0xFF808080, 0xFFAA8080, 0xFF80AA80, 0xFFAAAA80, // 1000 0000
+  0xFF8080AA, 0xFFAA80AA, 0xFF80AAAA, 0xFFAAAAAA, // 1000 0100
+  0xFF000000, 0xFF2A0000, 0xFF002A00, 0xFF2A2A00, // 1000 1000
+  0xFF00002A, 0xFF2A002A, 0xFF002A2A, 0xFF2A2A2A, // 1000 1100
+  0xFFD58080, 0xFFFF8080, 0xFFD5AA80, 0xFFFFAA80, // 1001 0000
+  0xFFD580AA, 0xFFFF80AA, 0xFFD5AAAA, 0xFFFFAAAA, // 1001 0100
+  0xFF550000, 0xFF7F0000, 0xFF552A00, 0xFF7F2A00, // 1001 1000
+  0xFF55002A, 0xFF7F002A, 0xFF552A2A, 0xFF7F2A2A, // 1001 1100
+  0xFF80D580, 0xFFAAD580, 0xFF80FF80, 0xFFAAFF80, // 1010 0000
+  0xFF80D5AA, 0xFFAAD5AA, 0xFF80FFAA, 0xFFAAFFAA, // 1010 0100
+  0xFF005500, 0xFF2A5500, 0xFF007F00, 0xFF2A7F00, // 1010 1000
+  0xFF00552A, 0xFF2A552A, 0xFF007F2A, 0xFF2A7F2A, // 1010 1100
+  0xFFD5D580, 0xFFFFD580, 0xFFD5FF80, 0xFFFFFF80, // 1011 0000
+  0xFFD5D5AA, 0xFFFFD5AA, 0xFFD5FFAA, 0xFFFFFFAA, // 1011 0100
+  0xFF555500, 0xFF7F5500, 0xFF557F00, 0xFF7F7F00, // 1011 1000
+  0xFF55552A, 0xFF7F552A, 0xFF557F2A, 0xFF7F7F2A, // 1011 1100
+  0xFF8080D5, 0xFFAA80D5, 0xFF80AAD5, 0xFFAAAAD5, // 1100 0000
+  0xFF8080FF, 0xFFAA80FF, 0xFF80AAFF, 0xFFAAAAFF, // 1100 0100
+  0xFF000055, 0xFF2A0055, 0xFF002A55, 0xFF2A2A55, // 1100 1000
+  0xFF00007F, 0xFF2A007F, 0xFF002A7F, 0xFF2A2A7F, // 1100 1100
+  0xFFD580D5, 0xFFFF80D5, 0xFFD5AAD5, 0xFFFFAAD5, // 1101 0000
+  0xFFD580FF, 0xFFFF80FF, 0xFFD5AAFF, 0xFFFFAAFF, // 1101 0100
+  0xFF550055, 0xFF7F0055, 0xFF552A55, 0xFF7F2A55, // 1101 1000
+  0xFF55007F, 0xFF7F007F, 0xFF552A7F, 0xFF7F2A7F, // 1101 1100
+  0xFF80D5D5, 0xFFAAD5D5, 0xFF80FFD5, 0xFFAAFFD5, // 1110 0000
+  0xFF80D5FF, 0xFFAAD5FF, 0xFF80FFFF, 0xFFAAFFFF, // 1110 0100
+  0xFF005555, 0xFF2A5555, 0xFF007F55, 0xFF2A7F55, // 1110 1000
+  0xFF00557F, 0xFF2A557F, 0xFF007F7F, 0xFF2A7F7F, // 1110 1100
+  0xFFD5D5D5, 0xFFFFD5D5, 0xFFD5FFD5, 0xFFFFFFD5, // 1111 0000
+  0xFFD5D5FF, 0xFFFFD5FF, 0xFFD5FFFF, 0xFFFFFFFF, // 1111 0100
+  0xFF555555, 0xFF7F5555, 0xFF557F55, 0xFF7F7F55, // 1111 1000
+  0xFF55557F, 0xFF7F557F, 0xFF557F7F, 0xFF7F7F7F, // 1111 1100
+};
+
+void DVBSubtitleCLUT::setEntry(int bpp, UINT index, UCHAR Y, UCHAR Cr, UCHAR Cb, UCHAR T)
+{
+  UCHAR Y2 = Y;
+  UCHAR A;
+  if (Y2 == 0) { Y2 = 16; A = 0; } else A = 255 - T;
+  switch (bpp)
+  {
+    case 2: palette2.setYCrCbA(index, Y2, Cr, Cb, A); break;
+    case 4: palette4.setYCrCbA(index, Y2, Cr, Cb, A); break;
+    case 8: palette8.setYCrCbA(index, Y2, Cr, Cb, A); break;
+  }
+}
+
+const Palette& DVBSubtitleCLUT::getPalette(int bpp) const
+{
+  switch (bpp)
+  {
+    case 2: return palette2; break;
+    case 4: return palette4; break;
+    default: return palette8; break;
+  }
+}
+
+DVBSubtitleRegion::DVBSubtitleRegion()
+: version(0xFF),
+  level(0),
+  CLUTid(0)
+{}
+
+void DVBSubtitleRegion::setDepth(UINT d)
+{
+  if (d < 4) palette.setBpp(1 << d);
+}
+
+DVBSubtitlePage::DVBSubtitlePage()
+: pts(0),
+  dirty(false),
+  version(0xFF),
+  timeout(0),
+  state(0),
+  serviceAcquired(false)
+{}
+
+void DVBSubtitlePage::setState(UCHAR s)
+{
+  state = s;
+  if (state == 1)
+    serviceAcquired = true;
+  if (state == 2) // Mode change
+  {
+    serviceAcquired = true;
+    dirty = true;
+    regions.clear();
+    currentDisplay.clear();
+    pendingDisplay.clear();
+    CLUTs.clear();
+  }
+}
+
+void DVBSubtitlePage::updateRegionPalettes(const DVBSubtitleCLUT& clut)
+{
+  RegionMap::iterator region;
+  for (region = regions.begin(); region != regions.end(); ++region)
+    if (region->second.CLUTid == clut.CLUTid)
+      region->second.palette = clut.getPalette(region->second.palette.getBpp());
+}
+
+class DVBSubtitleObject
+{
+  private:
+    UINT objectID;
+    UCHAR nonModColourFlag;
+    struct RegionRef
+    {
+      DVBSubtitleRegion* region;
+      UINT horizontalPosition;
+      UINT verticalPosition;
+    };
+    typedef std::vector<struct RegionRef> UseList;
+    UseList useList;
+    UCHAR map2to4[4];
+    UCHAR map2to8[4];
+    UCHAR map4to8[16];
+    void initMapTables();
+    UCHAR* currentMapTable;
+    UINT bitPos;
+    void drawLine(const RegionRef& use, UINT x, UINT y, UCHAR colour, UINT len);
+    UCHAR get2Bits(const UCHAR* data, UINT& index);
+    UCHAR get4Bits(const UCHAR* data, UINT& index);
+    bool decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
+    bool decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
+    bool decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
+  public:
+    DVBSubtitleObject(UINT id = 0);
+    void setNonModColourFlag(UCHAR flag) { nonModColourFlag = flag; }
+    void findWhereUsed(DVBSubtitlePage&);
+    void decodeSubBlock(const UCHAR* data, UINT length, bool even);
+};
+
+DVBSubtitleObject::DVBSubtitleObject(UINT id)
+: objectID(id),
+  nonModColourFlag(0),
+  currentMapTable(NULL),
+  bitPos(0)
+{
+  initMapTables();
+}
+
+void DVBSubtitleObject::initMapTables()
+{
+  map2to4[0] = 0x0; map2to4[1] = 0x7; map2to4[2] = 0x8; map2to4[3] = 0xF;
+  map2to8[0] = 0x00; map2to8[1] = 0x77; map2to8[2] = 0x88; map2to8[3] = 0xFF;
+  map4to8[0] = 0x00; map4to8[1] = 0x11; map4to8[2] = 0x22; map4to8[3] = 0x33;
+  map4to8[4] = 0x44; map4to8[5] = 0x55; map4to8[6] = 0x66; map4to8[7] = 0x77;
+  map4to8[8] = 0x88; map4to8[9] = 0x99; map4to8[10]= 0xAA; map4to8[11]= 0xBB;
+  map4to8[12]= 0xCC; map4to8[13]= 0xDD; map4to8[14]= 0xEE; map4to8[15]= 0xFF;
+}
+
+void DVBSubtitleObject::findWhereUsed(DVBSubtitlePage& page)
+{
+  useList.clear();
+  DVBSubtitlePage::RegionMap::iterator i;
+  for (i = page.regions.begin(); i != page.regions.end(); ++i)
+  {
+    DVBSubtitleRegion::ObjectList::iterator j;
+    for (j = i->second.objects.begin(); j != i->second.objects.end(); ++j)
+    {
+      if (j->objectID == objectID)
+      {
+        struct RegionRef usage;
+        usage.region = &(i->second);
+        usage.horizontalPosition = j->horizontalPosition;
+        usage.verticalPosition = j->verticalPosition;
+        useList.push_back(usage);
+      }
+    }
+  }
+}
+
+void DVBSubtitleObject::drawLine(const RegionRef& use, UINT x, UINT y,
+                                 UCHAR colour, UINT len)
+{
+  if (nonModColourFlag && colour == 1) return;
+  x += use.horizontalPosition;
+  y += use.verticalPosition;
+  for (UINT pos = x; pos < x + len; ++pos)
+    use.region->setIndex(pos, y, colour);
+}
+
+UCHAR DVBSubtitleObject::get2Bits(const UCHAR* data, UINT& index)
+{
+  UCHAR result = data[index];
+  if (!bitPos)
+  {
+    index++;
+    bitPos = 8;
+  }
+  bitPos -= 2;
+  return (result >> bitPos) & 0x03;
+}
+
+UCHAR DVBSubtitleObject::get4Bits(const UCHAR* data, UINT& index)
+{
+  UCHAR result = data[index];
+  if (!bitPos)
+  {
+    index++;
+    bitPos = 4;
+  }
+  else
+  {
+    result >>= 4;
+    bitPos -= 4;
+  }
+  return result & 0x0F;
+}
+
+bool DVBSubtitleObject::decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
+{
+  UINT rl = 0, colour = 0;
+  UCHAR code = get2Bits(data, index);
+  if (code)
+  {
+    colour = code;
+    rl = 1;
+  }
+  else
+  {
+    code = get2Bits(data, index);
+    if (code & 2) // Switch 1
+    {
+      rl = ((code & 1) << 2) + get2Bits(data, index) + 3;
+      colour = get2Bits(data, index);
+    }
+    else if (code & 1) rl = 1; // Colour 0
+    else
+    {
+      code = get2Bits(data, index);
+      switch (code)
+      {
+        case 0:
+          return false; // end of 2-bit/pixel_code_string
+        case 1:
+          rl = 2; // Colour 0
+          break;
+        case 2:
+          rl = (get2Bits(data, index) << 2) + get2Bits(data, index) + 12;
+          colour = get2Bits(data, index);
+          break;
+        case 3:
+          rl = (get2Bits(data, index) << 6) + (get2Bits(data, index) << 4) +
+               (get2Bits(data, index) << 2) + get2Bits(data, index) + 29;
+          colour = get2Bits(data, index);
+          break;
+      }
+    }
+  }
+  drawLine(use, x, y, colour, rl);
+  x += rl;
+  return true;
+}
+
+bool DVBSubtitleObject::decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
+{
+  UINT rl = 0, colour = 0;
+  UCHAR code = get4Bits(data, index);
+  if (code)
+  {
+    colour = code;
+    rl = 1;
+  }
+  else
+  {
+    code = get4Bits(data, index);
+    if (code & 8) // Switch 1
+    {
+      if (code & 4) // Switch 2
+      {
+        switch (code & 3) // Switch 3
+        {
+          case 0: // Colour 0
+            rl = 1;
+            break;
+          case 1: // Colour 0
+            rl = 2;
+            break;
+          case 2:
+            rl = get4Bits(data, index) + 9;
+            colour = get4Bits(data, index);
+            break;
+          case 3:
+            rl = (get4Bits(data, index) << 4) + get4Bits(data, index) + 25;
+            colour = get4Bits(data, index);
+            break;
+        }
+      }
+      else
+      {
+        rl = (code & 3) + 4;
+        colour = get4Bits(data, index);
+      }
+    }
+    else
+    {
+      if (!code) return false; // end of 4-bit/pixel_code_string
+      rl = code + 2; // Colour 0
+    }
+  }
+  drawLine(use, x, y, colour, rl);
+  x += rl;
+  return true;
+}
+
+bool DVBSubtitleObject::decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
+{
+  UINT rl = 0, colour = 0;
+  UCHAR code = data[index++];
+  if (code)
+  {
+    colour = code;
+    rl = 1;
+  }
+  else
+  {
+    code = data[index++];
+    rl = code & 0x7F;
+    if (code & 0x80)
+      colour = data[index++];
+    else if (!rl)
+      return false; // else colour 0
+  }
+  drawLine(use, x, y, colour, rl);
+  x += rl;
+  return true;
+}
+
+void DVBSubtitleObject::decodeSubBlock(const UCHAR* data, UINT length, bool even)
+{
+  UseList::const_iterator use;
+  for (use = useList.begin(); use != useList.end(); ++use)
+  {
+    UINT x = 0; UINT y = even ? 0 : 1; UINT index = 0;
+    while (index < length)
+    {
+      switch (data[index++])
+      {
+        case 0x10:
+          switch (use->region->palette.getBpp())
+          {
+            case 8: currentMapTable = map2to8; break;
+            case 4: currentMapTable = map2to4; break;
+            default: currentMapTable = NULL;
+          }
+          bitPos = 8;
+          while (decode2BppCodeString(data, index, *use,x,y) && index < length);
+          if (!bitPos) index++;
+          break;
+        case 0x11:
+          if (use->region->palette.getBpp() == 8)
+            currentMapTable = map4to8;
+          else
+            currentMapTable = NULL;
+          bitPos = 4;
+          while (decode4BppCodeString(data, index, *use,x,y) && index < length);
+          if (!bitPos) index++;
+          break;
+        case 0x12:
+          while (decode8BppCodeString(data, index, *use,x,y) && index < length);
+          break;
+        case 0x20:
+          map2to4[0] = data[index] >> 4;
+          map2to4[1] = data[index++] & 0x0F;
+          map2to4[2] = data[index] >> 4;
+          map2to4[3] = data[index++] & 0x0F;
+          break;
+        case 0x21:
+          for (int i = 0; i < 4; i++) map2to8[i] = data[index++];
+          break;
+        case 0x22:
+          for (int i = 0; i < 16; i++) map4to8[i] = data[index++];
+          break;
+        case 0xF0:
+          x = 0; y += 2;
+          break;
+      }
+    }
+  }
+}
+
+static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
+{
+  // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
+  if (pts1 > pts2)
+    return pts1 - pts2;
+  else
+    return (1LL<<33) + pts1 - pts2;
+}
+
+DVBSubtitles::DVBSubtitles(OSDReceiver* tosd)
+: osd(tosd),
+  pageOnDisplay(65536),
+  running(false),
+  showing(false),
+  threadNudged(false)
+{
+#ifndef WIN32
+  pthread_mutex_init(&input_mutex, NULL);
+  pthread_mutex_init(&output_mutex, NULL);
+#else
+  input_mutex=CreateMutex(NULL,FALSE,NULL);
+  output_mutex=CreateMutex(NULL,FALSE,NULL);
+#endif
+}
+
+void DVBSubtitles::put(const PESPacket& packet)
+{
+  lockInput();
+  if (running)
+  {
+    if (packet.getPTS() != PESPacket::PTS_INVALID)
+    {
+    worklist.push_back(packet);
+    nudge();
+    }
+  }
+  unlockInput();
+}
+
+bool DVBSubtitles::decodePacket(const PESPacket& packet)
+{
+  const std::vector<UCHAR>& data = packet.getData();
+  UINT packetSize = packet.getSize();
+  if (packetSize < 9) return false;
+  UINT segmentOffset = data[8] + 11;
+  while (packetSize > segmentOffset)
+  {
+    if (data[segmentOffset] == 0xFF) return true; // 'End of PES' marker
+    if (data[segmentOffset] != 0x0F) return false; // Out of sync
+    if (packetSize <= segmentOffset + 5) return false; // Packet truncated
+    UCHAR segmentType = data[segmentOffset + 1];
+    UINT pageID = (data[segmentOffset + 2] << 8)
+                 + data[segmentOffset + 3];
+    UINT segmentLength = (data[segmentOffset + 4] << 8)
+                        + data[segmentOffset + 5];
+    if (pageOnDisplay == 65536) pageOnDisplay = pageID;
+    if (pageOnDisplay != pageID) return false;
+    if (packetSize <= segmentOffset + 5 + segmentLength) return false;
+    const UCHAR* segmentData = &data[segmentOffset + 6];
+    pages[pageID]; // Create this page if it doesn't exist
+    PageMap::iterator pageEntry = pages.find(pageID);
+    if (pageEntry == pages.end()) return false;
+    DVBSubtitlePage& page = pageEntry->second;
+    if (packet.getPTS() != PESPacket::PTS_INVALID) page.pts = packet.getPTS();
+    switch (segmentType)
+    {
+    case 0x10: // Page composition
+    {
+      if (segmentLength < 2) break;
+      page.setState((segmentData[1] & 0x0C) >> 2);
+      if (!page.serviceAcquired) break;
+      UCHAR pageVersion = (segmentData[1] & 0xF0) >> 4;
+      if (pageVersion == page.version) break; // No update
+      page.version = pageVersion;
+      page.timeout = segmentData[0];
+      page.pendingDisplay.clear();
+      UINT parsePos = 2;
+      while (segmentLength > parsePos + 5)
+      {
+        UCHAR regionID = segmentData[parsePos];
+        page.regions[regionID]; // Create the region if it doesn't exist
+        DVBSubtitlePage::Coords coords;
+        coords.x = (segmentData[parsePos + 2] << 8) +
+                    segmentData[parsePos + 3];
+        coords.y = (segmentData[parsePos + 4] << 8) +
+                    segmentData[parsePos + 5];
+        page.pendingDisplay[regionID] = coords;
+        parsePos += 6;
+      }
+      break;
+    }
+    case 0x11: // Region composition
+    {
+      if (segmentLength < 10) break;
+      UCHAR regionID = segmentData[0];
+      page.regions[regionID]; // Create the region if it doesn't exist
+      DVBSubtitlePage::RegionMap::iterator regionEntry;
+      regionEntry = page.regions.find(regionID);
+      if (regionEntry == page.regions.end()) return false;
+      DVBSubtitleRegion& region = regionEntry->second;
+      UCHAR regionVersion = (segmentData[1] & 0xF0) >> 4;
+      if (regionVersion == region.version) break; // No update
+      region.version = regionVersion;
+      bool regionFillFlag = (segmentData[1] & 0x08) >> 3;
+      UINT regionWidth  = (segmentData[2] << 8) + segmentData[3];
+      UINT regionHeight = (segmentData[4] << 8) + segmentData[5];
+      region.setSize(regionWidth, regionHeight);
+      region.level = (segmentData[6] & 0xE0) >> 5;
+      region.setDepth((segmentData[6] & 0x1C) >> 2);
+      region.CLUTid = segmentData[7];
+      page.CLUTs[region.CLUTid]; // Create the CLUT if it doesn't exist
+      DVBSubtitlePage::CLUTMap::iterator clut;
+      clut = page.CLUTs.find(region.CLUTid);
+      if (clut == page.CLUTs.end()) return false;
+      region.palette = clut->second.getPalette(region.palette.getBpp());
+      if (regionFillFlag) switch (region.palette.getBpp())
+      {
+        case 2: region.setAllIndices((segmentData[9] & 0x0C) >> 2); break;
+        case 4: region.setAllIndices((segmentData[9] & 0xF0) >> 4); break;
+        case 8: region.setAllIndices(segmentData[8]); break;
+      }
+      region.objects.clear();
+      UINT parsePos = 10;
+      while (segmentLength > parsePos + 5)
+      {
+        UINT objectID = (segmentData[parsePos] << 8) +
+                         segmentData[parsePos + 1];
+        DVBSubtitleRegion::ObjectRef objref;
+        objref.objectID = objectID;
+        UINT objectType = (segmentData[parsePos + 2] & 0xC0) >> 6;
+        objref.horizontalPosition = ((segmentData[parsePos + 2] & 0x0F) << 8) +
+                                      segmentData[parsePos + 3];
+        objref.verticalPosition = ((segmentData[parsePos + 4] & 0x0F) << 8) +
+                                    segmentData[parsePos + 5];
+        if (objectType == 0) region.objects.push_back(objref);
+        parsePos += 6;
+      }
+      break; // case 0x11
+    }
+    case 0x12: // CLUT definition
+    {
+      if (segmentLength < 2) break;
+      UCHAR CLUTid = segmentData[0];
+      DVBSubtitlePage::CLUTMap::iterator clutEntry;
+      clutEntry = page.CLUTs.find(CLUTid);
+      if (clutEntry == page.CLUTs.end()) return false;
+      DVBSubtitleCLUT& clut = clutEntry->second;
+      clut.CLUTid = CLUTid;
+      UCHAR clutVersion = (segmentData[1] & 0xF0) >> 4;
+      if (clutVersion == clut.version) break; // No update
+      clut.version = clutVersion;
+      UINT parsePos = 2;
+      while (segmentLength > parsePos + 3)
+      {
+        UCHAR clutEntryID = segmentData[parsePos];
+        bool fullRangeFlag = segmentData[parsePos + 1] & 0x01;
+        UCHAR Y, Cr, Cb, T;
+        if (fullRangeFlag)
+        {
+          if (segmentLength <= parsePos + 5) break;
+          Y  = segmentData[parsePos + 2];
+          Cr = segmentData[parsePos + 3];
+          Cb = segmentData[parsePos + 4];
+          T  = segmentData[parsePos + 5];
+        }
+        else
+        {
+          Y   =  segmentData[parsePos + 2] & 0xFC;
+          Cr  = (segmentData[parsePos + 2] & 0x03) << 6;
+          Cr |= (segmentData[parsePos + 3] & 0xC0) >> 2;
+          Cb  = (segmentData[parsePos + 3] & 0x3C) << 2;
+          T   = (segmentData[parsePos + 3] & 0x03) << 6;
+        }
+        UCHAR entryFlags = segmentData[parsePos + 1];
+        if (entryFlags & 0x80) clut.setEntry(2, clutEntryID, Y,Cr,Cb,T);
+        if (entryFlags & 0x40) clut.setEntry(4, clutEntryID, Y,Cr,Cb,T);
+        if (entryFlags & 0x20) clut.setEntry(8, clutEntryID, Y,Cr,Cb,T);
+        parsePos += fullRangeFlag ? 6 : 4;
+      }
+      page.updateRegionPalettes(clut);
+      break; // case 0x12
+    }
+    case 0x13: // Object data
+    {
+      if (segmentLength < 3) break;
+      UINT objectID = (segmentData[0] << 8) + segmentData[1];
+      DVBSubtitleObject object(objectID);
+      object.findWhereUsed(page);
+      UCHAR codingMethod = (segmentData[2] & 0x0C) >> 2;
+      object.setNonModColourFlag(segmentData[2] & 0x01);
+      if (codingMethod == 0x00) // Coding of pixels
+      {
+        if (segmentLength < 7) break;
+        UINT topFieldLen     = (segmentData[3] << 8) + segmentData[4];
+        UINT bottomFieldLen  = (segmentData[5] << 8) + segmentData[6];
+        if (segmentLength < 7 + topFieldLen + bottomFieldLen) break;
+        object.decodeSubBlock(segmentData + 7, topFieldLen, true);
+        if (bottomFieldLen)
+          object.decodeSubBlock(segmentData + 7 + topFieldLen,
+                                bottomFieldLen, false);
+        else
+          object.decodeSubBlock(segmentData + 7, topFieldLen, false);
+      }
+      else if (codingMethod == 0x01) // Coded as a string of characters
+        ; // TODO
+      break;
+    }
+    case 0x14: // Display definition
+      break; // TODO: Ignore now and assume 720x576 per ETSI EN 300 743 V1.2.1
+    case 0x80: // End of display set
+      finishPage(page);
+      page.dirty = false;
+      page.currentDisplay = page.pendingDisplay;
+      break;
+    }
+    segmentOffset += (segmentLength + 6);
+  }
+  return false; // Fall through from while loop: we ran out of data
+}
+
+void DVBSubtitles::finishPage(const DVBSubtitlePage& page)
+{
+  if (page.dirty)
+  {
+    osd->clearOSD();
+  }
+
+  DVBSubtitlePage::DisplayMap::const_iterator i,j;
+  for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
+  { // For each currently displayed region, if the region has been
+    // moved or removed, blank out its previous screen position
+    j = page.pendingDisplay.find(i->first);
+    if (j == page.pendingDisplay.end() ||
+        j->second.x != i->second.x ||
+        j->second.y != i->second.y)
+    {
+      DVBSubtitlePage::RegionMap::const_iterator region_iter;
+      region_iter = page.regions.find(i->first);
+      if (region_iter == page.regions.end()) continue;
+      Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Clear region %d", i->first);
+      osd->clearOSDArea(i->second.x, i->second.y,
+           region_iter->second.getWidth(), region_iter->second.getHeight());
+    }
+  }
+
+  for (i = page.pendingDisplay.begin(); i != page.pendingDisplay.end(); ++i)
+  { // Display each pending region
+    DVBSubtitlePage::RegionMap::const_iterator region_iter;
+    region_iter = page.regions.find(i->first);
+    if (region_iter == page.regions.end()) continue;
+    Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Display region %d", i->first);
+    osd->drawOSDBitmap(i->second.x, i->second.y, region_iter->second);
+  }
+}
+
+void DVBSubtitles::lockInput()
+{
+#ifndef WIN32
+  pthread_mutex_lock(&input_mutex);
+#else
+  WaitForSingleObject(input_mutex, INFINITE);
+#endif
+}
+
+void DVBSubtitles::unlockInput()
+{
+#ifndef WIN32
+  pthread_mutex_unlock(&input_mutex);
+#else
+  ReleaseMutex(input_mutex);
+#endif
+}
+
+void DVBSubtitles::lockOutput()
+{
+#ifndef WIN32
+  pthread_mutex_lock(&output_mutex);
+#else
+  WaitForSingleObject(output_mutex, INFINITE);
+#endif
+}
+
+void DVBSubtitles::unlockOutput()
+{
+#ifndef WIN32
+  pthread_mutex_unlock(&output_mutex);
+#else
+  ReleaseMutex(output_mutex);
+#endif
+}
+
+int DVBSubtitles::start()
+{
+  lockInput();
+  running = true;
+  unlockInput();
+  return threadStart();
+}
+
+void DVBSubtitles::stop()
+{
+  threadStop();
+  lockInput();
+  running = false;
+  worklist.clear();
+  unlockInput();
+  lockOutput();
+  PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
+  if (pageEntry != pages.end())
+  {
+    const DVBSubtitlePage& page = pageEntry->second;
+    DVBSubtitlePage::DisplayMap::const_iterator i;
+    for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
+    {
+      DVBSubtitlePage::RegionMap::const_iterator region_iter;
+      region_iter = page.regions.find(i->first);
+      if (region_iter == page.regions.end()) continue;
+      osd->clearOSDArea(i->second.x, i->second.y,
+           region_iter->second.getWidth(), region_iter->second.getHeight());
+    }
+  }
+  pages.clear();
+  pageOnDisplay = 65536;
+  unlockOutput();
+  threadNudged = false;
+}
+
+void DVBSubtitles::show()
+{
+  lockOutput();
+  showing = true;
+  unlockOutput();
+}
+
+void DVBSubtitles::hide()
+{
+  lockOutput();
+  showing = false;
+  PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
+  if (pageEntry != pages.end())
+  {
+    const DVBSubtitlePage& page = pageEntry->second;
+    DVBSubtitlePage::DisplayMap::const_iterator i;
+    for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
+    {
+      DVBSubtitlePage::RegionMap::const_iterator region_iter;
+      region_iter = page.regions.find(i->first);
+      if (region_iter == page.regions.end()) continue;
+      osd->clearOSDArea(i->second.x, i->second.y,
+           region_iter->second.getWidth(), region_iter->second.getHeight());
+    }
+  }
+  pages.clear();
+  pageOnDisplay = 65536;
+  unlockOutput();
+}
+
+void DVBSubtitles::nudge()
+{
+  pthread_mutex_lock(&threadCondMutex);
+  threadNudged = true;
+  pthread_cond_signal(&threadCond);
+  pthread_mutex_unlock(&threadCondMutex);
+}
+
+void DVBSubtitles::threadMethod()
+{
+  struct timespec sleeptime;
+  sleeptime.tv_sec = 0;
+  sleeptime.tv_nsec = 0;
+  while (1)
+  {
+    threadCheckExit();
+    threadLock();
+    if (!threadNudged)
+    { // We have done the current work and no more has arrived. Sleep.
+      if (sleeptime.tv_sec == 0 && sleeptime.tv_nsec == 0)
+      {
+        Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping until nudged.");
+        threadWaitForSignal();
+      }
+      else
+      {
+        Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping for %d and %d", sleeptime.tv_sec, sleeptime.tv_nsec);
+        struct timespec targetTime;
+#ifndef WIN32
+        clock_gettime(CLOCK_REALTIME, &targetTime);
+#else
+        SYSTEMTIME systime;
+        __int64  filetime;
+        __int64  test;
+        GetSystemTime(&systime);
+        SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
+        targetTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
+        targetTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
+#endif
+        targetTime.tv_nsec += sleeptime.tv_nsec;
+        if (targetTime.tv_nsec > 999999999)
+        {
+          targetTime.tv_sec += 1;
+          targetTime.tv_nsec -= 1000000000;
+        }
+        targetTime.tv_sec += sleeptime.tv_sec;
+        threadWaitForSignalTimed(&targetTime);
+      }
+    }
+    threadNudged = false;
+    threadUnlock();
+    bool finished = false;
+    ULLONG nowPTS, pktPTS;
+    while (!finished)
+    {
+      threadCheckExit();
+      finished = true;
+      pktPTS = PESPacket::PTS_INVALID;
+      lockInput();
+      if (!worklist.empty())
+        pktPTS = worklist.front().getPTS();
+      unlockInput();
+      if (pktPTS != PESPacket::PTS_INVALID)
+      { // An entry exists in the work list
+        nowPTS = Video::getInstance()->getCurrentTimestamp();
+        if (PTSDifference(nowPTS, pktPTS) < 2*90000 ||
+            PTSDifference(pktPTS, nowPTS) < 4000)
+        { // It is due for processing or discarding.
+          finished = false;
+          lockInput();
+          if (!worklist.empty())
+          {
+            PESPacket packet = worklist.front();
+            worklist.pop_front();
+            unlockInput();
+            lockOutput();
+            if (showing) decodePacket(packet);
+            unlockOutput();
+          }
+          else unlockInput();
+        }
+        else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
+        { // Seems like a bad or very old entry. Get rid of it.
+          finished = false;
+          lockInput();
+          if (!worklist.empty()) worklist.pop_front();
+          unlockInput();
+        }
+      }
+    }
+    // Calculate how long to sleep for
+    sleeptime.tv_sec = 0;
+    sleeptime.tv_nsec = 0;
+    if (pktPTS != PESPacket::PTS_INVALID)
+    { // A future action exists
+      nowPTS = Video::getInstance()->getCurrentTimestamp();
+      ULLONG diff = PTSDifference(pktPTS, nowPTS);
+      sleeptime.tv_sec = diff / 90000;
+      sleeptime.tv_nsec = (long)(diff % 90000) * 10000L / 90000L * 100000L;
+      if (sleeptime.tv_sec > 60)
+      { // We have a problem. An action so far in the future should have
+        // been culled. Probably the action is already due and PTSDifference
+        // wrapped around. Therefore we sleep for a minimal time instead.
+        sleeptime.tv_sec = 0;
+        sleeptime.tv_nsec = 1;
+      }
+    }
+  }
+}
diff --git a/dvbsubtitles.h b/dvbsubtitles.h
new file mode 100644 (file)
index 0000000..71531c7
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+    Copyright 2008 Mark Calderbank
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef DVBSUBTITLES_H
+#define DVBSUBTITLES_H
+
+#ifdef WIN32
+#include "threadwin.h"
+#else
+#include "threadp.h"
+#endif
+#include "bitmap.h"
+#include "demuxer.h"
+#include <ctime>
+#include <vector>
+#include <deque>
+#include <map>
+
+class OSDReceiver;
+
+class DVBSubtitleCLUT
+{
+  public:
+    DVBSubtitleCLUT();
+    UCHAR CLUTid;
+    UCHAR version;
+    void setEntry(int bpp, UINT index, UCHAR Y, UCHAR Cr, UCHAR Cb, UCHAR T);
+    const Palette& getPalette(int bpp) const;
+  private:
+    Palette palette2;
+    Palette palette4;
+    Palette palette8;
+    const static ULONG defaultPalette2[4];
+    const static ULONG defaultPalette4[16];
+    const static ULONG defaultPalette8[256];
+};
+
+class DVBSubtitleRegion : public Bitmap
+{
+  public:
+    DVBSubtitleRegion();
+    UCHAR version;
+    UCHAR level;
+    UCHAR CLUTid;
+    void setDepth(UINT);
+    struct ObjectRef
+    {
+      UINT objectID;
+      UINT horizontalPosition;
+      UINT verticalPosition;
+    };
+    typedef std::vector<struct ObjectRef> ObjectList;
+    ObjectList objects;
+};
+
+class DVBSubtitlePage
+{
+  public:
+    DVBSubtitlePage();
+    ULLONG pts;
+    bool dirty;
+    UCHAR version;
+    UINT timeout;
+    UCHAR state;
+    bool serviceAcquired;
+    typedef std::map<UCHAR, DVBSubtitleCLUT> CLUTMap;
+    CLUTMap CLUTs;
+    typedef std::map<UCHAR, DVBSubtitleRegion> RegionMap;
+    RegionMap regions;
+    struct Coords { UINT x; UINT y; };
+    typedef std::map<UCHAR, Coords> DisplayMap;
+    DisplayMap currentDisplay;
+    DisplayMap pendingDisplay;
+
+    void setState(UCHAR s);
+    void updateRegionPalettes(const DVBSubtitleCLUT&);
+};
+
+class DVBSubtitles : public Thread_TYPE
+{
+  public:
+    DVBSubtitles(OSDReceiver* tosd = NULL);
+    ~DVBSubtitles() {}
+    void put(const PESPacket& packet);
+    int  start();
+    void stop();
+    void show();
+    void hide();
+
+  private:
+    OSDReceiver* osd;
+    std::deque<PESPacket> worklist;
+    typedef std::map<UINT, DVBSubtitlePage> PageMap;
+    PageMap pages;
+    UINT pageOnDisplay;
+    bool decodePacket(const PESPacket&);
+    void finishPage(const DVBSubtitlePage&);
+
+    bool running;
+    bool showing;
+    bool threadNudged;
+    void nudge();
+    void lockInput();
+    void unlockInput();
+    void lockOutput();
+    void unlockOutput();
+    void threadMethod();
+    void threadPostStopCleanup() {};
+#ifndef WIN32
+    pthread_mutex_t input_mutex;
+    pthread_mutex_t output_mutex;
+#else
+    HANDLE input_mutex;
+    HANDLE output_mutex;
+#endif
+};
+
+#endif
index 5fb9fcbb6f13548b518a04be5b7b71f44229c9dd..150d416c5fe7fe9bccdc547aa98cadc65b0cec1d 100644 (file)
@@ -19,4 +19,5 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o     \
            eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o           \
            vvideolivetv.o vsleeptimer.o                                       \
            playerlivetv.o playerliveradio.o                                   \
-           wprogressbar.o
+           wprogressbar.o                                                     \
+           bitmap.o dvbsubtitles.o
diff --git a/osdreceiver.h b/osdreceiver.h
new file mode 100644 (file)
index 0000000..4fb350d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+    Copyright 2008 Mark Calderbank
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+#ifndef OSDRECEIVER_H
+#define OSDRECEIVER_H
+
+class Bitmap;
+
+class OSDReceiver
+{
+  public:
+    virtual void drawOSDBitmap(UINT posX, UINT posY, const Bitmap&)=0;
+    virtual void clearOSD()=0;
+    virtual void clearOSDArea(UINT posX, UINT posY, UINT width, UINT height)=0;
+};
+
+#endif
index db3e01a6d92591d0c7d9c56a459b84e0d25e70b0..774cda9ce48eed7af2d01d26ebf7ee8f067a2972 100644 (file)
--- a/player.cc
+++ b/player.cc
 #include "messagequeue.h"
 #include "remote.h"
 #include "message.h"
+#include "dvbsubtitles.h"
+#include "osdreceiver.h"
 
 #define USER_RESPONSE_TIME 500 // Milliseconds
 
 // ----------------------------------- Called from outside, one offs or info funcs
 
-Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver)
+Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
 : vfeed(this), afeed(this)
 {
   messageQueue = tmessageQueue;
   messageReceiver = tmessageReceiver;
+  osdReceiver = tosdReceiver;
   audio = Audio::getInstance();
   video = Video::getInstance();
   logger = Log::getInstance();
@@ -50,6 +53,8 @@ Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver)
   state = S_STOP;
   ifactor = 4;
 
+  subtitlesShowing = false;
+
   videoStartup = false;
   threadBuffer = NULL;
 
@@ -74,8 +79,10 @@ int Player::init()
 
   demuxer = new DemuxerVDR();
   if (!demuxer) return 0;
+  subtitles = new DVBSubtitles(osdReceiver);
+  if (!subtitles) return 0;
 
-  if (!demuxer->init(this, audio, video, 2097152, 524288))
+  if (!demuxer->init(this, audio, video, 2097152, 524288, subtitles))
   {
     logger->log("Player", Log::ERR, "Demuxer failed to init");
     shutdown();
@@ -101,6 +108,8 @@ int Player::shutdown()
 
   delete demuxer;
   demuxer = NULL;
+  delete subtitles;
+  subtitles = NULL;
 
 #ifdef WIN32
   CloseHandle(mutex);
@@ -168,6 +177,21 @@ void Player::setAudioChannel(int newChannel, int type)
     demuxer->setAudioChannel(newChannel);
 }
 
+bool Player::toggleSubtitles()
+{
+  if (!subtitlesShowing)
+  {
+    subtitlesShowing = true;
+    subtitles->show();
+  }
+  else
+  {
+    subtitlesShowing = false;
+    subtitles->hide();
+  }
+  return subtitlesShowing;
+}
+
 // ----------------------------------- Externally called events
 
 void Player::play()
@@ -264,7 +288,7 @@ void Player::fastBackward()
 void Player::jumpToPercent(double percent)
 {
   lock();
-  logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
+  logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
   ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
   switchState(S_JUMP, newFrame);
 //  unLock(); - let thread unlock this
@@ -347,6 +371,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           threadStop();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           demuxer->flush();
           state = S_FFWD;
           threadStart();
@@ -359,6 +384,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           threadStop();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           demuxer->flush();
           state = S_FBWD;
           threadStart();
@@ -368,6 +394,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
         {
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           threadStop();
           video->stop();
           video->blank();
@@ -389,6 +416,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           threadStop();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           demuxer->flush();
           state = S_PAUSE_I;
           video->reset();
@@ -423,6 +451,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           audio->systemMuteOn();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           threadStop();
           video->unPause();
           audio->unPause();
@@ -436,6 +465,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           audio->systemMuteOn();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           threadStop();
           video->unPause();
           audio->unPause();
@@ -447,6 +477,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
         {
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           threadStop();
           video->stop();
           video->blank();
@@ -473,6 +504,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           threadStop();
           vfeed.stop();
           afeed.stop();
+          subtitles->stop();
           demuxer->flush();
           state = S_PAUSE_I;
           video->reset();
@@ -680,6 +712,7 @@ void Player::switchState(UCHAR toState, ULONG jumpFrame)
           logger->log("Player", Log::DEBUG, "Immediate play");
           afeed.start();
           vfeed.start();
+          subtitles->start();
           video->sync();
           audio->sync();
           audio->play();
@@ -748,6 +781,7 @@ void Player::restartAtFrame(ULONG newFrame)
 {
   vfeed.stop();
   afeed.stop();
+  subtitles->stop();
   threadStop();
   video->stop();
   video->reset();
@@ -760,6 +794,7 @@ void Player::restartAtFrame(ULONG newFrame)
   videoStartup = true;
   afeed.start();
   vfeed.start();
+  subtitles->start();
   threadStart();
   audio->play();
   video->sync();
@@ -909,6 +944,7 @@ void Player::threadMethod()
     videoStartup = true;
     afeed.start();
     vfeed.start();
+    subtitles->start();
     audio->play();
     audio->sync();
     audio->systemMuteOff();
index 51af5916f54ec604fca0e635e62a82a88b7d44f6..b009956b7a11f723be66dc119289c126829816bc 100644 (file)
--- a/player.h
+++ b/player.h
@@ -45,11 +45,13 @@ class Video;
 class VDR;
 class Log;
 class Demuxer;
+class OSDReceiver;
+class DVBSubtitles;
 
 class Player : public Thread_TYPE, public Callback
 {
   public:
-    Player(MessageQueue* messageQueue, void* messageReceiver);
+    Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver);
     virtual ~Player();
 
     int init();
@@ -58,6 +60,7 @@ class Player : public Thread_TYPE, public Callback
     void setLengthBytes(ULLONG length);
     void setLengthFrames(ULONG length);
     void setAudioChannel(int newChannel, int type);
+    bool toggleSubtitles();
 
     void play();
     void stop();
@@ -118,12 +121,15 @@ class Player : public Thread_TYPE, public Callback
     void restartAtFrame(ULONG newFrame);
     void restartAtFramePI(ULONG newFrame);
 
+    bool subtitlesShowing;
     MessageQueue* messageQueue;
     void* messageReceiver;
+    OSDReceiver* osdReceiver;
     Log* logger;
     Audio* audio;
     Video* video;
     Demuxer* demuxer;
+    DVBSubtitles* subtitles;
     VDR* vdr;
     VFeed vfeed;
     AFeed afeed;
index 856400417f113bb1609bd0e483b0673f601e6397..1a725117217ac02ce6781197df036a927801cae1 100644 (file)
--- a/surface.h
+++ b/surface.h
@@ -39,6 +39,8 @@ extern osd_font_t font_CaslonRoman_1_25;
 extern osd_font_t font_helvB24;
 extern osd_font_t font_helvB18;
 
+class Bitmap;
+
 class Surface
 {
   public:
@@ -61,6 +63,7 @@ class Surface
     virtual void drawPixel(int x, int y, unsigned int c)=0;
     virtual void drawHorzLine(int x1, int x2, int y, unsigned int c)=0;
     virtual void drawVertLine(int x, int y1, int y2, unsigned int c)=0;
+    virtual void drawBitmap(int x, int y, const Bitmap& bm)=0;
     virtual int updateToScreen(int sx, int sy, int w, int h, int dx, int dy)=0;
     virtual void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)=0;
     virtual void screenShot(char* fileName)=0;
index e7d8f5d8e45407f50d8b8ba9c3c9bec86c1497a8..e946ffdf9f869983631968a920b67d0054269b05 100644 (file)
@@ -22,6 +22,7 @@
 #include "surfacemvp.h"
 
 #include "osd.h"
+#include "bitmap.h"
 #include "log.h"
 
 SurfaceMVP::SurfaceMVP(int id)
@@ -264,6 +265,67 @@ void SurfaceMVP::drawVertLine(int x, int y1, int y2, unsigned int c)
   fillblt(x, y1, 1, y2-y1, c);
 }
 
+void SurfaceMVP::drawBitmap(int x, int y, const Bitmap& bm)
+{
+  UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight();
+  if (bmw == 0 || bmh == 0) return;
+  if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return;
+  int remainder = (surface.sfc.width % 4);
+  UINT line;
+  if (remainder == 0)
+    line = surface.sfc.width;
+  else
+    line = surface.sfc.width + (4 - remainder);
+  const std::vector<UCHAR>& bmdata = bm.rawData();
+  const std::vector<UCHAR>& Y = bm.palette.getYVector();
+  const std::vector<UCHAR>& Cr = bm.palette.getCrVector();
+  const std::vector<UCHAR>& Cb = bm.palette.getCbVector();
+  const std::vector<UCHAR>& A = bm.palette.getAVector();
+  UINT b_offset = 0;
+  UINT s_offset = x + y*line;
+  UINT plotWidth = bmw;
+  UINT plotHeight = bmh;
+  if (x + plotWidth - 1 > surface.sfc.width)
+    plotWidth = surface.sfc.width - x + 1;
+  if (y + plotHeight - 1 > surface.sfc.height)
+    plotHeight = surface.sfc.height - y + 1;
+  for (UINT j = 0; j < plotHeight; ++j)
+  {
+    UINT i = 0;
+    if (x & 1) // odd x - need to plot first column separately
+    {
+      UCHAR index = bmdata[b_offset];
+      *(surface.base[0] + s_offset) = Y[index];
+      *(surface.base[1] + s_offset - 1) = Cb[index];
+      *(surface.base[1] + s_offset) = Cr[index];
+      *(surface.base[2] + s_offset) = A[index];
+      i = 1;
+    }
+    // Now, plot pairs of pixels with averaged chroma values
+    while (i < plotWidth - 1)
+    {
+      UCHAR index1 = bmdata[b_offset + i];
+      UCHAR index2 = bmdata[b_offset + i + 1];
+      *(surface.base[0] + s_offset + i) = Y[index1];
+      *(surface.base[0] + s_offset + i + 1) = Y[index2];
+      *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2;
+      *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2;
+      *(surface.base[2] + s_offset + i) = A[index1];
+      *(surface.base[2] + s_offset + i + 1) = A[index2];
+      i += 2;
+    }
+    if (i == plotWidth - 1) // One column left to do
+    {
+      UCHAR index = bmdata[b_offset + i];
+      *(surface.base[0] + s_offset + i) = Y[index];
+      *(surface.base[1] + s_offset + i) = Cb[index];
+      *(surface.base[1] + s_offset + i + 1) = Cr[index];
+      *(surface.base[2] + s_offset + i) = A[index];
+    }
+    s_offset += line;
+    b_offset += bmw;
+  }
+}
 
   /* surface update to screen needs:
   source x distance into this surface
index 3e4bb87c24c96b67465c51dad9cd078858e7455e..f71908c6117fff0d701755846ed831911c4c6f3a 100644 (file)
@@ -164,6 +164,7 @@ class SurfaceMVP : public Surface
     void drawPixel(int x, int y, unsigned int c);
     void drawHorzLine(int x1, int x2, int y, unsigned int c);
     void drawVertLine(int x, int y1, int y2, unsigned int c);
+    void drawBitmap(int x, int y, const Bitmap& bm);
     int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);
     void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);
     void screenShot(char* fileName);
index 2707bd7c52fa394df023cc3c8217010017e2bc83..6b52b731ad231b0171d3b3a3b6e16c77386ced16 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "surfacewin.h"
 #include "osdwin.h"
+#include "bitmap.h"
 #include "log.h"
 
 SurfaceWin::SurfaceWin(int id)
@@ -215,6 +216,14 @@ void SurfaceWin::drawVertLine(int x, int y1, int y2, unsigned int c)
   fillblt(x, y1, 1, y2-y1, c);
 }
 
+void SurfaceWin::drawBitmap(int x, int y, const Bitmap& bm)
+{
+  // Temporary code? Draw one pixel at a time using drawPixel()
+  for (UINT j = 0; j < bm.getHeight(); ++j)
+    for (UINT i = 0; i < bm.getWidth(); ++i)
+      drawPixel(x+i, y+j, bm.getColour(i,j));
+}
+
 int SurfaceWin::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME
 {
   WaitForSingleObject(event,INFINITE); //since this might be called before surface
index baa62aae4a2bb64ad7e502a57f4758229e25ac2a..40f8736496bb9b6bab7f30a36758e4c641aefa55 100644 (file)
@@ -42,6 +42,7 @@ class SurfaceWin : public Surface
     void drawPixel(int x, int y, unsigned int c);
     void drawHorzLine(int x1, int x2, int y, unsigned int c);
     void drawVertLine(int x, int y1, int y2, unsigned int c);
+    void drawBitmap(int x, int y, const Bitmap& bm);
     int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);
     void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);
     void screenShot(char* fileName);
index e9c0b219026e7719b6f4febf607dfb08013eb68b..308d75623b8b270c07fff1bb19325e5893f46583 100644 (file)
@@ -35,6 +35,7 @@
 #include "boxstack.h"
 #include "vinfo.h"
 #include "i18n.h"
+#include "bitmap.h"
 
 VVideoRec::VVideoRec(Recording* rec)
 {
@@ -45,7 +46,7 @@ VVideoRec::VVideoRec(Recording* rec)
   vas = NULL;
   vsummary = NULL;
 
-  player = new Player(Command::getInstance(), this);
+  player = new Player(Command::getInstance(), this, this);
   player->init();
 
   videoMode = video->getMode();
@@ -93,8 +94,6 @@ VVideoRec::VVideoRec(Recording* rec)
   clocksRegion.y = barRegion.y + 12;
   clocksRegion.w = 170;
   clocksRegion.h = surface->getFontHeight();
-
-
 //  barBlue.set(0, 0, 150, 150);
   barBlue.set(0, 0, 0, 128);
 
@@ -368,6 +367,7 @@ int VVideoRec::handleCommand(int command)
     case Remote::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
     case Remote::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
 
+    case Remote::RECORD: player->toggleSubtitles(); return 2;
 #ifdef DEV
 //    case Remote::RED:
 //    {
@@ -864,3 +864,24 @@ void VVideoRec::removeSummary()
   }
 }
 
+void VVideoRec::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm)
+{
+  drawBitmap(posX, posY, bm);
+  Region r;
+  r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
+  boxstack->update(this, &r);
+}
+
+void VVideoRec::clearOSD()
+{
+  rectangle(area, transparent);
+  boxstack->update(this, &area);
+}
+
+void VVideoRec::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height)
+{
+  Region r;
+  r.x = posX; r.y = posY; r.w = width; r.h = height;
+  rectangle(r, transparent);
+  boxstack->update(this, &r);
+}
index 36f5231482c2b224ff3b45033e6ac95ac14e2e9f..8c89b74bd0a0e63053768fe457de4be540861761 100644 (file)
@@ -28,6 +28,7 @@
 #include "wwss.h"
 #include "region.h"
 #include "colour.h"
+#include "osdreceiver.h"
 
 class VDR;
 class Video;
@@ -38,10 +39,11 @@ class VAudioSelector;
 class Message;
 class BoxStack;
 class VInfo;
+class Bitmap;
 
 //#include "vepg.h" // for testing EPG in NTSC with a NTSC test video
 
-class VVideoRec : public Boxx, public TimerReceiver
+class VVideoRec : public Boxx, public TimerReceiver, public OSDReceiver
 {
   public:
     VVideoRec(Recording* rec);
@@ -53,6 +55,10 @@ class VVideoRec : public Boxx, public TimerReceiver
     void timercall(int clientReference);
     void processMessage(Message* m);
 
+    void drawOSDBitmap(UINT posX, UINT posY, const Bitmap&);
+    void clearOSD();
+    void clearOSDArea(UINT posX, UINT posY, UINT width, UINT height);
+
   private:
     BoxStack* boxstack;
     VDR* vdr;