vdr  2.4.7
dvbsubtitle.c
Go to the documentation of this file.
1 /*
2  * dvbsubtitle.c: DVB subtitles
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * Original author: Marco Schluessler <marco@lordzodiac.de>
8  * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
9  *
10  * $Id: dvbsubtitle.c 4.2 2020/05/15 12:32:51 kls Exp $
11  */
12 
13 #include "dvbsubtitle.h"
14 #define __STDC_FORMAT_MACROS // Required for format specifiers
15 #include <inttypes.h>
16 #include "device.h"
17 #include "libsi/si.h"
18 
19 #define PAGE_COMPOSITION_SEGMENT 0x10
20 #define REGION_COMPOSITION_SEGMENT 0x11
21 #define CLUT_DEFINITION_SEGMENT 0x12
22 #define OBJECT_DATA_SEGMENT 0x13
23 #define DISPLAY_DEFINITION_SEGMENT 0x14
24 #define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156
25 #define END_OF_DISPLAY_SET_SEGMENT 0x80
26 #define STUFFING_SEGMENT 0xFF
27 
28 #define PGS_PALETTE_SEGMENT 0x14
29 #define PGS_OBJECT_SEGMENT 0x15
30 #define PGS_PRESENTATION_SEGMENT 0x16
31 #define PGS_WINDOW_SEGMENT 0x17
32 #define PGS_DISPLAY_SEGMENT 0x80
33 
34 // Set these to 'true' for debug output, which is written into the file dbg-log.htm
35 // in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
36 // used to display the subtitles.
37 static bool DebugNormal = false; // shows pages, regions and objects
38 static bool DebugVerbose = false; // shows everything
45 static bool DebugPixel = DebugVerbose;
46 static bool DebugCluts = DebugVerbose;
47 static bool DebugOutput = DebugVerbose;
48 
49 #define dbgdisplay(a...) if (DebugDisplay) SD.WriteHtml(a)
50 #define dbgpages(a...) if (DebugPages) SD.WriteHtml(a)
51 #define dbgregions(a...) if (DebugRegions) SD.WriteHtml(a)
52 #define dbgobjects(a...) if (DebugObjects) SD.WriteHtml(a)
53 #define dbgconverter(a...) if (DebugConverter) SD.WriteHtml(a)
54 #define dbgsegments(a...) if (DebugSegments) SD.WriteHtml(a)
55 #define dbgpixel(a...) if (DebugPixel) SD.WriteHtml(a)
56 #define dbgcluts(a...) if (DebugCluts) SD.WriteHtml(a)
57 #define dbgoutput(a...) if (DebugOutput) SD.WriteHtml(a)
58 
59 #define DBGMAXBITMAPS 100 // debug output will be stopped after this many bitmaps
60 #define DBGBITMAPWIDTH 400
61 
62 #define FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY // some don't properly handle version numbers, which renders them useless because subtitles are not displayed
63 
64 // --- cSubtitleDebug --------------------------------------------------------
65 
67 private:
69  int imgCnt;
70  int64_t firstPts;
71  bool newFile;
72  double factor;
73 public:
74  cSubtitleDebug(void) { Reset(); }
75  void Reset(void);
76  bool Active(void) { return imgCnt < DBGMAXBITMAPS; }
77  int64_t FirstPts(void) { return firstPts; }
78  void SetFirstPts(int64_t FirstPts) { if (firstPts < 0) firstPts = FirstPts; }
79  void SetFactor(double Factor) { factor = Factor; }
80  cString WriteJpeg(const cBitmap *Bitmap, int MaxX = 0, int MaxY = 0);
81  void WriteHtml(const char *Format, ...);
82  };
83 
85 {
86  imgCnt = 0;
87  firstPts = -1;
88  newFile = true;
89  factor = 1.0;
90 }
91 
92 cString cSubtitleDebug::WriteJpeg(const cBitmap *Bitmap, int MaxX, int MaxY)
93 {
94  if (!Active())
95  return NULL;
96  cMutexLock MutexLock(&mutex);
97  cBitmap *Scaled = Bitmap->Scaled(factor, factor, true);
98  int w = MaxX ? int(round(MaxX * factor)) : Scaled->Width();
99  int h = MaxY ? int(round(MaxY * factor)) : Scaled->Height();
100  uchar mem[w * h * 3];
101  for (int x = 0; x < w; x++) {
102  for (int y = 0; y < h; y++) {
103  tColor c = Scaled->GetColor(x, y);
104  int o = (y * w + x) * 3;
105  mem[o++] = (c & 0x00FF0000) >> 16;
106  mem[o++] = (c & 0x0000FF00) >> 8;
107  mem[o] = (c & 0x000000FF);
108  }
109  }
110  delete Scaled;
111  int Size = 0;
112  uchar *Jpeg = RgbToJpeg(mem, w, h, Size);
113  cString ImgName = cString::sprintf("dbg-%03d.jpg", imgCnt++);
114  int f = open(ImgName, O_WRONLY | O_CREAT, DEFFILEMODE);
115  if (f >= 0) {
116  if (write(f, Jpeg, Size) < 0)
117  LOG_ERROR_STR(*ImgName);
118  close(f);
119  }
120  free(Jpeg);
121  return ImgName;
122 }
123 
124 void cSubtitleDebug::WriteHtml(const char *Format, ...)
125 {
126  if (!Active())
127  return;
128  cMutexLock MutexLock(&mutex);
129  if (FILE *f = fopen("dbg-log.htm", newFile ? "w" : "a")) {
130  va_list ap;
131  va_start(ap, Format);
132  vfprintf(f, Format, ap);
133  va_end(ap);
134  fclose(f);
135  newFile = false;
136  }
137 }
138 
140 
141 // --- cSubtitleClut ---------------------------------------------------------
142 
143 class cSubtitleClut : public cListObject {
144 private:
145  int clutId;
150  tColor yuv2rgb(int Y, int Cb, int Cr);
151  void SetColor(int Bpp, int Index, tColor Color);
152 public:
153  cSubtitleClut(int ClutId);
154  void Parse(cBitStream &bs);
155  void ParsePgs(cBitStream &bs);
156  int ClutId(void) { return clutId; }
157  int ClutVersionNumber(void) { return clutVersionNumber; }
158  const cPalette *GetPalette(int Bpp);
159  };
160 
162 :palette2(2)
163 ,palette4(4)
164 ,palette8(8)
165 {
166  int a = 0, r = 0, g = 0, b = 0;
167  clutId = ClutId;
168  clutVersionNumber = -1;
169  // ETSI EN 300 743 10.3: 4-entry CLUT default contents
170  palette2.SetColor(0, ArgbToColor( 0, 0, 0, 0));
171  palette2.SetColor(1, ArgbToColor(255, 255, 255, 255));
172  palette2.SetColor(2, ArgbToColor(255, 0, 0, 0));
173  palette2.SetColor(3, ArgbToColor(255, 127, 127, 127));
174  // ETSI EN 300 743 10.2: 16-entry CLUT default contents
175  palette4.SetColor(0, ArgbToColor(0, 0, 0, 0));
176  for (int i = 1; i < 16; ++i) {
177  if (i < 8) {
178  r = (i & 1) ? 255 : 0;
179  g = (i & 2) ? 255 : 0;
180  b = (i & 4) ? 255 : 0;
181  }
182  else {
183  r = (i & 1) ? 127 : 0;
184  g = (i & 2) ? 127 : 0;
185  b = (i & 4) ? 127 : 0;
186  }
187  palette4.SetColor(i, ArgbToColor(255, r, g, b));
188  }
189  // ETSI EN 300 743 10.1: 256-entry CLUT default contents
190  palette8.SetColor(0, ArgbToColor(0, 0, 0, 0));
191  for (int i = 1; i < 256; ++i) {
192  if (i < 8) {
193  r = (i & 1) ? 255 : 0;
194  g = (i & 2) ? 255 : 0;
195  b = (i & 4) ? 255 : 0;
196  a = 63;
197  }
198  else {
199  switch (i & 0x88) {
200  case 0x00:
201  r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
202  g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
203  b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
204  a = 255;
205  break;
206  case 0x08:
207  r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
208  g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
209  b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
210  a = 127;
211  break;
212  case 0x80:
213  r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
214  g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
215  b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
216  a = 255;
217  break;
218  case 0x88:
219  r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
220  g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
221  b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
222  a = 255;
223  break;
224  }
225  }
226  palette8.SetColor(i, ArgbToColor(a, r, g, b));
227  }
228 }
229 
231 {
232  int Version = bs.GetBits(4);
233 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
234  if (clutVersionNumber == Version)
235  return; // no update
236 #endif
237  clutVersionNumber = Version;
238  bs.SkipBits(4); // reserved
239  dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
240  while (!bs.IsEOF()) {
241  uchar clutEntryId = bs.GetBits(8);
242  bool entryClut2Flag = bs.GetBit();
243  bool entryClut4Flag = bs.GetBit();
244  bool entryClut8Flag = bs.GetBit();
245  bs.SkipBits(4); // reserved
246  uchar yval;
247  uchar crval;
248  uchar cbval;
249  uchar tval;
250  if (bs.GetBit()) { // full_range_flag
251  yval = bs.GetBits(8);
252  crval = bs.GetBits(8);
253  cbval = bs.GetBits(8);
254  tval = bs.GetBits(8);
255  }
256  else {
257  yval = bs.GetBits(6) << 2;
258  crval = bs.GetBits(4) << 4;
259  cbval = bs.GetBits(4) << 4;
260  tval = bs.GetBits(2) << 6;
261  }
262  tColor value = 0;
263  if (yval) {
264  value = yuv2rgb(yval, cbval, crval);
265  value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
266  }
267  dbgcluts("%2d %d %d %d %08X<br>\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value);
268  if (entryClut2Flag)
269  SetColor(2, clutEntryId, value);
270  if (entryClut4Flag)
271  SetColor(4, clutEntryId, value);
272  if (entryClut8Flag)
273  SetColor(8, clutEntryId, value);
274  }
275 }
276 
278 {
279  int Version = bs.GetBits(8);
280  if (clutVersionNumber == Version)
281  return; // no update
282  clutVersionNumber = Version;
283  dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
284  for (int i = 0; i < 256; ++i)
285  SetColor(8, i, ArgbToColor(0, 0, 0, 0));
286  while (!bs.IsEOF()) {
287  uchar clutEntryId = bs.GetBits(8);
288  uchar yval = bs.GetBits(8);
289  uchar crval = bs.GetBits(8);
290  uchar cbval = bs.GetBits(8);
291  uchar tval = bs.GetBits(8);
292  tColor value = 0;
293  if (yval) {
294  value = yuv2rgb(yval, cbval, crval);
295  value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * tval / 10) << 24;
296  }
297  dbgcluts("%2d %08X<br>\n", clutEntryId, value);
298  SetColor(8, clutEntryId, value);
299  }
300 }
301 
302 tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr)
303 {
304  int Ey, Epb, Epr;
305  int Eg, Eb, Er;
306 
307  Ey = (Y - 16);
308  Epb = (Cb - 128);
309  Epr = (Cr - 128);
310  /* ITU-R 709 */
311  Er = constrain((298 * Ey + 460 * Epr) / 256, 0, 255);
312  Eg = constrain((298 * Ey - 55 * Epb - 137 * Epr) / 256, 0, 255);
313  Eb = constrain((298 * Ey + 543 * Epb ) / 256, 0, 255);
314 
315  return (Er << 16) | (Eg << 8) | Eb;
316 }
317 
318 void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color)
319 {
320  switch (Bpp) {
321  case 2: palette2.SetColor(Index, Color); break;
322  case 4: palette4.SetColor(Index, Color); break;
323  case 8: palette8.SetColor(Index, Color); break;
324  default: esyslog("ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)", Bpp, Index, Color);
325  }
326 }
327 
329 {
330  switch (Bpp) {
331  case 2: return &palette2;
332  case 4: return &palette4;
333  case 8: return &palette8;
334  default: esyslog("ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)", Bpp);
335  }
336  return &palette8;
337 }
338 
339 // --- cSubtitleObject -------------------------------------------------------
340 
341 class cSubtitleObject : public cListObject {
342 private:
343  int objectId;
349  int topIndex;
352  char *txtData;
354  void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length);
355  bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
356  bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
357  bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
358  bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
359  void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even);
360  void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
361 public:
364  void Parse(cBitStream &bs);
365  void ParsePgs(cBitStream &bs);
366  int ObjectId(void) { return objectId; }
370  void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg);
371  };
372 
374 {
375  objectId = ObjectId;
376  objectVersionNumber = -1;
377  objectCodingMethod = -1;
378  nonModifyingColorFlag = false;
379  topLength = 0;
380  botLength = 0;
381  topIndex = 0;
382  topData = NULL;
383  botData = NULL;
384  txtData = NULL;
385  lineHeight = 26; // configurable subtitling font size?
386 }
387 
389 {
390  free(topData);
391  free(botData);
392  free(txtData);
393 }
394 
396 {
397  int Version = bs.GetBits(4);
398 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
399  if (objectVersionNumber == Version)
400  return; // no update
401 #endif
402  objectVersionNumber = Version;
403  objectCodingMethod = bs.GetBits(2);
405  bs.SkipBit(); // reserved
406  dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag); // no "<br>\n" here, DecodeCharacterString() may add data
407  if (objectCodingMethod == 0) { // coding of pixels
408  topLength = bs.GetBits(16);
409  botLength = bs.GetBits(16);
410  free(topData);
411  if ((topData = MALLOC(uchar, topLength)) != NULL)
412  memcpy(topData, bs.GetData(), topLength);
413  else
414  topLength = 0;
415  free(botData);
416  if ((botData = MALLOC(uchar, botLength)) != NULL)
417  memcpy(botData, bs.GetData() + topLength, botLength);
418  else
419  botLength = 0;
420  bs.WordAlign();
421  }
422  else if (objectCodingMethod == 1) { // coded as a string of characters
423  int numberOfCodes = bs.GetBits(8);
424  DecodeCharacterString(bs.GetData(), numberOfCodes);
425  }
426  dbgobjects("<br>\n");
427  if (DebugObjects) {
428  // We can't get the actual clut here, so we use a default one. This may lead to
429  // funny colors, but we just want to get a rough idea of what's in the object, anyway.
430  cSubtitleClut Clut(0);
431  cBitmap b(1920, 1080, 8);
432  b.Replace(*Clut.GetPalette(b.Bpp()));
433  b.Clean();
434  Render(&b, 0, 0, 0, 1);
435  int x1, y1, x2, y2;
436  if (b.Dirty(x1, y1, x2, y2)) {
437  cString ImgName = SD.WriteJpeg(&b, x2, y2);
438  dbgobjects("<img src=\"%s\"><br>\n", *ImgName);
439  }
440  }
441 }
442 
444 {
445  int Version = bs.GetBits(8);
446  if (objectVersionNumber == Version)
447  return; // no update
448  objectVersionNumber = Version;
449  objectCodingMethod = 0;
450  int sequenceDescriptor = bs.GetBits(8);
451  if (!(sequenceDescriptor & 0x80) && topData != NULL) {
452  memcpy(topData + topIndex, bs.GetData(), (bs.Length() - bs.Index()) / 8);
453  topIndex += (bs.Length() - bs.Index()) / 8;
454  return;
455  }
456  topLength = bs.GetBits(24) - 4 + 1; // exclude width / height, add sub block type
457  bs.SkipBits(32);
458  if ((topData = MALLOC(uchar, topLength)) != NULL) {
459  topData[topIndex++] = 0xFF; // PGS end of line
460  memcpy(topData + 1, bs.GetData(), (bs.Length() - bs.Index()) / 8);
461  topIndex += (bs.Length() - bs.Index()) / 8 + 1;
462  }
463  dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag);
464 }
465 
466 void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
467 {
468  // "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
469  // character_code to be a 16-bit index number into the character table identified
470  // in the subtitle_descriptor. However, the "subtitling_descriptor" <sic> according to
471  // "ETSI EN 300 468 V1.13.1 (2012-04)" doesn't contain a "character table identifier".
472  // It only contains a three letter language code, without any specification as to how
473  // this is related to a specific character table.
474  // Apparently the first "code" in textual subtitles contains the character table
475  // identifier, and all codes are 8-bit only. So let's first make Data a string of
476  // 8-bit characters:
477  if (NumberOfCodes > 0) {
478  char txt[NumberOfCodes + 1];
479  for (int i = 0; i < NumberOfCodes; i++)
480  txt[i] = Data[i * 2 + 1];
481  txt[NumberOfCodes] = 0;
482  const uchar *from = (uchar *)txt;
483  int len = NumberOfCodes;
484  const char *CharacterTable = SI::getCharacterTable(from, len);
485  dbgobjects(" table %s raw '%s'", CharacterTable, from);
486  cCharSetConv conv(CharacterTable, cCharSetConv::SystemCharacterTable());
487  const char *s = conv.Convert((const char *)from);
488  dbgobjects(" conv '%s'", s);
489  free(txtData);
490  txtData = strdup(s);
491  }
492 }
493 
494 void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
495 {
496  int x = 0;
497  int y = Even ? 0 : 1;
498  uint8_t map2to4[ 4] = { 0x00, 0x07, 0x08, 0x0F };
499  uint8_t map2to8[ 4] = { 0x00, 0x77, 0x88, 0xFF };
500  uint8_t map4to8[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
501  const uint8_t *mapTable = NULL;
502  cBitStream bs(Data, Length * 8);
503  while (!bs.IsEOF()) {
504  switch (bs.GetBits(8)) {
505  case 0x10:
506  dbgpixel("2-bit / pixel code string<br>\n");
507  switch (Bitmap->Bpp()) {
508  case 8: mapTable = map2to8; break;
509  case 4: mapTable = map2to4; break;
510  default: mapTable = NULL; break;
511  }
512  while (Decode2BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
513  ;
514  bs.ByteAlign();
515  break;
516  case 0x11:
517  dbgpixel("4-bit / pixel code string<br>\n");
518  switch (Bitmap->Bpp()) {
519  case 8: mapTable = map4to8; break;
520  default: mapTable = NULL; break;
521  }
522  while (Decode4BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
523  ;
524  bs.ByteAlign();
525  break;
526  case 0x12:
527  dbgpixel("8-bit / pixel code string<br>\n");
528  while (Decode8BppCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF())
529  ;
530  break;
531  case 0x20:
532  dbgpixel("sub block 2 to 4 map<br>\n");
533  for (int i = 0; i < 4; ++i)
534  map2to4[i] = bs.GetBits(4);
535  break;
536  case 0x21:
537  dbgpixel("sub block 2 to 8 map<br>\n");
538  for (int i = 0; i < 4; ++i)
539  map2to8[i] = bs.GetBits(8);
540  break;
541  case 0x22:
542  dbgpixel("sub block 4 to 8 map<br>\n");
543  for (int i = 0; i < 16; ++i)
544  map4to8[i] = bs.GetBits(8);
545  break;
546  case 0xF0:
547  dbgpixel("end of object line<br>\n");
548  x = 0;
549  y += 2;
550  break;
551  case 0xFF:
552  dbgpixel("PGS code string, including EOLs<br>\n");
553  while (DecodePgsCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF()) {
554  x = 0;
555  y++;
556  }
557  break;
558  default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__);
559  }
560  }
561 }
562 
563 void cSubtitleObject::DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
564 {
565  if (nonModifyingColorFlag && Index == 1)
566  return;
567  for (int pos = x; pos < x + Length; pos++)
568  Bitmap->SetIndex(pos, y, Index);
569 }
570 
571 bool cSubtitleObject::Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
572 {
573  int rl = 0;
574  int color = 0;
575  uchar code = bs->GetBits(2);
576  if (code) {
577  color = code;
578  rl = 1;
579  }
580  else if (bs->GetBit()) { // switch_1
581  rl = bs->GetBits(3) + 3;
582  color = bs->GetBits(2);
583  }
584  else if (bs->GetBit()) // switch_2
585  rl = 1; //color 0
586  else {
587  switch (bs->GetBits(2)) { // switch_3
588  case 0:
589  return false;
590  case 1:
591  rl = 2; //color 0
592  break;
593  case 2:
594  rl = bs->GetBits(4) + 12;
595  color = bs->GetBits(2);
596  break;
597  case 3:
598  rl = bs->GetBits(8) + 29;
599  color = bs->GetBits(2);
600  break;
601  default: ;
602  }
603  }
604  if (MapTable)
605  color = MapTable[color];
606  DrawLine(Bitmap, px + x, py + y, color, rl);
607  x += rl;
608  return true;
609 }
610 
611 bool cSubtitleObject::Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
612 {
613  int rl = 0;
614  int color = 0;
615  uchar code = bs->GetBits(4);
616  if (code) {
617  color = code;
618  rl = 1;
619  }
620  else if (bs->GetBit() == 0) { // switch_1
621  code = bs->GetBits(3);
622  if (code)
623  rl = code + 2; //color 0
624  else
625  return false;
626  }
627  else if (bs->GetBit() == 0) { // switch_2
628  rl = bs->GetBits(2) + 4;
629  color = bs->GetBits(4);
630  }
631  else {
632  switch (bs->GetBits(2)) { // switch_3
633  case 0: // color 0
634  rl = 1;
635  break;
636  case 1: // color 0
637  rl = 2;
638  break;
639  case 2:
640  rl = bs->GetBits(4) + 9;
641  color = bs->GetBits(4);
642  break;
643  case 3:
644  rl = bs->GetBits(8) + 25;
645  color = bs->GetBits(4);
646  break;
647  }
648  }
649  if (MapTable)
650  color = MapTable[color];
651  DrawLine(Bitmap, px + x, py + y, color, rl);
652  x += rl;
653  return true;
654 }
655 
656 bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
657 {
658  int rl = 0;
659  int color = 0;
660  uchar code = bs->GetBits(8);
661  if (code) {
662  color = code;
663  rl = 1;
664  }
665  else if (bs->GetBit()) {
666  rl = bs->GetBits(7);
667  color = bs->GetBits(8);
668  }
669  else {
670  code = bs->GetBits(7);
671  if (code)
672  rl = code; // color 0
673  else
674  return false;
675  }
676  DrawLine(Bitmap, px + x, py + y, color, rl);
677  x += rl;
678  return true;
679 }
680 
681 bool cSubtitleObject::DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
682 {
683  while (!bs->IsEOF()) {
684  int color = bs->GetBits(8);
685  int rl = 1;
686  if (!color) {
687  int flags = bs->GetBits(8);
688  rl = flags & 0x3f;
689  if (flags & 0x40)
690  rl = (rl << 8) + bs->GetBits(8);
691  color = flags & 0x80 ? bs->GetBits(8) : 0;
692  }
693  if (rl > 0) {
694  DrawLine(Bitmap, px + x, py + y, color, rl);
695  x += rl;
696  }
697  else if (!rl)
698  return true;
699  }
700  return false;
701 }
702 
703 void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
704 {
705  if (objectCodingMethod == 0) { // coding of pixels
706  DecodeSubBlock(Bitmap, px, py, topData, topLength, true);
707  if (botLength)
708  DecodeSubBlock(Bitmap, px, py, botData, botLength, false);
709  else
710  DecodeSubBlock(Bitmap, px, py, topData, topLength, false);
711  }
712  else if (objectCodingMethod == 1) { // coded as a string of characters
713  if (txtData) {
714  //TODO couldn't we draw the text directly into Bitmap?
716  cBitmap tmp(font->Width(txtData), font->Height(), Bitmap->Bpp());
717  double factor = (double)lineHeight / font->Height();
718  tmp.DrawText(0, 0, txtData, Bitmap->Color(IndexFg), Bitmap->Color(IndexBg), font);
719  cBitmap *scaled = tmp.Scaled(factor, factor, true);
720  Bitmap->DrawBitmap(px, py, *scaled);
721  delete scaled;
722  delete font;
723  }
724  }
725 }
726 
727 // --- cSubtitleObjects ------------------------------------------------------
728 
729 class cSubtitleObjects : public cList<cSubtitleObject> {
730 public:
731  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
732  };
733 
735 {
736  for (cSubtitleObject *so = First(); so; so = Next(so)) {
737  if (so->ObjectId() == ObjectId)
738  return so;
739  }
740  if (!New)
741  return NULL;
742  cSubtitleObject *Object = new cSubtitleObject(ObjectId);
743  Add(Object);
744  return Object;
745 }
746 
747 // --- cSubtitleObjectRef ----------------------------------------------------
748 
750 protected:
751  int objectId;
758 public:
759  cSubtitleObjectRef(void);
761  int ObjectId(void) { return objectId; }
762  int ObjectType(void) { return objectType; }
768  };
769 
771 {
772  objectId = 0;
773  objectType = 0;
774  objectProviderFlag = 0;
779 }
780 
782 {
783  objectId = bs.GetBits(16);
784  objectType = bs.GetBits(2);
785  objectProviderFlag = bs.GetBits(2);
787  bs.SkipBits(4); // reserved
789  if (objectType == 0x01 || objectType == 0x02) {
792  }
793  else {
796  }
797  dbgregions("<b>objectref</b> id %d type %d flag %d x %d y %d fg %d bg %d<br>\n", objectId, objectType, objectProviderFlag, objectHorizontalPosition, objectVerticalPosition, foregroundPixelCode, backgroundPixelCode);
798 }
799 
800 // --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
801 
803 private:
804  int windowId;
806  int cropX;
807  int cropY;
808  int cropW;
809  int cropH;
810 public:
812 };
813 
816 {
817  objectId = bs.GetBits(16);
818  windowId = bs.GetBits(8);
819  compositionFlag = bs.GetBits(8);
820  bs.SkipBits(32); // skip absolute position, object is aligned to region
821  if ((compositionFlag & 0x80) != 0) {
822  cropX = bs.GetBits(16);
823  cropY = bs.GetBits(16);
824  cropW = bs.GetBits(16);
825  cropH = bs.GetBits(16);
826  }
827  else
828  cropX = cropY = cropW = cropH = 0;
829  dbgregions("<b>objectrefPgs</b> id %d flag %d x %d y %d cropX %d cropY %d cropW %d cropH %d<br>\n", objectId, compositionFlag, objectHorizontalPosition, objectVerticalPosition, cropX, cropY, cropW, cropH);
830 }
831 
832 // --- cSubtitleRegion -------------------------------------------------------
833 
834 class cSubtitleRegion : public cListObject {
835 private:
836  int regionId;
843  int clutId;
848 public:
850  void Parse(cBitStream &bs);
851  void ParsePgs(cBitStream &bs);
852  void SetDimensions(int Width, int Height);
853  int RegionId(void) { return regionId; }
855  bool RegionFillFlag(void) { return regionFillFlag; }
856  int RegionWidth(void) { return regionWidth; }
857  int RegionHeight(void) { return regionHeight; }
859  int RegionDepth(void) { return regionDepth; }
860  int ClutId(void) { return clutId; }
861  void Render(cBitmap *Bitmap, cSubtitleObjects *Objects);
862  };
863 
865 {
866  regionId = RegionId;
867  regionVersionNumber = -1;
868  regionFillFlag = false;
869  regionWidth = 0;
870  regionHeight = 0;
872  regionDepth = 0;
873  clutId = -1;
877 }
878 
880 {
881  int Version = bs.GetBits(4);
882 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
883  if (regionVersionNumber == Version)
884  return; // no update
885 #endif
886  regionVersionNumber = Version;
887  regionFillFlag = bs.GetBit();
888  bs.SkipBits(3); // reserved
889  regionWidth = bs.GetBits(16);
890  regionHeight = bs.GetBits(16);
891  regionLevelOfCompatibility = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
892  regionDepth = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
893  bs.SkipBits(2); // reserved
894  clutId = bs.GetBits(8);
898  bs.SkipBits(2); // reserved
899  dbgregions("<b>region</b> id %d version %d fill %d width %d height %d level %d depth %d clutId %d<br>\n", regionId, regionVersionNumber, regionFillFlag, regionWidth, regionHeight, regionLevelOfCompatibility, regionDepth, clutId);
900  // no objectRefs.Clear() here!
901  while (!bs.IsEOF())
903 }
904 
906 {
907  regionDepth = 8;
908  bs.SkipBits(8); // skip palette update flag
909  clutId = bs.GetBits(8);
910  dbgregions("<b>region</b> id %d version %d clutId %d<br>\n", regionId, regionVersionNumber, clutId);
911  int objects = bs.GetBits(8);
912  while (objects--)
914 }
915 
916 void cSubtitleRegion::SetDimensions(int Width, int Height)
917 {
918  regionWidth = Width;
919  regionHeight = Height;
920  dbgregions("<b>region</b> id %d width %d height %d<br>\n", regionId, regionWidth, regionHeight);
921 }
922 
924 {
925  if (regionFillFlag) {
926  switch (Bitmap->Bpp()) {
927  case 2: Bitmap->Fill(region2bitPixelCode); break;
928  case 4: Bitmap->Fill(region4bitPixelCode); break;
929  case 8: Bitmap->Fill(region8bitPixelCode); break;
930  default: dbgregions("unknown bpp %d (%s %d)<br>\n", Bitmap->Bpp(), __FUNCTION__, __LINE__);
931  }
932  }
933  for (cSubtitleObjectRef *sor = objectRefs.First(); sor; sor = objectRefs.Next(sor)) {
934  if (cSubtitleObject *so = Objects->GetObjectById(sor->ObjectId())) {
935  so->Render(Bitmap, sor->ObjectHorizontalPosition(), sor->ObjectVerticalPosition(), sor->ForegroundPixelCode(), sor->BackgroundPixelCode());
936  }
937  }
938 }
939 
940 // --- cSubtitleRegionRef ----------------------------------------------------
941 
943 private:
944  int regionId;
947 public:
948  cSubtitleRegionRef(int id, int x, int y);
950  int RegionId(void) { return regionId; }
953  };
954 
956 {
957  regionId = id;
960  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
961 }
963 {
964  regionId = bs.GetBits(8);
965  bs.SkipBits(8); // reserved
968  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
969 }
970 
971 // --- cDvbSubtitlePage ------------------------------------------------------
972 
974 private:
975  int pageId;
979  int64_t pts;
980  bool pending;
985 public:
987  void Parse(int64_t Pts, cBitStream &bs);
988  void ParsePgs(int64_t Pts, cBitStream &bs);
989  int PageId(void) { return pageId; }
990  int PageTimeout(void) { return pageTimeout; }
991  int PageVersionNumber(void) { return pageVersionNumber; }
992  int PageState(void) { return pageState; }
993  int64_t Pts(void) const { return pts; }
994  bool Pending(void) { return pending; }
995  cSubtitleObjects *Objects(void) { return &objects; }
996  tArea *GetAreas(int &NumAreas, double FactorX, double FactorY);
997  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
998  cSubtitleClut *GetClutById(int ClutId, bool New = false);
999  cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
1000  cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
1002  void SetPending(bool Pending) { pending = Pending; }
1003  };
1004 
1006 {
1007  pageId = PageId;
1008  pageTimeout = 0;
1009  pageVersionNumber = -1;
1010  pageState = -1;
1011  pts = -1;
1012  pending = false;
1013 }
1014 
1015 void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
1016 {
1017  if (Pts >= 0)
1018  pts = Pts;
1019  pageTimeout = bs.GetBits(8);
1020  int Version = bs.GetBits(4);
1021 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1022  if (pageVersionNumber == Version)
1023  return; // no update
1024 #endif
1025  pageVersionNumber = Version;
1026  pageState = bs.GetBits(2);
1027  switch (pageState) {
1028  case 0: // normal case - page update
1029  break;
1030  case 1: // acquisition point - page refresh
1031  regions.Clear();
1032  objects.Clear();
1033  break;
1034  case 2: // mode change - new page
1035  regions.Clear();
1036  cluts.Clear();
1037  objects.Clear();
1038  break;
1039  case 3: // reserved
1040  break;
1041  default: dbgpages("unknown page state: %d<br>\n", pageState);
1042  }
1043  bs.SkipBits(2); // reserved
1044  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1045  regionRefs.Clear();
1046  while (!bs.IsEOF())
1048  pending = true;
1049 }
1050 
1052 {
1053  if (Pts >= 0)
1054  pts = Pts;
1055  pageTimeout = 60000;
1056  int Version = bs.GetBits(16);
1057  if (pageVersionNumber == Version)
1058  return;
1059  pageVersionNumber = Version;
1060  pageState = bs.GetBits(2);
1061  switch (pageState) {
1062  case 0: // normal case - page update
1063  regions.Clear();
1064  break;
1065  case 1: // acquisition point - page refresh
1066  case 2: // epoch start - new page
1067  case 3: // epoch continue - new page
1068  regions.Clear();
1069  cluts.Clear();
1070  objects.Clear();
1071  break;
1072  default: dbgpages("unknown page state: %d<br>\n", pageState);
1073  }
1074  bs.SkipBits(6);
1075  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1076  regionRefs.Clear();
1077  pending = true;
1078 }
1079 
1080 tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY)
1081 {
1082  if (regions.Count() > 0) {
1083  NumAreas = regionRefs.Count();
1084  tArea *Areas = new tArea[NumAreas];
1085  tArea *a = Areas;
1086  for (cSubtitleRegionRef *srr = regionRefs.First(); srr; srr = regionRefs.Next(srr)) {
1087  if (cSubtitleRegion *sr = GetRegionById(srr->RegionId())) {
1088  a->x1 = int(round(FactorX * srr->RegionHorizontalAddress()));
1089  a->y1 = int(round(FactorY * srr->RegionVerticalAddress()));
1090  a->x2 = int(round(FactorX * (srr->RegionHorizontalAddress() + sr->RegionWidth() - 1)));
1091  a->y2 = int(round(FactorY * (srr->RegionVerticalAddress() + sr->RegionHeight() - 1)));
1092  a->bpp = sr->RegionDepth();
1093  while ((a->Width() & 3) != 0)
1094  a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
1095  }
1096  else
1097  a->x1 = a->y1 = a->x2 = a->y2 = a->bpp = 0;
1098  a++;
1099  }
1100  return Areas;
1101  }
1102  NumAreas = 0;
1103  return NULL;
1104 }
1105 
1107 {
1108  for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
1109  if (sc->ClutId() == ClutId)
1110  return sc;
1111  }
1112  if (!New)
1113  return NULL;
1114  cSubtitleClut *Clut = new cSubtitleClut(ClutId);
1115  cluts.Add(Clut);
1116  return Clut;
1117 }
1118 
1120 {
1121  for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
1122  if (sr->RegionId() == RegionId)
1123  return sr;
1124  }
1125  if (!New)
1126  return NULL;
1127  cSubtitleRegion *Region = new cSubtitleRegion(RegionId);
1128  regions.Add(Region);
1129  return Region;
1130 }
1131 
1133 {
1134  return objects.GetObjectById(ObjectId, New);
1135 }
1136 
1137 // --- cDvbSubtitleAssembler -------------------------------------------------
1138 
1140 private:
1142  int length;
1143  int pos;
1144  int size;
1145  bool Realloc(int Size);
1146 public:
1147  cDvbSubtitleAssembler(void);
1148  virtual ~cDvbSubtitleAssembler();
1149  void Reset(void);
1150  unsigned char *Get(int &Length);
1151  void Put(const uchar *Data, int Length);
1152  };
1153 
1155 {
1156  data = NULL;
1157  size = 0;
1158  Reset();
1159 }
1160 
1162 {
1163  free(data);
1164 }
1165 
1167 {
1168  length = 0;
1169  pos = 0;
1170 }
1171 
1173 {
1174  if (Size > size) {
1175  Size = max(Size, 2048);
1176  if (uchar *NewBuffer = (uchar *)realloc(data, Size)) {
1177  size = Size;
1178  data = NewBuffer;
1179  }
1180  else {
1181  esyslog("ERROR: can't allocate memory for subtitle assembler");
1182  length = 0;
1183  size = 0;
1184  free(data);
1185  data = NULL;
1186  return false;
1187  }
1188  }
1189  return true;
1190 }
1191 
1192 unsigned char *cDvbSubtitleAssembler::Get(int &Length)
1193 {
1194  if (length > pos + 5) {
1195  Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
1196  if (length >= pos + Length) {
1197  unsigned char *result = data + pos;
1198  pos += Length;
1199  return result;
1200  }
1201  }
1202  return NULL;
1203 }
1204 
1205 void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
1206 {
1207  if (Length && Realloc(length + Length)) {
1208  memcpy(data + length, Data, Length);
1209  length += Length;
1210  }
1211 }
1212 
1213 // --- cDvbSubtitleBitmaps ---------------------------------------------------
1214 
1216 private:
1217  int state;
1218  int64_t pts;
1219  int timeout;
1222  double osdFactorX;
1223  double osdFactorY;
1225 public:
1226  cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
1228  int State(void) { return state; }
1229  int64_t Pts(void) { return pts; }
1230  int Timeout(void) { return timeout; }
1231  void AddBitmap(cBitmap *Bitmap);
1232  bool HasBitmaps(void) { return bitmaps.Size(); }
1233  void Draw(cOsd *Osd);
1234  void DbgDump(int WindowWidth, int WindowHeight);
1235  };
1236 
1237 cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
1238 {
1239  state = State;
1240  pts = Pts;
1241  timeout = Timeout;
1242  areas = Areas;
1243  numAreas = NumAreas;
1244  osdFactorX = OsdFactorX;
1245  osdFactorY = OsdFactorY;
1246 }
1247 
1249 {
1250  delete[] areas;
1251  for (int i = 0; i < bitmaps.Size(); i++)
1252  delete bitmaps[i];
1253 }
1254 
1256 {
1257  bitmaps.Append(Bitmap);
1258 }
1259 
1261 {
1262  bool Scale = !(DoubleEqual(osdFactorX, 1.0) && DoubleEqual(osdFactorY, 1.0));
1263  bool AntiAlias = true;
1264  if (Scale && osdFactorX > 1.0 || osdFactorY > 1.0) {
1265  // Upscaling requires 8bpp:
1266  int Bpp[MAXOSDAREAS];
1267  for (int i = 0; i < numAreas; i++) {
1268  Bpp[i] = areas[i].bpp;
1269  areas[i].bpp = 8;
1270  }
1271  if (Osd->CanHandleAreas(areas, numAreas) != oeOk) {
1272  for (int i = 0; i < numAreas; i++)
1273  areas[i].bpp = Bpp[i];
1274  AntiAlias = false;
1275  }
1276  }
1277  if (State() == 0 || Osd->SetAreas(areas, numAreas) == oeOk) {
1278  for (int i = 0; i < bitmaps.Size(); i++) {
1279  cBitmap *b = bitmaps[i];
1280  Osd->DrawScaledBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b, osdFactorX, osdFactorY, AntiAlias);
1281  }
1282  Osd->Flush();
1283  }
1284 }
1285 
1286 void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
1287 {
1288  if (!SD.Active())
1289  return;
1290  SD.SetFirstPts(Pts());
1291  double STC = double(cDevice::PrimaryDevice()->GetSTC() - SD.FirstPts()) / 90000;
1292  double Start = double(Pts() - SD.FirstPts()) / 90000;
1293  double Duration = Timeout();
1294  double End = Start + Duration;
1295  cBitmap Bitmap(WindowWidth, WindowHeight, 8);
1296 #define DBGBACKGROUND 0xA0A0A0
1297  Bitmap.DrawRectangle(0, 0, WindowWidth - 1, WindowHeight - 1, DBGBACKGROUND);
1298  for (int i = 0; i < bitmaps.Size(); i++) {
1299  cBitmap *b = bitmaps[i];
1300  Bitmap.DrawBitmap(b->X0(), b->Y0(), *b);
1301  }
1302  cString ImgName = SD.WriteJpeg(&Bitmap);
1303 #define BORDER //" border=1"
1304  SD.WriteHtml("<p>%s<br>", State() == 0 ? "page update" : State() == 1 ? "page refresh" : State() == 2 ? "new page" : "???");
1305  SD.WriteHtml("<table" BORDER "><tr><td>");
1306  SD.WriteHtml("%.2f", STC);
1307  SD.WriteHtml("</td><td>");
1308  SD.WriteHtml("<img src=\"%s\">", *ImgName);
1309  SD.WriteHtml("</td><td style=\"height:100%%\"><table" BORDER " style=\"height:100%%\">");
1310  SD.WriteHtml("<tr><td valign=top><b>%.2f</b></td></tr>", Start);
1311  SD.WriteHtml("<tr><td valign=middle>%.2f</td></tr>", Duration);
1312  SD.WriteHtml("<tr><td valign=bottom>%.2f</td></tr>", End);
1313  SD.WriteHtml("</table></td>");
1314  SD.WriteHtml("</tr></table>\n");
1315 }
1316 
1317 // --- cDvbSubtitleConverter -------------------------------------------------
1318 
1320 
1322 :cThread("subtitle converter")
1323 {
1325  osd = NULL;
1326  frozen = false;
1327  ddsVersionNumber = -1;
1328  displayWidth = windowWidth = 720;
1329  displayHeight = windowHeight = 576;
1334  SD.Reset();
1335  Start();
1336 }
1337 
1339 {
1340  Cancel(3);
1341  delete dvbSubtitleAssembler;
1342  delete osd;
1343  delete bitmaps;
1344  delete pages;
1345 }
1346 
1348 {
1349  setupLevel++;
1350 }
1351 
1353 {
1354  dbgconverter("converter reset -----------------------<br>\n");
1356  Lock();
1357  pages->Clear();
1358  bitmaps->Clear();
1359  DELETENULL(osd);
1360  frozen = false;
1361  ddsVersionNumber = -1;
1362  displayWidth = windowWidth = 720;
1363  displayHeight = windowHeight = 576;
1366  Unlock();
1367 }
1368 
1370 {
1371  if (Data && Length > 8) {
1372  int PayloadOffset = PesPayloadOffset(Data);
1373  int SubstreamHeaderLength = 4;
1374  bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1375 
1376  // Compatibility mode for old subtitles plugin:
1377  if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1378  PayloadOffset--;
1379  SubstreamHeaderLength = 1;
1380  ResetSubtitleAssembler = Data[8] >= 5;
1381  }
1382 
1383  if (Length > PayloadOffset + SubstreamHeaderLength) {
1384  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1385  if (pts >= 0)
1386  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1387  const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1388  int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1389  if (ResetSubtitleAssembler)
1391 
1392  if (length > 3) {
1393  if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1394  dvbSubtitleAssembler->Put(data + 2, length - 2);
1395  else
1396  dvbSubtitleAssembler->Put(data, length);
1397 
1398  int Count;
1399  while (true) {
1400  unsigned char *b = dvbSubtitleAssembler->Get(Count);
1401  if (b && b[0] == 0x0F) {
1402  if (ExtractSegment(b, Count, pts) == -1)
1403  break;
1404  }
1405  else
1406  break;
1407  }
1408  }
1409  }
1410  return Length;
1411  }
1412  return 0;
1413 }
1414 
1415 int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1416 {
1417  if (Data && Length > 8) {
1418  int PayloadOffset = PesPayloadOffset(Data);
1419  if (Length > PayloadOffset) {
1420  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1421  if (pts >= 0)
1422  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1423  const uchar *data = Data + PayloadOffset;
1424  int length = Length - PayloadOffset;
1425  if (length > 0) {
1426  if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1427  data += 2;
1428  length -= 2;
1429  }
1430  const uchar *b = data;
1431  while (length > 0) {
1432  if (b[0] == STUFFING_SEGMENT)
1433  break;
1434  int n;
1435  if (b[0] == 0x0F)
1436  n = ExtractSegment(b, length, pts);
1437  else
1438  n = ExtractPgsSegment(b, length, pts);
1439  if (n < 0)
1440  break;
1441  b += n;
1442  length -= n;
1443  }
1444  }
1445  }
1446  return Length;
1447  }
1448  return 0;
1449 }
1450 
1451 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
1452 
1454 {
1455  int LastSetupLevel = setupLevel;
1456  cTimeMs Timeout;
1457  while (Running()) {
1458  int WaitMs = 100;
1459  if (!frozen) {
1460  LOCK_THREAD;
1461  if (osd) {
1462  int NewSetupLevel = setupLevel;
1463  if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
1464  dbgoutput("closing osd<br>\n");
1465  DELETENULL(osd);
1466  }
1467  LastSetupLevel = NewSetupLevel;
1468  }
1469  for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
1470  // Calculate the Delta between the STC (the current timestamp of the video)
1471  // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1472  // A negative Delta means that the bitmap will be presented in the future:
1473  int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1474  int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits
1475  if (Delta > (int64_t(1) << 31))
1476  Delta -= (int64_t(1) << 32);
1477  else if (Delta < -((int64_t(1) << 31) - 1))
1478  Delta += (int64_t(1) << 32);
1479  Delta /= 90; // STC and PTS are in 1/90000s
1480  if (Delta >= 0) { // found a bitmap that shall be displayed...
1481  if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
1482  if (!sb->HasBitmaps()) {
1483  Timeout.Set();
1484  WaitMs = 0;
1485  }
1486  else if (AssertOsd()) {
1487  dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1488  sb->Draw(osd);
1489  Timeout.Set(sb->Timeout() * 1000);
1490  dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
1491  }
1492  }
1493  else
1494  WaitMs = 0; // bitmap already timed out, so try next one immediately
1495  dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1496  bitmaps->Del(sb);
1497  break;
1498  }
1499  }
1500  }
1501  cCondWait::SleepMs(WaitMs);
1502  }
1503 }
1504 
1506 {
1507  int OsdWidth, OsdHeight;
1508  double OsdAspect;
1509  int VideoWidth, VideoHeight;
1510  double VideoAspect;
1511  cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1512  cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
1513  if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1514  osdFactorX = osdFactorY = 1.0;
1515  osdDeltaX = osdDeltaY = 0;
1516  }
1517  else {
1518  osdFactorX = osdFactorY = min(double(OsdWidth) / displayWidth, double(OsdHeight) / displayHeight);
1519  osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1520  osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1521  }
1522 }
1523 
1525 {
1526  LOCK_THREAD;
1527  if (!osd) {
1528  SetOsdData();
1530  }
1531  return osd != NULL;
1532 }
1533 
1535 {
1536  for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1537  if (sp->PageId() == PageId)
1538  return sp;
1539  }
1540  if (!New)
1541  return NULL;
1542  cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1543  pages->Add(Page);
1544  return Page;
1545 }
1546 
1547 int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1548 {
1549  cBitStream bs(Data, Length * 8);
1550  if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1551  int segmentType = bs.GetBits(8);
1552  if (segmentType == STUFFING_SEGMENT)
1553  return -1;
1554  LOCK_THREAD;
1555  cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1556  int segmentLength = bs.GetBits(16);
1557  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1558  return -1;
1559  switch (segmentType) {
1560  case PAGE_COMPOSITION_SEGMENT: {
1561  if (page->Pending()) {
1562  dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
1563  FinishPage(page);
1564  }
1565  dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
1566  page->Parse(Pts, bs);
1568  break;
1569  }
1571  dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
1572  cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1573  region->Parse(bs);
1574  break;
1575  }
1576  case CLUT_DEFINITION_SEGMENT: {
1577  dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
1578  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1579  clut->Parse(bs);
1580  break;
1581  }
1582  case OBJECT_DATA_SEGMENT: {
1583  dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
1584  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1585  object->Parse(bs);
1586  break;
1587  }
1589  dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
1590  int version = bs.GetBits(4);
1591 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1592  if (version == ddsVersionNumber)
1593  break; // no update
1594 #endif
1595  bool displayWindowFlag = bs.GetBit();
1598  bs.SkipBits(3); // reserved
1599  displayWidth = windowWidth = bs.GetBits(16) + 1;
1600  displayHeight = windowHeight = bs.GetBits(16) + 1;
1601  if (displayWindowFlag) {
1602  windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1603  windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1604  windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1605  windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1606  }
1607  SetOsdData();
1608  ddsVersionNumber = version;
1609  dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
1610  break;
1611  }
1613  dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
1614  bs.SkipBits(4); // dss_version_number
1615  bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1616  bs.SkipBits(3); // reserved
1617  bs.SkipBits(8); // page_default_disparity_shift
1618  if (disparity_shift_update_sequence_page_flag) {
1619  bs.SkipBits(8); // disparity_shift_update_sequence_length
1620  bs.SkipBits(24); // interval_duration[23..0]
1621  int division_period_count = bs.GetBits(8);
1622  for (int i = 0; i < division_period_count; ++i) {
1623  bs.SkipBits(8); // interval_count
1624  bs.SkipBits(8); // disparity_shift_update_integer_part
1625  }
1626  }
1627  while (!bs.IsEOF()) {
1628  bs.SkipBits(8); // region_id
1629  bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1630  bs.SkipBits(5); // reserved
1631  int number_of_subregions_minus_1 = bs.GetBits(2);
1632  for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1633  if (number_of_subregions_minus_1 > 0) {
1634  bs.SkipBits(16); // subregion_horizontal_position
1635  bs.SkipBits(16); // subregion_width
1636  }
1637  bs.SkipBits(8); // subregion_disparity_shift_integer_part
1638  bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1639  bs.SkipBits(4); // reserved
1640  if (disparity_shift_update_sequence_region_flag) {
1641  bs.SkipBits(8); // disparity_shift_update_sequence_length
1642  bs.SkipBits(24); // interval_duration[23..0]
1643  int division_period_count = bs.GetBits(8);
1644  for (int i = 0; i < division_period_count; ++i) {
1645  bs.SkipBits(8); // interval_count
1646  bs.SkipBits(8); // disparity_shift_update_integer_part
1647  }
1648  }
1649  }
1650  }
1651  break;
1652  }
1654  dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
1655  FinishPage(page);
1656  page->SetPending(false);
1657  break;
1658  }
1659  default:
1660  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1661  }
1662  return bs.Length() / 8;
1663  }
1664  return -1;
1665 }
1666 
1667 int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
1668 {
1669  cBitStream bs(Data, Length * 8);
1670  if (Length >= 3) {
1671  int segmentType = bs.GetBits(8);
1672  int segmentLength = bs.GetBits(16);
1673  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1674  return -1;
1675  LOCK_THREAD;
1676  cDvbSubtitlePage *page = GetPageById(0, true);
1677  switch (segmentType) {
1678  case PGS_PRESENTATION_SEGMENT: {
1679  if (page->Pending()) {
1680  dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
1681  FinishPage(page);
1682  }
1683  dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
1684  displayWidth = windowWidth = bs.GetBits(16);
1685  displayHeight = windowHeight = bs.GetBits(16);
1686  bs.SkipBits(8);
1687  page->ParsePgs(Pts, bs);
1689  cSubtitleRegion *region = page->GetRegionById(0, true);
1690  region->ParsePgs(bs);
1691  break;
1692  }
1693  case PGS_WINDOW_SEGMENT: {
1694  bs.SkipBits(16);
1695  int regionHorizontalAddress = bs.GetBits(16);
1696  int regionVerticalAddress = bs.GetBits(16);
1697  int regionWidth = bs.GetBits(16);
1698  int regionHeight = bs.GetBits(16);
1699  cSubtitleRegion *region = page->GetRegionById(0, true);
1700  region->SetDimensions(regionWidth, regionHeight);
1701  page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
1702  dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
1703  break;
1704  }
1705  case PGS_PALETTE_SEGMENT: {
1706  dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
1707  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1708  clut->ParsePgs(bs);
1709  break;
1710  }
1711  case PGS_OBJECT_SEGMENT: {
1712  dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
1713  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1714  object->ParsePgs(bs);
1715  break;
1716  }
1717  case PGS_DISPLAY_SEGMENT: {
1718  dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
1719  FinishPage(page);
1720  page->SetPending(false);
1721  break;
1722  }
1723  default:
1724  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1725  return -1;
1726  }
1727  return bs.Length() / 8;
1728  }
1729  return -1;
1730 }
1731 
1733 {
1734  if (!AssertOsd())
1735  return;
1736  int NumAreas;
1737  tArea *Areas = Page->GetAreas(NumAreas, osdFactorX, osdFactorY);
1738  int Bpp = 8;
1739  bool Reduced = false;
1740  while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) {
1741  dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(Areas, NumAreas));
1742  int HalfBpp = Bpp / 2;
1743  if (HalfBpp >= 2) {
1744  for (int i = 0; i < NumAreas; i++) {
1745  if (Areas[i].bpp >= Bpp) {
1746  Areas[i].bpp = HalfBpp;
1747  Reduced = true;
1748  }
1749  }
1750  Bpp = HalfBpp;
1751  }
1752  else
1753  return; // unable to draw bitmaps
1754  }
1755  cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY);
1756  bitmaps->Add(Bitmaps);
1757  for (int i = 0; i < NumAreas; i++) {
1758  if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1759  if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1760  if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1761  cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1762  bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1763  sr->Render(bm, Page->Objects());
1764  if (Reduced) {
1765  if (sr->RegionDepth() != Areas[i].bpp) {
1766  if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1767  //TODO this is untested - didn't have any such subtitle stream
1768  cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1769  dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1770  bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1771  }
1772  else {
1773  dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1774  bm->ShrinkBpp(Areas[i].bpp);
1775  }
1776  }
1777  }
1778  bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1779  Bitmaps->AddBitmap(bm);
1780  }
1781  }
1782  }
1783  }
1784  if (DebugPages)
1785  Bitmaps->DbgDump(windowWidth, windowHeight);
1786 }
void SkipBit(void)
Definition: tools.h:359
const uint8_t * GetData(void) const
Definition: tools.h:364
int Index(void) const
Definition: tools.h:363
void WordAlign(void)
Definition: tools.c:1437
bool SetLength(int Length)
Definition: tools.c:1444
int Length(void) const
Definition: tools.h:362
bool IsEOF(void) const
Definition: tools.h:360
void SkipBits(int n)
Definition: tools.h:358
uint32_t GetBits(int n)
Definition: tools.c:1422
void ByteAlign(void)
Definition: tools.c:1430
int GetBit(void)
Definition: tools.c:1413
Definition: osd.h:169
void ShrinkBpp(int NewBpp)
Shrinks the color depth of the bitmap to NewBpp by keeping only the 2^NewBpp most frequently used col...
Definition: osd.c:796
void SetOffset(int X0, int Y0)
Sets the offset of this bitmap to the given values.
Definition: osd.h:195
void ReduceBpp(const cPalette &Palette)
Reduces the color depth of the bitmap to that of the given Palette.
Definition: osd.c:765
int Height(void) const
Definition: osd.h:189
bool Dirty(int &x1, int &y1, int &x2, int &y2)
Tells whether there is a dirty area and returns the bounding rectangle of that area (relative to the ...
Definition: osd.c:342
cBitmap * Scaled(double FactorX, double FactorY, bool AntiAlias=false) const
Creates a copy of this bitmap, scaled by the given factors.
Definition: osd.c:838
int X0(void) const
Definition: osd.h:186
tColor GetColor(int x, int y) const
Returns the color at the given coordinates.
Definition: osd.h:277
void SetIndex(int x, int y, tIndex Index)
Sets the index at the given coordinates to Index.
Definition: osd.c:500
void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
Draws a filled rectangle defined by the upper left (x1, y1) and lower right (x2, y2) corners with the...
Definition: osd.c:611
void Clean(void)
Marks the dirty area as clean.
Definition: osd.c:354
void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg=0, tColor ColorBg=0, bool ReplacePalette=false, bool Overlay=false)
Sets the pixels in this bitmap with the data from the given Bitmap, putting the upper left corner of ...
Definition: osd.c:533
void Fill(tIndex Index)
Fills the bitmap data with the given Index.
Definition: osd.c:515
void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width=0, int Height=0, int Alignment=taDefault)
Draws the given string at coordinates (x, y) with the given foreground and background color and font.
Definition: osd.c:562
int Y0(void) const
Definition: osd.h:187
int Width(void) const
Definition: osd.h:188
static const char * SystemCharacterTable(void)
Definition: tools.h:170
const char * Convert(const char *From, char *To=NULL, size_t ToLength=0)
Converts the given Text from FromCode to ToCode (as set in the constructor).
Definition: tools.c:993
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:72
virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect)
Returns the Width, Height and VideoAspect ratio of the currently displayed video material.
Definition: device.c:519
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles.
Definition: device.c:1231
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:148
virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect)
Returns the Width, Height and PixelAspect ratio the OSD should use to best fit the resolution of the ...
Definition: device.c:526
virtual ~cDvbSubtitleAssembler()
Definition: dvbsubtitle.c:1161
void Put(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1205
bool Realloc(int Size)
Definition: dvbsubtitle.c:1172
unsigned char * Get(int &Length)
Definition: dvbsubtitle.c:1192
void Draw(cOsd *Osd)
Definition: dvbsubtitle.c:1260
int64_t Pts(void)
Definition: dvbsubtitle.c:1229
bool HasBitmaps(void)
Definition: dvbsubtitle.c:1232
void DbgDump(int WindowWidth, int WindowHeight)
Definition: dvbsubtitle.c:1286
cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
Definition: dvbsubtitle.c:1237
void AddBitmap(cBitmap *Bitmap)
Definition: dvbsubtitle.c:1255
cVector< cBitmap * > bitmaps
Definition: dvbsubtitle.c:1224
int Convert(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1415
cList< cDvbSubtitleBitmaps > * bitmaps
Definition: dvbsubtitle.h:41
void FinishPage(cDvbSubtitlePage *Page)
Definition: dvbsubtitle.c:1732
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbsubtitle.c:1453
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1667
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1547
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition: dvbsubtitle.h:26
cList< cDvbSubtitlePage > * pages
Definition: dvbsubtitle.h:40
static int setupLevel
Definition: dvbsubtitle.h:25
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
Definition: dvbsubtitle.c:1534
virtual ~cDvbSubtitleConverter()
Definition: dvbsubtitle.c:1338
static void SetupChanged(void)
Definition: dvbsubtitle.c:1347
int ConvertFragments(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1369
int PageId(void)
Definition: dvbsubtitle.c:989
bool Pending(void)
Definition: dvbsubtitle.c:994
cSubtitleObjects objects
Definition: dvbsubtitle.c:981
cList< cSubtitleRegion > regions
Definition: dvbsubtitle.c:983
cSubtitleClut * GetClutById(int ClutId, bool New=false)
Definition: dvbsubtitle.c:1106
void SetPending(bool Pending)
Definition: dvbsubtitle.c:1002
cSubtitleObjects * Objects(void)
Definition: dvbsubtitle.c:995
cDvbSubtitlePage(int PageId)
Definition: dvbsubtitle.c:1005
tArea * GetAreas(int &NumAreas, double FactorX, double FactorY)
Definition: dvbsubtitle.c:1080
void Parse(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1015
int PageState(void)
Definition: dvbsubtitle.c:992
int PageTimeout(void)
Definition: dvbsubtitle.c:990
cList< cSubtitleClut > cluts
Definition: dvbsubtitle.c:982
int PageVersionNumber(void)
Definition: dvbsubtitle.c:991
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:1132
void AddRegionRef(cSubtitleRegionRef *rf)
Definition: dvbsubtitle.c:1001
cList< cSubtitleRegionRef > regionRefs
Definition: dvbsubtitle.c:984
void ParsePgs(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1051
cSubtitleRegionRef * GetRegionRefByIndex(int RegionRefIndex)
Definition: dvbsubtitle.c:1000
cSubtitleRegion * GetRegionById(int RegionId, bool New=false)
Definition: dvbsubtitle.c:1119
int64_t Pts(void) const
Definition: dvbsubtitle.c:993
Definition: font.h:37
virtual int Width(void) const =0
Returns the original character width as requested when the font was created, or 0 if the default widt...
static cFont * CreateFont(const char *Name, int CharHeight, int CharWidth=0)
Creates a new font object with the given Name and makes its characters CharHeight pixels high.
Definition: font.c:428
virtual int Height(void) const =0
Returns the height of this font in pixel (all characters have the same height).
virtual void Clear(void)
Definition: tools.c:2235
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2190
int Count(void) const
Definition: tools.h:594
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2158
int Index(void) const
Definition: tools.c:2078
Definition: tools.h:598
const cSubtitleObject * Next(const cSubtitleObject *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:617
const cSubtitleObject * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:610
const T * Get(int Index) const
Returns the list element at the given Index, or NULL if no such element exists.
Definition: tools.h:607
Definition: thread.h:67
static cOsd * NewOsd(int Left, int Top, uint Level=OSD_LEVEL_DEFAULT)
Returns a pointer to a newly created cOsd object, which will be located at the given coordinates.
Definition: osd.c:2215
The cOsd class is the interface to the "On Screen Display".
Definition: osd.h:729
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition: osd.c:2029
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition: osd.c:2007
virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition: osd.c:2141
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition: osd.c:2191
Definition: osd.h:88
tColor Color(int Index) const
Returns the color at the given Index.
Definition: osd.h:119
void Replace(const cPalette &Palette)
Replaces the colors of this palette with the colors from the given palette.
Definition: osd.c:208
void SetColor(int Index, tColor Color)
Sets the palette entry at Index to Color.
Definition: osd.c:172
int Bpp(void) const
Definition: osd.h:111
int SubtitleFgTransparency
Definition: config.h:289
int FontOsdSize
Definition: config.h:334
int SubtitleOffset
Definition: config.h:288
int SubtitleBgTransparency
Definition: config.h:289
char FontOsd[MAXFONTNAME]
Definition: config.h:328
Definition: tools.h:174
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1133
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:230
int ClutId(void)
Definition: dvbsubtitle.c:156
int clutVersionNumber
Definition: dvbsubtitle.c:146
cPalette palette4
Definition: dvbsubtitle.c:148
cPalette palette2
Definition: dvbsubtitle.c:147
tColor yuv2rgb(int Y, int Cb, int Cr)
Definition: dvbsubtitle.c:302
void SetColor(int Bpp, int Index, tColor Color)
Definition: dvbsubtitle.c:318
int ClutVersionNumber(void)
Definition: dvbsubtitle.c:157
const cPalette * GetPalette(int Bpp)
Definition: dvbsubtitle.c:328
cSubtitleClut(int ClutId)
Definition: dvbsubtitle.c:161
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:277
cPalette palette8
Definition: dvbsubtitle.c:149
cString WriteJpeg(const cBitmap *Bitmap, int MaxX=0, int MaxY=0)
Definition: dvbsubtitle.c:92
void SetFactor(double Factor)
Definition: dvbsubtitle.c:79
bool Active(void)
Definition: dvbsubtitle.c:76
int64_t FirstPts(void)
Definition: dvbsubtitle.c:77
cSubtitleDebug(void)
Definition: dvbsubtitle.c:74
void WriteHtml(const char *Format,...)
Definition: dvbsubtitle.c:124
void Reset(void)
Definition: dvbsubtitle.c:84
void SetFirstPts(int64_t FirstPts)
Definition: dvbsubtitle.c:78
int64_t firstPts
Definition: dvbsubtitle.c:70
cSubtitleObjectRefPgs(cBitStream &bs)
Definition: dvbsubtitle.c:814
int ObjectType(void)
Definition: dvbsubtitle.c:762
int ObjectProviderFlag(void)
Definition: dvbsubtitle.c:763
int ForegroundPixelCode(void)
Definition: dvbsubtitle.c:766
int ObjectVerticalPosition(void)
Definition: dvbsubtitle.c:765
int ObjectHorizontalPosition(void)
Definition: dvbsubtitle.c:764
int BackgroundPixelCode(void)
Definition: dvbsubtitle.c:767
int ObjectId(void)
Definition: dvbsubtitle.c:761
cSubtitleObject(int ObjectId)
Definition: dvbsubtitle.c:373
int ObjectId(void)
Definition: dvbsubtitle.c:366
bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:611
void DecodeCharacterString(const uchar *Data, int NumberOfCodes)
Definition: dvbsubtitle.c:466
bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:571
bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:656
void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
Definition: dvbsubtitle.c:563
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:443
bool nonModifyingColorFlag
Definition: dvbsubtitle.c:346
void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
Definition: dvbsubtitle.c:494
void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
Definition: dvbsubtitle.c:703
int ObjectVersionNumber(void)
Definition: dvbsubtitle.c:367
bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:681
int ObjectCodingMethod(void)
Definition: dvbsubtitle.c:368
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:395
bool NonModifyingColorFlag(void)
Definition: dvbsubtitle.c:369
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:734
cSubtitleRegionRef(int id, int x, int y)
Definition: dvbsubtitle.c:955
int RegionId(void)
Definition: dvbsubtitle.c:950
int RegionHorizontalAddress(void)
Definition: dvbsubtitle.c:951
int RegionVerticalAddress(void)
Definition: dvbsubtitle.c:952
int ClutId(void)
Definition: dvbsubtitle.c:860
cList< cSubtitleObjectRef > objectRefs
Definition: dvbsubtitle.c:847
int RegionWidth(void)
Definition: dvbsubtitle.c:856
int regionLevelOfCompatibility
Definition: dvbsubtitle.c:841
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:879
int RegionHeight(void)
Definition: dvbsubtitle.c:857
bool RegionFillFlag(void)
Definition: dvbsubtitle.c:855
void SetDimensions(int Width, int Height)
Definition: dvbsubtitle.c:916
int RegionId(void)
Definition: dvbsubtitle.c:853
int RegionDepth(void)
Definition: dvbsubtitle.c:859
void Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
Definition: dvbsubtitle.c:923
int RegionVersionNumber(void)
Definition: dvbsubtitle.c:854
cSubtitleRegion(int RegionId)
Definition: dvbsubtitle.c:864
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:905
int RegionLevelOfCompatibility(void)
Definition: dvbsubtitle.c:858
Definition: thread.h:79
void Unlock(void)
Definition: thread.h:95
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
void Lock(void)
Definition: thread.h:94
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
Definition: tools.h:367
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:776
bool TimedOut(void) const
Definition: tools.c:781
int Size(void) const
Definition: tools.h:721
virtual void Append(T Data)
Definition: tools.h:741
cSetup Setup
Definition: config.c:372
#define PGS_PALETTE_SEGMENT
Definition: dvbsubtitle.c:28
#define DBGBITMAPWIDTH
Definition: dvbsubtitle.c:60
#define OBJECT_DATA_SEGMENT
Definition: dvbsubtitle.c:22
#define dbgpages(a...)
Definition: dvbsubtitle.c:50
#define DBGBACKGROUND
#define LimitTo32Bit(n)
Definition: dvbsubtitle.c:1451
#define PGS_OBJECT_SEGMENT
Definition: dvbsubtitle.c:29
static bool DebugNormal
Definition: dvbsubtitle.c:37
#define CLUT_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:21
#define dbgsegments(a...)
Definition: dvbsubtitle.c:54
static bool DebugSegments
Definition: dvbsubtitle.c:44
#define END_OF_DISPLAY_SET_SEGMENT
Definition: dvbsubtitle.c:25
static bool DebugVerbose
Definition: dvbsubtitle.c:38
#define dbgpixel(a...)
Definition: dvbsubtitle.c:55
#define dbgcluts(a...)
Definition: dvbsubtitle.c:56
#define dbgregions(a...)
Definition: dvbsubtitle.c:51
#define REGION_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:20
#define DBGMAXBITMAPS
Definition: dvbsubtitle.c:59
static bool DebugCluts
Definition: dvbsubtitle.c:46
#define BORDER
#define dbgdisplay(a...)
Definition: dvbsubtitle.c:49
#define PAGE_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:19
#define STUFFING_SEGMENT
Definition: dvbsubtitle.c:26
#define PGS_WINDOW_SEGMENT
Definition: dvbsubtitle.c:31
#define dbgobjects(a...)
Definition: dvbsubtitle.c:52
static bool DebugRegions
Definition: dvbsubtitle.c:41
static bool DebugDisplay
Definition: dvbsubtitle.c:39
#define PGS_DISPLAY_SEGMENT
Definition: dvbsubtitle.c:32
static bool DebugConverter
Definition: dvbsubtitle.c:43
static bool DebugObjects
Definition: dvbsubtitle.c:42
static cSubtitleDebug SD
Definition: dvbsubtitle.c:139
#define DISPARITY_SIGNALING_SEGMENT
Definition: dvbsubtitle.c:24
#define dbgconverter(a...)
Definition: dvbsubtitle.c:53
#define PGS_PRESENTATION_SEGMENT
Definition: dvbsubtitle.c:30
static bool DebugPixel
Definition: dvbsubtitle.c:45
#define DISPLAY_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:23
static bool DebugOutput
Definition: dvbsubtitle.c:47
static bool DebugPages
Definition: dvbsubtitle.c:40
#define dbgoutput(a...)
Definition: dvbsubtitle.c:57
uint32_t tColor
Definition: font.h:29
uint8_t tIndex
Definition: font.h:31
const char * getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte)
Definition: si.c:364
#define OSD_LEVEL_SUBTITLES
Definition: osd.h:22
@ oeOk
Definition: osd.h:44
tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
Definition: osd.h:58
#define MAXOSDAREAS
Definition: osd.h:716
int PesPayloadOffset(const uchar *p)
Definition: remux.h:184
bool PesHasPts(const uchar *p)
Definition: remux.h:189
int64_t PesGetPts(const uchar *p)
Definition: remux.h:199
Definition: osd.h:298
int Width(void) const
Definition: osd.h:301
int bpp
Definition: osd.h:300
int x2
Definition: osd.h:299
int y1
Definition: osd.h:299
int x1
Definition: osd.h:299
int y2
Definition: osd.h:299
#define LOCK_THREAD
Definition: thread.h:167
uchar * RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
Converts the given Memory to a JPEG image and returns a pointer to the resulting image.
Definition: tools.c:1304
T constrain(T v, T l, T h)
Definition: tools.h:66
#define LOG_ERROR_STR(s)
Definition: tools.h:40
unsigned char uchar
Definition: tools.h:31
#define MALLOC(type, size)
Definition: tools.h:47
void DELETENULL(T *&p)
Definition: tools.h:49
bool DoubleEqual(double a, double b)
Definition: tools.h:93
T min(T a, T b)
Definition: tools.h:58
T max(T a, T b)
Definition: tools.h:59
#define esyslog(a...)
Definition: tools.h:35