vdr
1.7.27
|
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 }