vdr  1.7.27
dvbsubtitle.c
Go to the documentation of this file.
00001 /*
00002  * dvbsubtitle.c: DVB subtitles
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * Original author: Marco Schluessler <marco@lordzodiac.de>
00008  * With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
00009  *
00010  * $Id: dvbsubtitle.c 2.31 2012/03/16 11:56:56 kls Exp $
00011  */
00012 
00013 
00014 #include "dvbsubtitle.h"
00015 #define __STDC_FORMAT_MACROS // Required for format specifiers
00016 #include <inttypes.h>
00017 #include "device.h"
00018 #include "libsi/si.h"
00019 
00020 //#define FINISHPAGE_HACK
00021 
00022 #define PAGE_COMPOSITION_SEGMENT    0x10
00023 #define REGION_COMPOSITION_SEGMENT  0x11
00024 #define CLUT_DEFINITION_SEGMENT     0x12
00025 #define OBJECT_DATA_SEGMENT         0x13
00026 #define DISPLAY_DEFINITION_SEGMENT  0x14
00027 #define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156
00028 #define END_OF_DISPLAY_SET_SEGMENT  0x80
00029 #define STUFFING_SEGMENT            0xFF
00030 
00031 // Set these to 'true' for debug output:
00032 static bool DebugConverter = false;
00033 static bool DebugSegments = false;
00034 static bool DebugPages = false;
00035 static bool DebugRegions = false;
00036 static bool DebugObjects = false;
00037 static bool DebugCluts = false;
00038 
00039 #define dbgconverter(a...) if (DebugConverter) fprintf(stderr, a)
00040 #define dbgsegments(a...) if (DebugSegments) fprintf(stderr, a)
00041 #define dbgpages(a...) if (DebugPages) fprintf(stderr, a)
00042 #define dbgregions(a...) if (DebugRegions) fprintf(stderr, a)
00043 #define dbgobjects(a...) if (DebugObjects) fprintf(stderr, a)
00044 #define dbgcluts(a...) if (DebugCluts) fprintf(stderr, a)
00045 
00046 // --- cSubtitleClut ---------------------------------------------------------
00047 
00048 class cSubtitleClut : public cListObject {
00049 private:
00050   int clutId;
00051   int version;
00052   cPalette palette2;
00053   cPalette palette4;
00054   cPalette palette8;
00055 public:
00056   cSubtitleClut(int ClutId);
00057   int ClutId(void) { return clutId; }
00058   int Version(void) { return version; }
00059   void SetVersion(int Version) { version = Version; }
00060   void SetColor(int Bpp, int Index, tColor Color);
00061   const cPalette *GetPalette(int Bpp);
00062   };
00063 
00064 cSubtitleClut::cSubtitleClut(int ClutId)
00065 :palette2(2)
00066 ,palette4(4)
00067 ,palette8(8)
00068 {
00069   int a = 0, r = 0, g = 0, b = 0;
00070   clutId = ClutId;
00071   version = -1;
00072   // ETSI EN 300 743 10.3: 4-entry CLUT default contents
00073   palette2.SetColor(0, ArgbToColor(  0,   0,   0,   0));
00074   palette2.SetColor(1, ArgbToColor(255, 255, 255, 255));
00075   palette2.SetColor(2, ArgbToColor(255,   0,   0,   0));
00076   palette2.SetColor(3, ArgbToColor(255, 127, 127, 127));
00077   // ETSI EN 300 743 10.2: 16-entry CLUT default contents
00078   palette4.SetColor(0, ArgbToColor(0, 0, 0, 0));
00079   for (int i = 1; i < 16; ++i) {
00080       if (i < 8) {
00081          r = (i & 1) ? 255 : 0;
00082          g = (i & 2) ? 255 : 0;
00083          b = (i & 4) ? 255 : 0;
00084          }
00085       else {
00086          r = (i & 1) ? 127 : 0;
00087          g = (i & 2) ? 127 : 0;
00088          b = (i & 4) ? 127 : 0;
00089          }
00090       palette4.SetColor(i, ArgbToColor(255, r, g, b));
00091       }
00092   // ETSI EN 300 743 10.1: 256-entry CLUT default contents
00093   palette8.SetColor(0, ArgbToColor(0, 0, 0, 0));
00094   for (int i = 1; i < 256; ++i) {
00095       if (i < 8) {
00096          r = (i & 1) ? 255 : 0;
00097          g = (i & 2) ? 255 : 0;
00098          b = (i & 4) ? 255 : 0;
00099          a = 63;
00100          }
00101       else {
00102          switch (i & 0x88) {
00103            case 0x00:
00104                 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
00105                 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
00106                 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
00107                 a = 255;
00108                 break;
00109            case 0x08:
00110                 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
00111                 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
00112                 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
00113                 a = 127;
00114                 break;
00115            case 0x80:
00116                 r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
00117                 g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
00118                 b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
00119                 a = 255;
00120                 break;
00121            case 0x88:
00122                 r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
00123                 g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
00124                 b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
00125                 a = 255;
00126                 break;
00127             }
00128          }
00129       palette8.SetColor(i, ArgbToColor(a, r, g, b));
00130       }
00131 }
00132 
00133 void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color)
00134 {
00135   switch (Bpp) {
00136     case 2: palette2.SetColor(Index, Color); break;
00137     case 4: palette4.SetColor(Index, Color); break;
00138     case 8: palette8.SetColor(Index, Color); break;
00139     default: esyslog("ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)", Bpp, Index, Color);
00140     }
00141 }
00142 
00143 const cPalette *cSubtitleClut::GetPalette(int Bpp)
00144 {
00145   switch (Bpp) {
00146     case 2: return &palette2;
00147     case 4: return &palette4;
00148     case 8: return &palette8;
00149     default: esyslog("ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)", Bpp);
00150     }
00151   return &palette8;
00152 }
00153 
00154 // --- cSubtitleObject -------------------------------------------------------
00155 
00156 class cSubtitleObject : public cListObject {
00157 private:
00158   int objectId;
00159   int version;
00160   int codingMethod;
00161   bool nonModifyingColorFlag;
00162   uchar backgroundPixelCode;
00163   uchar foregroundPixelCode;
00164   int providerFlag;
00165   int px;
00166   int py;
00167   cBitmap *bitmap;
00168   char textData[Utf8BufSize(256)]; // number of character codes is an 8-bit field
00169   void DrawLine(int x, int y, tIndex Index, int Length);
00170   bool Decode2BppCodeString(cBitStream *bs, int&x, int y, const uint8_t *MapTable);
00171   bool Decode4BppCodeString(cBitStream *bs, int&x, int y, const uint8_t *MapTable);
00172   bool Decode8BppCodeString(cBitStream *bs, int&x, int y);
00173 public:
00174   cSubtitleObject(int ObjectId, cBitmap *Bitmap);
00175   int ObjectId(void) { return objectId; }
00176   int Version(void) { return version; }
00177   int CodingMethod(void) { return codingMethod; }
00178   uchar BackgroundPixelCode(void) { return backgroundPixelCode; }
00179   uchar ForegroundPixelCode(void) { return foregroundPixelCode; }
00180   const char *TextData(void) { return &textData[0]; }
00181   int X(void) { return px; }
00182   int Y(void) { return py; }
00183   bool NonModifyingColorFlag(void) { return nonModifyingColorFlag; }
00184   void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
00185   void DecodeSubBlock(const uchar *Data, int Length, bool Even);
00186   void SetVersion(int Version) { version = Version; }
00187   void SetBackgroundPixelCode(uchar BackgroundPixelCode) { backgroundPixelCode = BackgroundPixelCode; }
00188   void SetForegroundPixelCode(uchar ForegroundPixelCode) { foregroundPixelCode = ForegroundPixelCode; }
00189   void SetNonModifyingColorFlag(bool NonModifyingColorFlag) { nonModifyingColorFlag = NonModifyingColorFlag; }
00190   void SetCodingMethod(int CodingMethod) { codingMethod = CodingMethod; }
00191   void SetPosition(int x, int y) { px = x; py = y; }
00192   void SetProviderFlag(int ProviderFlag) { providerFlag = ProviderFlag; }
00193   };
00194 
00195 cSubtitleObject::cSubtitleObject(int ObjectId, cBitmap *Bitmap)
00196 {
00197   objectId = ObjectId;
00198   version = -1;
00199   codingMethod = -1;
00200   nonModifyingColorFlag = false;
00201   backgroundPixelCode = 0;
00202   foregroundPixelCode = 0;
00203   providerFlag = -1;
00204   px = py = 0;
00205   bitmap = Bitmap;
00206   memset(textData, 0, sizeof(textData));
00207 }
00208 
00209 void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
00210 {
00211   if (NumberOfCodes > 0) {
00212      bool singleByte;
00213      const uchar *from = &Data[1];
00214      int len = NumberOfCodes * 2 - 1;
00215      cCharSetConv conv(SI::getCharacterTable(from, len, &singleByte));
00216      if (singleByte) {
00217         char txt[NumberOfCodes + 1];
00218         char *p = txt;
00219         for (int i = 2; i < NumberOfCodes; ++i) {
00220             char c = Data[i * 2 + 1] & 0xFF;
00221             if (c == 0)
00222                break;
00223             if (' ' <= c && c <= '~' || c == '\n' || 0xA0 <= c)
00224                *(p++) = c;
00225             else if (c == 0x8A)
00226                *(p++) = '\n';
00227             }
00228         *p = 0;
00229         const char *s = conv.Convert(txt);
00230         Utf8Strn0Cpy(textData, s, Utf8StrLen(s));
00231         }
00232      else {
00233         // TODO: add proper multibyte support for "UTF-16", "EUC-KR", "GB2312", "GBK", "UTF-8"
00234         }
00235      }
00236 }
00237 
00238 void cSubtitleObject::DecodeSubBlock(const uchar *Data, int Length, bool Even)
00239 {
00240   int x = 0;
00241   int y = Even ? 0 : 1;
00242   uint8_t map2to4[ 4] = { 0x00, 0x07, 0x08, 0x0F };
00243   uint8_t map2to8[ 4] = { 0x00, 0x77, 0x88, 0xFF };
00244   uint8_t map4to8[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
00245   const uint8_t *mapTable = NULL;
00246   cBitStream bs(Data, Length * 8);
00247   while (!bs.IsEOF()) {
00248         switch (bs.GetBits(8)) {
00249           case 0x10:
00250                dbgobjects("2-bit / pixel code string\n");
00251                switch (bitmap->Bpp()) {
00252                  case 8:  mapTable = map2to8; break;
00253                  case 4:  mapTable = map2to4; break;
00254                  default: mapTable = NULL;    break;
00255                  }
00256                while (Decode2BppCodeString(&bs, x, y, mapTable) && !bs.IsEOF())
00257                      ;
00258                bs.ByteAlign();
00259                break;
00260           case 0x11:
00261                dbgobjects("4-bit / pixel code string\n");
00262                switch (bitmap->Bpp()) {
00263                  case 8:  mapTable = map4to8; break;
00264                  default: mapTable = NULL;    break;
00265                  }
00266                while (Decode4BppCodeString(&bs, x, y, mapTable) && !bs.IsEOF())
00267                      ;
00268                bs.ByteAlign();
00269                break;
00270           case 0x12:
00271                dbgobjects("8-bit / pixel code string\n");
00272                while (Decode8BppCodeString(&bs, x, y) && !bs.IsEOF())
00273                      ;
00274                break;
00275           case 0x20:
00276                dbgobjects("sub block 2 to 4 map\n");
00277                map2to4[0] = bs.GetBits(4);
00278                map2to4[1] = bs.GetBits(4);
00279                map2to4[2] = bs.GetBits(4);
00280                map2to4[3] = bs.GetBits(4);
00281                break;
00282           case 0x21:
00283                dbgobjects("sub block 2 to 8 map\n");
00284                for (int i = 0; i < 4; ++i)
00285                    map2to8[i] = bs.GetBits(8);
00286                break;
00287           case 0x22:
00288                dbgobjects("sub block 4 to 8 map\n");
00289                for (int i = 0; i < 16; ++i)
00290                    map4to8[i] = bs.GetBits(8);
00291                break;
00292           case 0xF0:
00293                dbgobjects("end of object line\n");
00294                x = 0;
00295                y += 2;
00296                break;
00297           default: dbgobjects("unknown sub block %s %d\n", __FUNCTION__, __LINE__);
00298           }
00299         }
00300 }
00301 
00302 void cSubtitleObject::DrawLine(int x, int y, tIndex Index, int Length)
00303 {
00304   if (nonModifyingColorFlag && Index == 1)
00305      return;
00306   x += px;
00307   y += py;
00308   for (int pos = x; pos < x + Length; pos++)
00309       bitmap->SetIndex(pos, y, Index);
00310 }
00311 
00312 bool cSubtitleObject::Decode2BppCodeString(cBitStream *bs, int &x, int y, const uint8_t *MapTable)
00313 {
00314   int rl = 0;
00315   int color = 0;
00316   uchar code = bs->GetBits(2);
00317   if (code) {
00318      color = code;
00319      rl = 1;
00320      }
00321   else if (bs->GetBit()) { // switch_1
00322      rl = bs->GetBits(3) + 3;
00323      color = bs->GetBits(2);
00324      }
00325   else if (bs->GetBit()) // switch_2
00326      rl = 1; //color 0
00327   else {
00328      switch (bs->GetBits(2)) { // switch_3
00329        case 0:
00330             return false;
00331        case 1:
00332             rl = 2; //color 0
00333             break;
00334        case 2:
00335             rl = bs->GetBits(4) + 12;
00336             color = bs->GetBits(2);
00337             break;
00338        case 3:
00339             rl = bs->GetBits(8) + 29;
00340             color = bs->GetBits(2);
00341             break;
00342        default: ;
00343        }
00344      }
00345   if (MapTable)
00346      color = MapTable[color];
00347   DrawLine(x, y, color, rl);
00348   x += rl;
00349   return true;
00350 }
00351 
00352 bool cSubtitleObject::Decode4BppCodeString(cBitStream *bs, int &x, int y, const uint8_t *MapTable)
00353 {
00354   int rl = 0;
00355   int color = 0;
00356   uchar code = bs->GetBits(4);
00357   if (code) {
00358      color = code;
00359      rl = 1;
00360      }
00361   else if (bs->GetBit() == 0) { // switch_1
00362      code = bs->GetBits(3);
00363      if (code)
00364         rl = code + 2; //color 0
00365      else
00366         return false;
00367      }
00368   else if (bs->GetBit() == 0) { // switch_2
00369      rl = bs->GetBits(2) + 4;
00370      color = bs->GetBits(4);
00371      }
00372   else {
00373      switch (bs->GetBits(2)) { // switch_3
00374        case 0: // color 0
00375             rl = 1;
00376             break;
00377        case 1: // color 0
00378             rl = 2;
00379             break;
00380        case 2:
00381             rl = bs->GetBits(4) + 9;
00382             color = bs->GetBits(4);
00383             break;
00384        case 3:
00385             rl = bs->GetBits(8) + 25;
00386             color = bs->GetBits(4);
00387             break;
00388        }
00389      }
00390   if (MapTable)
00391      color = MapTable[color];
00392   DrawLine(x, y, color, rl);
00393   x += rl;
00394   return true;
00395 }
00396 
00397 bool cSubtitleObject::Decode8BppCodeString(cBitStream *bs, int &x, int y)
00398 {
00399   int rl = 0;
00400   int color = 0;
00401   uchar code = bs->GetBits(8);
00402   if (code) {
00403      color = code;
00404      rl = 1;
00405      }
00406   else if (bs->GetBit()) {
00407      rl = bs->GetBits(7);
00408      color = bs->GetBits(8);
00409      }
00410   else {
00411      code = bs->GetBits(7);
00412      if (code)
00413         rl = code; // color 0
00414      else
00415         return false;
00416      }
00417   DrawLine(x, y, color, rl);
00418   x += rl;
00419   return true;
00420 }
00421 
00422 // --- cSubtitleRegion -------------------------------------------------------
00423 
00424 class cSubtitleRegion : public cListObject, public cBitmap {
00425 private:
00426   int regionId;
00427   int version;
00428   int clutId;
00429   int horizontalAddress;
00430   int verticalAddress;
00431   int level;
00432   int lineHeight;
00433   cList<cSubtitleObject> objects;
00434 public:
00435   cSubtitleRegion(int RegionId);
00436   int RegionId(void) { return regionId; }
00437   int Version(void) { return version; }
00438   int ClutId(void) { return clutId; }
00439   int Level(void) { return level; }
00440   int Depth(void) { return Bpp(); }
00441   void FillRegion(tIndex Index);
00442   cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
00443   int HorizontalAddress(void) { return horizontalAddress; }
00444   int VerticalAddress(void) { return verticalAddress; }
00445   void SetVersion(int Version) { version = Version; }
00446   void SetClutId(int ClutId) { clutId = ClutId; }
00447   void SetLevel(int Level);
00448   void SetDepth(int Depth);
00449   void SetHorizontalAddress(int HorizontalAddress) { horizontalAddress = HorizontalAddress; }
00450   void SetVerticalAddress(int VerticalAddress) { verticalAddress = VerticalAddress; }
00451   void UpdateTextData(cSubtitleClut *Clut);
00452   };
00453 
00454 cSubtitleRegion::cSubtitleRegion(int RegionId)
00455 :cBitmap(1, 1, 4)
00456 {
00457   regionId = RegionId;
00458   version = -1;
00459   clutId = -1;
00460   horizontalAddress = 0;
00461   verticalAddress = 0;
00462   level = 0;
00463   lineHeight = 26; // configurable subtitling font size
00464 }
00465 
00466 void cSubtitleRegion::FillRegion(tIndex Index)
00467 {
00468   dbgregions("FillRegion %d\n", Index);
00469   for (int y = 0; y < Height(); y++) {
00470       for (int x = 0; x < Width(); x++)
00471           SetIndex(x, y, Index);
00472       }
00473 }
00474 
00475 cSubtitleObject *cSubtitleRegion::GetObjectById(int ObjectId, bool New)
00476 {
00477   cSubtitleObject *result = NULL;
00478   for (cSubtitleObject *so = objects.First(); so; so = objects.Next(so)) {
00479       if (so->ObjectId() == ObjectId)
00480          result = so;
00481       }
00482   if (!result && New) {
00483      result = new cSubtitleObject(ObjectId, this);
00484      objects.Add(result);
00485      }
00486   return result;
00487 }
00488 
00489 void cSubtitleRegion::UpdateTextData(cSubtitleClut *Clut)
00490 {
00491   const cPalette *palette = Clut ? Clut->GetPalette(Depth()) : NULL;
00492   for (cSubtitleObject *so = objects.First(); so && palette; so = objects.Next(so)) {
00493       if (Utf8StrLen(so->TextData()) > 0) {
00494          cFont *font = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize);
00495          cBitmap tmp(font->Width(so->TextData()), font->Height(), Depth());
00496          double factor = (double)lineHeight / font->Height();
00497          tmp.DrawText(0, 0, so->TextData(), palette->Color(so->ForegroundPixelCode()), palette->Color(so->BackgroundPixelCode()), font);
00498          cBitmap *scaled = tmp.Scaled(factor, factor, true);
00499          DrawBitmap(so->X(), so->Y(), *scaled);
00500          delete scaled;
00501          delete font;
00502          }
00503       }
00504 }
00505 
00506 void cSubtitleRegion::SetLevel(int Level)
00507 {
00508   if (Level > 0 && Level < 4)
00509      level = 1 << Level;
00510 }
00511 
00512 void cSubtitleRegion::SetDepth(int Depth)
00513 {
00514   if (Depth > 0 && Depth < 4)
00515      SetBpp(1 << Depth);
00516 }
00517 
00518 // --- cDvbSubtitlePage ------------------------------------------------------
00519 
00520 class cDvbSubtitlePage : public cListObject {
00521 private:
00522   int pageId;
00523   int version;
00524   int state;
00525   int64_t pts;
00526   int timeout;
00527   cList<cSubtitleClut> cluts;
00528 public:
00529   cList<cSubtitleRegion> regions;
00530   cDvbSubtitlePage(int PageId);
00531   virtual ~cDvbSubtitlePage();
00532   int PageId(void) { return pageId; }
00533   int Version(void) { return version; }
00534   int State(void) { return state; }
00535   tArea *GetAreas(double FactorX, double FactorY);
00536   cSubtitleClut *GetClutById(int ClutId, bool New = false);
00537   cSubtitleObject *GetObjectById(int ObjectId);
00538   cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
00539   int64_t Pts(void) const { return pts; }
00540   int Timeout(void) { return timeout; }
00541   void SetVersion(int Version) { version = Version; }
00542   void SetPts(int64_t Pts) { pts = Pts; }
00543   void SetState(int State);
00544   void SetTimeout(int Timeout) { timeout = Timeout; }
00545   };
00546 
00547 cDvbSubtitlePage::cDvbSubtitlePage(int PageId)
00548 {
00549   pageId = PageId;
00550   version = -1;
00551   state = -1;
00552   pts = 0;
00553   timeout = 0;
00554 }
00555 
00556 cDvbSubtitlePage::~cDvbSubtitlePage()
00557 {
00558 }
00559 
00560 tArea *cDvbSubtitlePage::GetAreas(double FactorX, double FactorY)
00561 {
00562   if (regions.Count() > 0) {
00563      tArea *Areas = new tArea[regions.Count()];
00564      tArea *a = Areas;
00565      for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
00566          a->x1 = int(round(FactorX * sr->HorizontalAddress()));
00567          a->y1 = int(round(FactorY * sr->VerticalAddress()));
00568          a->x2 = int(round(FactorX * (sr->HorizontalAddress() + sr->Width() - 1)));
00569          a->y2 = int(round(FactorY * (sr->VerticalAddress() + sr->Height() - 1)));
00570          a->bpp = sr->Bpp();
00571          while ((a->Width() & 3) != 0)
00572                a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
00573          a++;
00574          }
00575      return Areas;
00576      }
00577   return NULL;
00578 }
00579 
00580 cSubtitleClut *cDvbSubtitlePage::GetClutById(int ClutId, bool New)
00581 {
00582   cSubtitleClut *result = NULL;
00583   for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
00584       if (sc->ClutId() == ClutId)
00585          result = sc;
00586       }
00587   if (!result && New) {
00588      result = new cSubtitleClut(ClutId);
00589      cluts.Add(result);
00590      }
00591   return result;
00592 }
00593 
00594 cSubtitleRegion *cDvbSubtitlePage::GetRegionById(int RegionId, bool New)
00595 {
00596   cSubtitleRegion *result = NULL;
00597   for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
00598       if (sr->RegionId() == RegionId)
00599          result = sr;
00600       }
00601   if (!result && New) {
00602      result = new cSubtitleRegion(RegionId);
00603      regions.Add(result);
00604      }
00605   return result;
00606 }
00607 
00608 cSubtitleObject *cDvbSubtitlePage::GetObjectById(int ObjectId)
00609 {
00610   cSubtitleObject *result = NULL;
00611   for (cSubtitleRegion *sr = regions.First(); sr && !result; sr = regions.Next(sr))
00612       result = sr->GetObjectById(ObjectId);
00613   return result;
00614 }
00615 
00616 void cDvbSubtitlePage::SetState(int State)
00617 {
00618   state = State;
00619   switch (state) {
00620     case 0: // normal case - page update
00621          dbgpages("page update\n");
00622          break;
00623     case 1: // acquisition point - page refresh
00624          dbgpages("page refresh\n");
00625          regions.Clear();
00626          break;
00627     case 2: // mode change - new page
00628          dbgpages("new Page\n");
00629          regions.Clear();
00630          cluts.Clear();
00631          break;
00632     case 3: // reserved
00633          break;
00634     default: dbgpages("unknown page state (%s %d)\n", __FUNCTION__, __LINE__);
00635     }
00636 }
00637 
00638 // --- cDvbSubtitleAssembler -------------------------------------------------
00639 
00640 class cDvbSubtitleAssembler {
00641 private:
00642   uchar *data;
00643   int length;
00644   int pos;
00645   int size;
00646   bool Realloc(int Size);
00647 public:
00648   cDvbSubtitleAssembler(void);
00649   virtual ~cDvbSubtitleAssembler();
00650   void Reset(void);
00651   unsigned char *Get(int &Length);
00652   void Put(const uchar *Data, int Length);
00653   };
00654 
00655 cDvbSubtitleAssembler::cDvbSubtitleAssembler(void)
00656 {
00657   data = NULL;
00658   size = 0;
00659   Reset();
00660 }
00661 
00662 cDvbSubtitleAssembler::~cDvbSubtitleAssembler()
00663 {
00664   free(data);
00665 }
00666 
00667 void cDvbSubtitleAssembler::Reset(void)
00668 {
00669   length = 0;
00670   pos = 0;
00671 }
00672 
00673 bool cDvbSubtitleAssembler::Realloc(int Size)
00674 {
00675   if (Size > size) {
00676      Size = max(Size, 2048);
00677      if (uchar *NewBuffer = (uchar *)realloc(data, Size)) {
00678         size = Size;
00679         data = NewBuffer;
00680         }
00681      else {
00682         esyslog("ERROR: can't allocate memory for subtitle assembler");
00683         length = 0;
00684         size = 0;
00685         free(data);
00686         data = NULL;
00687         return false;
00688         }
00689      }
00690   return true;
00691 }
00692 
00693 unsigned char *cDvbSubtitleAssembler::Get(int &Length)
00694 {
00695   if (length > pos + 5) {
00696      Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
00697      if (length >= pos + Length) {
00698         unsigned char *result = data + pos;
00699         pos += Length;
00700         return result;
00701         }
00702      }
00703   return NULL;
00704 }
00705 
00706 void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
00707 {
00708   if (Length && Realloc(length + Length)) {
00709      memcpy(data + length, Data, Length);
00710      length += Length;
00711      }
00712 }
00713 
00714 // --- cDvbSubtitleBitmaps ---------------------------------------------------
00715 
00716 class cDvbSubtitleBitmaps : public cListObject {
00717 private:
00718   int64_t pts;
00719   int timeout;
00720   tArea *areas;
00721   int numAreas;
00722   double osdFactorX;
00723   double osdFactorY;
00724   cVector<cBitmap *> bitmaps;
00725 public:
00726   cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
00727   ~cDvbSubtitleBitmaps();
00728   int64_t Pts(void) { return pts; }
00729   int Timeout(void) { return timeout; }
00730   void AddBitmap(cBitmap *Bitmap);
00731   void Draw(cOsd *Osd);
00732   };
00733 
00734 cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
00735 {
00736   pts = Pts;
00737   timeout = Timeout;
00738   areas = Areas;
00739   numAreas = NumAreas;
00740   osdFactorX = OsdFactorX;
00741   osdFactorY = OsdFactorY;
00742 }
00743 
00744 cDvbSubtitleBitmaps::~cDvbSubtitleBitmaps()
00745 {
00746   delete[] areas;
00747   for (int i = 0; i < bitmaps.Size(); i++)
00748       delete bitmaps[i];
00749 }
00750 
00751 void cDvbSubtitleBitmaps::AddBitmap(cBitmap *Bitmap)
00752 {
00753   bitmaps.Append(Bitmap);
00754 }
00755 
00756 void cDvbSubtitleBitmaps::Draw(cOsd *Osd)
00757 {
00758   bool Scale = !(DoubleEqual(osdFactorX, 1.0) && DoubleEqual(osdFactorY, 1.0));
00759   bool AntiAlias = true;
00760   if (Scale && osdFactorX > 1.0 || osdFactorY > 1.0) {
00761      // Upscaling requires 8bpp:
00762      int Bpp[MAXOSDAREAS];
00763      for (int i = 0; i < numAreas; i++) {
00764          Bpp[i] = areas[i].bpp;
00765          areas[i].bpp = 8;
00766          }
00767      if (Osd->CanHandleAreas(areas, numAreas) != oeOk) {
00768         for (int i = 0; i < numAreas; i++)
00769             Bpp[i] = areas[i].bpp = Bpp[i];
00770         AntiAlias = false;
00771         }
00772      }
00773   if (Osd->SetAreas(areas, numAreas) == oeOk) {
00774      for (int i = 0; i < bitmaps.Size(); i++) {
00775          cBitmap *b = bitmaps[i];
00776          if (Scale)
00777             b = b->Scaled(osdFactorX, osdFactorY, AntiAlias);
00778          Osd->DrawBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b);
00779          if (b != bitmaps[i])
00780             delete b;
00781          }
00782      Osd->Flush();
00783      }
00784 }
00785 
00786 // --- cDvbSubtitleConverter -------------------------------------------------
00787 
00788 int cDvbSubtitleConverter::setupLevel = 0;
00789 
00790 cDvbSubtitleConverter::cDvbSubtitleConverter(void)
00791 :cThread("subtitleConverter")
00792 {
00793   dvbSubtitleAssembler = new cDvbSubtitleAssembler;
00794   osd = NULL;
00795   frozen = false;
00796   ddsVersionNumber = -1;
00797   displayWidth = windowWidth = 720;
00798   displayHeight = windowHeight = 576;
00799   windowHorizontalOffset = 0;
00800   windowVerticalOffset = 0;
00801   pages = new cList<cDvbSubtitlePage>;
00802   bitmaps = new cList<cDvbSubtitleBitmaps>;
00803   Start();
00804 }
00805 
00806 cDvbSubtitleConverter::~cDvbSubtitleConverter()
00807 {
00808   Cancel(3);
00809   delete dvbSubtitleAssembler;
00810   delete osd;
00811   delete bitmaps;
00812   delete pages;
00813 }
00814 
00815 void cDvbSubtitleConverter::SetupChanged(void)
00816 {
00817   setupLevel++;
00818 }
00819 
00820 void cDvbSubtitleConverter::Reset(void)
00821 {
00822   dbgconverter("Converter reset -----------------------\n");
00823   dvbSubtitleAssembler->Reset();
00824   Lock();
00825   pages->Clear();
00826   bitmaps->Clear();
00827   DELETENULL(osd);
00828   frozen = false;
00829   ddsVersionNumber = -1;
00830   displayWidth = windowWidth = 720;
00831   displayHeight = windowHeight = 576;
00832   windowHorizontalOffset = 0;
00833   windowVerticalOffset = 0;
00834   Unlock();
00835 }
00836 
00837 int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length)
00838 {
00839   if (Data && Length > 8) {
00840      int PayloadOffset = PesPayloadOffset(Data);
00841      int SubstreamHeaderLength = 4;
00842      bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
00843 
00844      // Compatibility mode for old subtitles plugin:
00845      if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
00846         PayloadOffset--;
00847         SubstreamHeaderLength = 1;
00848         ResetSubtitleAssembler = Data[8] >= 5;
00849         }
00850 
00851      if (Length > PayloadOffset + SubstreamHeaderLength) {
00852         int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
00853         if (pts)
00854            dbgconverter("Converter PTS: %"PRId64"\n", pts);
00855         const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
00856         int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
00857         if (ResetSubtitleAssembler)
00858            dvbSubtitleAssembler->Reset();
00859 
00860         if (length > 3) {
00861            if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
00862               dvbSubtitleAssembler->Put(data + 2, length - 2);
00863            else
00864               dvbSubtitleAssembler->Put(data, length);
00865 
00866            int Count;
00867            while (true) {
00868                  unsigned char *b = dvbSubtitleAssembler->Get(Count);
00869                  if (b && b[0] == 0x0F) {
00870                     if (ExtractSegment(b, Count, pts) == -1)
00871                        break;
00872                     }
00873                  else
00874                     break;
00875                  }
00876            }
00877         }
00878      return Length;
00879      }
00880   return 0;
00881 }
00882 
00883 int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
00884 {
00885   if (Data && Length > 8) {
00886      int PayloadOffset = PesPayloadOffset(Data);
00887      if (Length > PayloadOffset) {
00888         int64_t pts = PesGetPts(Data);
00889         if (pts)
00890            dbgconverter("Converter PTS: %"PRId64"\n", pts);
00891         const uchar *data = Data + PayloadOffset;
00892         int length = Length - PayloadOffset;
00893         if (length > 3) {
00894            if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
00895               data += 2;
00896               length -= 2;
00897               }
00898            const uchar *b = data;
00899            while (length > 0) {
00900                  if (b[0] == 0x0F) {
00901                     int n = ExtractSegment(b, length, pts);
00902                     if (n < 0)
00903                        break;
00904                     b += n;
00905                     length -= n;
00906                     }
00907                  else
00908                     break;
00909                  }
00910            }
00911         }
00912      return Length;
00913      }
00914   return 0;
00915 }
00916 
00917 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
00918 #define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms
00919 
00920 void cDvbSubtitleConverter::Action(void)
00921 {
00922   int LastSetupLevel = setupLevel;
00923   cTimeMs Timeout;
00924   while (Running()) {
00925         int WaitMs = 100;
00926         if (!frozen) {
00927            LOCK_THREAD;
00928            if (osd) {
00929               int NewSetupLevel = setupLevel;
00930               if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
00931                  DELETENULL(osd);
00932                  }
00933               LastSetupLevel = NewSetupLevel;
00934               }
00935            if (cDvbSubtitleBitmaps *sb = bitmaps->First()) {
00936               int64_t STC = cDevice::PrimaryDevice()->GetSTC();
00937               int64_t Delta = LimitTo32Bit(sb->Pts()) - LimitTo32Bit(STC); // some devices only deliver 32 bits
00938               if (Delta > (int64_t(1) << 31))
00939                  Delta -= (int64_t(1) << 32);
00940               else if (Delta < -((int64_t(1) << 31) - 1))
00941                  Delta += (int64_t(1) << 32);
00942               Delta /= 90; // STC and PTS are in 1/90000s
00943               if (Delta <= MAXDELTA) {
00944                  if (Delta <= 0) {
00945                     dbgconverter("Got %d bitmaps, showing #%d\n", bitmaps->Count(), sb->Index() + 1);
00946                     if (AssertOsd()) {
00947                        sb->Draw(osd);
00948                        Timeout.Set(sb->Timeout() * 1000);
00949                        dbgconverter("PTS: %"PRId64"  STC: %"PRId64" (%"PRId64") timeout: %d\n", sb->Pts(), cDevice::PrimaryDevice()->GetSTC(), Delta, sb->Timeout());
00950                        }
00951                     bitmaps->Del(sb);
00952                     }
00953                  else if (Delta < WaitMs)
00954                     WaitMs = Delta;
00955                  }
00956               else
00957                  bitmaps->Del(sb);
00958               }
00959            }
00960         cCondWait::SleepMs(WaitMs);
00961         }
00962 }
00963 
00964 tColor cDvbSubtitleConverter::yuv2rgb(int Y, int Cb, int Cr)
00965 {
00966   int Ey, Epb, Epr;
00967   int Eg, Eb, Er;
00968 
00969   Ey = (Y - 16);
00970   Epb = (Cb - 128);
00971   Epr = (Cr - 128);
00972   /* ITU-R 709 */
00973   Er = constrain((298 * Ey             + 460 * Epr) / 256, 0, 255);
00974   Eg = constrain((298 * Ey -  55 * Epb - 137 * Epr) / 256, 0, 255);
00975   Eb = constrain((298 * Ey + 543 * Epb            ) / 256, 0, 255);
00976 
00977   return (Er << 16) | (Eg << 8) | Eb;
00978 }
00979 
00980 void cDvbSubtitleConverter::SetOsdData(void)
00981 {
00982   int OsdWidth, OsdHeight;
00983   double OsdAspect;
00984   int VideoWidth, VideoHeight;
00985   double VideoAspect;
00986   cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
00987   cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
00988   if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
00989      osdFactorX = osdFactorY = 1.0;
00990      osdDeltaX = osdDeltaY = 0;
00991      }
00992   else {
00993      osdFactorX = VideoAspect * OsdHeight / displayWidth;
00994      osdFactorY = double(OsdHeight) / displayHeight;
00995      osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
00996      osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
00997      }
00998 }
00999 
01000 bool cDvbSubtitleConverter::AssertOsd(void)
01001 {
01002   LOCK_THREAD;
01003   if (!osd) {
01004      SetOsdData();
01005      osd = cOsdProvider::NewOsd(int(round(osdFactorX * windowHorizontalOffset + osdDeltaX)), int(round(osdFactorY * windowVerticalOffset + osdDeltaY)) + Setup.SubtitleOffset, OSD_LEVEL_SUBTITLES);
01006      }
01007   return osd != NULL;
01008 }
01009 
01010 int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
01011 {
01012   cBitStream bs(Data, Length * 8);
01013   if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
01014      int segmentType = bs.GetBits(8);
01015      if (segmentType == STUFFING_SEGMENT)
01016         return -1;
01017      int pageId = bs.GetBits(16);
01018      int segmentLength = bs.GetBits(16);
01019      if (!bs.SetLength(bs.Index() + segmentLength * 8))
01020         return -1;
01021      cDvbSubtitlePage *page = NULL;
01022      LOCK_THREAD;
01023      for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
01024          if (sp->PageId() == pageId) {
01025             page = sp;
01026             break;
01027             }
01028          }
01029      if (!page) {
01030         page = new cDvbSubtitlePage(pageId);
01031         pages->Add(page);
01032         dbgpages("Create SubtitlePage %d (total pages = %d)\n", pageId, pages->Count());
01033         }
01034      if (Pts)
01035         page->SetPts(Pts);
01036      switch (segmentType) {
01037        case PAGE_COMPOSITION_SEGMENT: {
01038             dbgsegments("PAGE_COMPOSITION_SEGMENT\n");
01039             int pageTimeout = bs.GetBits(8);
01040             int pageVersion = bs.GetBits(4);
01041             if (pageVersion == page->Version())
01042                break; // no update
01043             page->SetVersion(pageVersion);
01044             page->SetTimeout(pageTimeout);
01045             page->SetState(bs.GetBits(2));
01046             bs.SkipBits(2); // reserved
01047             dbgpages("Update page id %d version %d pts %"PRId64" timeout %d state %d\n", pageId, page->Version(), page->Pts(), page->Timeout(), page->State());
01048             while (!bs.IsEOF()) {
01049                   cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
01050                   bs.SkipBits(8); // reserved
01051                   region->SetHorizontalAddress(bs.GetBits(16));
01052                   region->SetVerticalAddress(bs.GetBits(16));
01053                   }
01054             break;
01055             }
01056        case REGION_COMPOSITION_SEGMENT: {
01057             dbgsegments("REGION_COMPOSITION_SEGMENT\n");
01058             cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8));
01059             if (!region)
01060                break;
01061             int regionVersion = bs.GetBits(4);
01062             if (regionVersion == region->Version())
01063                break; // no update
01064             region->SetVersion(regionVersion);
01065             bool regionFillFlag = bs.GetBit();
01066             bs.SkipBits(3); // reserved
01067             int regionWidth = bs.GetBits(16);
01068             if (regionWidth < 1)
01069                regionWidth = 1;
01070             int regionHeight = bs.GetBits(16);
01071             if (regionHeight < 1)
01072                regionHeight = 1;
01073             region->SetSize(regionWidth, regionHeight);
01074             region->SetLevel(bs.GetBits(3));
01075             region->SetDepth(bs.GetBits(3));
01076             bs.SkipBits(2); // reserved
01077             region->SetClutId(bs.GetBits(8));
01078             dbgregions("Region pageId %d id %d version %d fill %d width %d height %d level %d depth %d clutId %d\n", pageId, region->RegionId(), region->Version(), regionFillFlag, regionWidth, regionHeight, region->Level(), region->Depth(), region->ClutId());
01079             int region8bitPixelCode = bs.GetBits(8);
01080             int region4bitPixelCode = bs.GetBits(4);
01081             int region2bitPixelCode = bs.GetBits(2);
01082             bs.SkipBits(2); // reserved
01083             if (regionFillFlag) {
01084                switch (region->Bpp()) {
01085                  case 2: region->FillRegion(region8bitPixelCode); break;
01086                  case 4: region->FillRegion(region4bitPixelCode); break;
01087                  case 8: region->FillRegion(region2bitPixelCode); break;
01088                  default: dbgregions("unknown bpp %d (%s %d)\n", region->Bpp(), __FUNCTION__, __LINE__);
01089                  }
01090                }
01091             while (!bs.IsEOF()) {
01092                   cSubtitleObject *object = region->GetObjectById(bs.GetBits(16), true);
01093                   int objectType = bs.GetBits(2);
01094                   object->SetCodingMethod(objectType);
01095                   object->SetProviderFlag(bs.GetBits(2));
01096                   int objectHorizontalPosition = bs.GetBits(12);
01097                   bs.SkipBits(4); // reserved
01098                   int objectVerticalPosition = bs.GetBits(12);
01099                   object->SetPosition(objectHorizontalPosition, objectVerticalPosition);
01100                   if (objectType == 0x01 || objectType == 0x02) {
01101                      object->SetForegroundPixelCode(bs.GetBits(8));
01102                      object->SetBackgroundPixelCode(bs.GetBits(8));
01103                      }
01104                   }
01105             break;
01106             }
01107        case CLUT_DEFINITION_SEGMENT: {
01108             dbgsegments("CLUT_DEFINITION_SEGMENT\n");
01109             cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
01110             int clutVersion = bs.GetBits(4);
01111             if (clutVersion == clut->Version())
01112                break; // no update
01113             clut->SetVersion(clutVersion);
01114             bs.SkipBits(4); // reserved
01115             dbgcluts("Clut pageId %d id %d version %d\n", pageId, clut->ClutId(), clut->Version());
01116             while (!bs.IsEOF()) {
01117                   uchar clutEntryId = bs.GetBits(8);
01118                   bool entryClut2Flag = bs.GetBit();
01119                   bool entryClut4Flag = bs.GetBit();
01120                   bool entryClut8Flag = bs.GetBit();
01121                   bs.SkipBits(4); // reserved
01122                   uchar yval;
01123                   uchar crval;
01124                   uchar cbval;
01125                   uchar tval;
01126                   if (bs.GetBit()) { // full_range_flag
01127                      yval  = bs.GetBits(8);
01128                      crval = bs.GetBits(8);
01129                      cbval = bs.GetBits(8);
01130                      tval  = bs.GetBits(8);
01131                      }
01132                   else {
01133                      yval  = bs.GetBits(6) << 2;
01134                      crval = bs.GetBits(4) << 4;
01135                      cbval = bs.GetBits(4) << 4;
01136                      tval  = bs.GetBits(2) << 6;
01137                      }
01138                   tColor value = 0;
01139                   if (yval) {
01140                      value = yuv2rgb(yval, cbval, crval);
01141                      value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
01142                      }
01143                   dbgcluts("%2d %d %d %d %08X\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value);
01144                   if (entryClut2Flag)
01145                      clut->SetColor(2, clutEntryId, value);
01146                   if (entryClut4Flag)
01147                      clut->SetColor(4, clutEntryId, value);
01148                   if (entryClut8Flag)
01149                      clut->SetColor(8, clutEntryId, value);
01150                   }
01151             dbgcluts("\n");
01152             break;
01153             }
01154        case OBJECT_DATA_SEGMENT: {
01155             dbgsegments("OBJECT_DATA_SEGMENT\n");
01156             cSubtitleObject *object = page->GetObjectById(bs.GetBits(16));
01157             if (!object)
01158                break;
01159             int objectVersion = bs.GetBits(4);
01160             if (objectVersion == object->Version())
01161                break; // no update
01162             object->SetVersion(objectVersion);
01163             int codingMethod = bs.GetBits(2);
01164             object->SetNonModifyingColorFlag(bs.GetBit());
01165             bs.SkipBit(); // reserved
01166             dbgobjects("Object pageId %d id %d version %d method %d modify %d\n", pageId, object->ObjectId(), object->Version(), object->CodingMethod(), object->NonModifyingColorFlag());
01167             if (codingMethod == 0) { // coding of pixels
01168                int topFieldLength = bs.GetBits(16);
01169                int bottomFieldLength = bs.GetBits(16);
01170                object->DecodeSubBlock(bs.GetData(), topFieldLength, true);
01171                if (bottomFieldLength)
01172                   object->DecodeSubBlock(bs.GetData() + topFieldLength, bottomFieldLength, false);
01173                else
01174                   object->DecodeSubBlock(bs.GetData(), topFieldLength, false);
01175                bs.WordAlign();
01176                }
01177             else if (codingMethod == 1) { // coded as a string of characters
01178                int numberOfCodes = bs.GetBits(8);
01179                object->DecodeCharacterString(bs.GetData(), numberOfCodes);
01180                }
01181 #ifdef FINISHPAGE_HACK
01182             FinishPage(page); // flush to OSD right away
01183 #endif
01184             break;
01185             }
01186        case DISPLAY_DEFINITION_SEGMENT: {
01187             dbgsegments("DISPLAY_DEFINITION_SEGMENT\n");
01188             int version = bs.GetBits(4);
01189             if (version != ddsVersionNumber) {
01190                bool displayWindowFlag = bs.GetBit();
01191                windowHorizontalOffset = 0;
01192                windowVerticalOffset   = 0;
01193                bs.SkipBits(3); // reserved
01194                displayWidth  = windowWidth  = bs.GetBits(16) + 1;
01195                displayHeight = windowHeight = bs.GetBits(16) + 1;
01196                if (displayWindowFlag) {
01197                   windowHorizontalOffset = bs.GetBits(16);                              // displayWindowHorizontalPositionMinimum
01198                   windowWidth            = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
01199                   windowVerticalOffset   = bs.GetBits(16);                              // displayWindowVerticalPositionMinimum
01200                   windowHeight           = bs.GetBits(16) - windowVerticalOffset + 1;   // displayWindowVerticalPositionMaximum
01201                   }
01202                SetOsdData();
01203                SetupChanged();
01204                ddsVersionNumber = version;
01205                }
01206             break;
01207             }
01208        case DISPARITY_SIGNALING_SEGMENT: {
01209             dbgsegments("DISPARITY_SIGNALING_SEGMENT\n");
01210             bs.SkipBits(4); // dss_version_number
01211             bool disparity_shift_update_sequence_page_flag = bs.GetBit();
01212             bs.SkipBits(3); // reserved
01213             bs.SkipBits(8); // page_default_disparity_shift
01214             if (disparity_shift_update_sequence_page_flag) {
01215                bs.SkipBits(8); // disparity_shift_update_sequence_length
01216                bs.SkipBits(24); // interval_duration[23..0]
01217                int division_period_count = bs.GetBits(8);
01218                for (int i = 0; i < division_period_count; ++i) {
01219                    bs.SkipBits(8); // interval_count
01220                    bs.SkipBits(8); // disparity_shift_update_integer_part
01221                    }
01222                }
01223             while (!bs.IsEOF()) {
01224                   bs.SkipBits(8); // region_id
01225                   bool disparity_shift_update_sequence_region_flag = bs.GetBit();
01226                   bs.SkipBits(5); // reserved
01227                   int number_of_subregions_minus_1 = bs.GetBits(2);
01228                   for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
01229                       if (number_of_subregions_minus_1 > 0) {
01230                          bs.SkipBits(16); // subregion_horizontal_position
01231                          bs.SkipBits(16); // subregion_width
01232                          }
01233                       bs.SkipBits(8); // subregion_disparity_shift_integer_part
01234                       bs.SkipBits(4); // subregion_disparity_shift_fractional_part
01235                       bs.SkipBits(4); // reserved
01236                       if (disparity_shift_update_sequence_region_flag) {
01237                          bs.SkipBits(8); // disparity_shift_update_sequence_length
01238                          bs.SkipBits(24); // interval_duration[23..0]
01239                          int division_period_count = bs.GetBits(8);
01240                          for (int i = 0; i < division_period_count; ++i) {
01241                              bs.SkipBits(8); // interval_count
01242                              bs.SkipBits(8); // disparity_shift_update_integer_part
01243                              }
01244                          }
01245                       }
01246                   }
01247             break;
01248             }
01249        case END_OF_DISPLAY_SET_SEGMENT: {
01250             dbgsegments("END_OF_DISPLAY_SET_SEGMENT\n");
01251             FinishPage(page);
01252             break;
01253             }
01254        default:
01255             dbgsegments("*** unknown segment type: %02X\n", segmentType);
01256        }
01257      return bs.Length() / 8;
01258      }
01259   return -1;
01260 }
01261 
01262 void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
01263 {
01264   if (!AssertOsd())
01265      return;
01266   tArea *Areas = Page->GetAreas(osdFactorX, osdFactorY);
01267   int NumAreas = Page->regions.Count();
01268   int Bpp = 8;
01269   bool Reduced = false;
01270   while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) {
01271         int HalfBpp = Bpp / 2;
01272         if (HalfBpp >= 2) {
01273            for (int i = 0; i < NumAreas; i++) {
01274                if (Areas[i].bpp >= Bpp) {
01275                   Areas[i].bpp = HalfBpp;
01276                   Reduced = true;
01277                   }
01278                }
01279            Bpp = HalfBpp;
01280            }
01281         else
01282            return; // unable to draw bitmaps
01283         }
01284   cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->Pts(), Page->Timeout(), Areas, NumAreas, osdFactorX, osdFactorY);
01285   bitmaps->Add(Bitmaps);
01286   for (int i = 0; i < NumAreas; i++) { 
01287       cSubtitleRegion *sr = Page->regions.Get(i);
01288       cSubtitleClut *clut = Page->GetClutById(sr->ClutId());
01289       if (!clut)
01290          continue;
01291       sr->Replace(*clut->GetPalette(sr->Bpp()));
01292       sr->UpdateTextData(clut);
01293       if (Reduced) {
01294          if (sr->Bpp() != Areas[i].bpp) {
01295             if (sr->Level() <= Areas[i].bpp) {
01296                //TODO this is untested - didn't have any such subtitle stream
01297                cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
01298                if (Clut) {
01299                   dbgregions("reduce region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp);
01300                   sr->ReduceBpp(*Clut->GetPalette(sr->Bpp()));
01301                   }
01302                }
01303             else {
01304                dbgregions("condense region %d bpp %d level %d area bpp %d\n", sr->RegionId(), sr->Bpp(), sr->Level(), Areas[i].bpp);
01305                sr->ShrinkBpp(Areas[i].bpp);
01306                }
01307             }
01308          }
01309       int posX = sr->HorizontalAddress();
01310       int posY = sr->VerticalAddress();
01311       if (sr->Width() > 0 && sr->Height() > 0) {
01312          cBitmap *bm = new cBitmap(sr->Width(), sr->Height(), sr->Bpp(), posX, posY);
01313          bm->DrawBitmap(posX, posY, *sr);
01314          Bitmaps->AddBitmap(bm);
01315          }
01316       }
01317 }