vdr  2.2.0
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 3.10 2015/01/20 14:53:57 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:
362  cSubtitleObject(int ObjectId);
363  ~cSubtitleObject();
364  void Parse(cBitStream &bs);
365  void ParsePgs(cBitStream &bs);
366  int ObjectId(void) { return objectId; }
367  int ObjectVersionNumber(void) { return objectVersionNumber; }
368  int ObjectCodingMethod(void) { return objectCodingMethod; }
369  bool NonModifyingColorFlag(void) { return nonModifyingColorFlag; }
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);
404  nonModifyingColorFlag = bs.GetBit();
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  bool singleByte;
483  const uchar *from = (uchar *)txt;
484  int len = NumberOfCodes;
485  const char *CharacterTable = SI::getCharacterTable(from, len, &singleByte);
486  dbgobjects(" table %s single %d raw '%s'", CharacterTable, singleByte, from);
487  cCharSetConv conv(CharacterTable, cCharSetConv::SystemCharacterTable());
488  const char *s = conv.Convert((const char *)from);
489  dbgobjects(" conv '%s'", s);
490  free(txtData);
491  txtData = strdup(s);
492  }
493 }
494 
495 void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
496 {
497  int x = 0;
498  int y = Even ? 0 : 1;
499  uint8_t map2to4[ 4] = { 0x00, 0x07, 0x08, 0x0F };
500  uint8_t map2to8[ 4] = { 0x00, 0x77, 0x88, 0xFF };
501  uint8_t map4to8[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
502  const uint8_t *mapTable = NULL;
503  cBitStream bs(Data, Length * 8);
504  while (!bs.IsEOF()) {
505  switch (bs.GetBits(8)) {
506  case 0x10:
507  dbgpixel("2-bit / pixel code string<br>\n");
508  switch (Bitmap->Bpp()) {
509  case 8: mapTable = map2to8; break;
510  case 4: mapTable = map2to4; break;
511  default: mapTable = NULL; break;
512  }
513  while (Decode2BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
514  ;
515  bs.ByteAlign();
516  break;
517  case 0x11:
518  dbgpixel("4-bit / pixel code string<br>\n");
519  switch (Bitmap->Bpp()) {
520  case 8: mapTable = map4to8; break;
521  default: mapTable = NULL; break;
522  }
523  while (Decode4BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
524  ;
525  bs.ByteAlign();
526  break;
527  case 0x12:
528  dbgpixel("8-bit / pixel code string<br>\n");
529  while (Decode8BppCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF())
530  ;
531  break;
532  case 0x20:
533  dbgpixel("sub block 2 to 4 map<br>\n");
534  for (int i = 0; i < 4; ++i)
535  map2to4[i] = bs.GetBits(4);
536  break;
537  case 0x21:
538  dbgpixel("sub block 2 to 8 map<br>\n");
539  for (int i = 0; i < 4; ++i)
540  map2to8[i] = bs.GetBits(8);
541  break;
542  case 0x22:
543  dbgpixel("sub block 4 to 8 map<br>\n");
544  for (int i = 0; i < 16; ++i)
545  map4to8[i] = bs.GetBits(8);
546  break;
547  case 0xF0:
548  dbgpixel("end of object line<br>\n");
549  x = 0;
550  y += 2;
551  break;
552  case 0xFF:
553  dbgpixel("PGS code string, including EOLs<br>\n");
554  while (DecodePgsCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF()) {
555  x = 0;
556  y++;
557  }
558  break;
559  default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__);
560  }
561  }
562 }
563 
564 void cSubtitleObject::DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
565 {
566  if (nonModifyingColorFlag && Index == 1)
567  return;
568  for (int pos = x; pos < x + Length; pos++)
569  Bitmap->SetIndex(pos, y, Index);
570 }
571 
572 bool cSubtitleObject::Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
573 {
574  int rl = 0;
575  int color = 0;
576  uchar code = bs->GetBits(2);
577  if (code) {
578  color = code;
579  rl = 1;
580  }
581  else if (bs->GetBit()) { // switch_1
582  rl = bs->GetBits(3) + 3;
583  color = bs->GetBits(2);
584  }
585  else if (bs->GetBit()) // switch_2
586  rl = 1; //color 0
587  else {
588  switch (bs->GetBits(2)) { // switch_3
589  case 0:
590  return false;
591  case 1:
592  rl = 2; //color 0
593  break;
594  case 2:
595  rl = bs->GetBits(4) + 12;
596  color = bs->GetBits(2);
597  break;
598  case 3:
599  rl = bs->GetBits(8) + 29;
600  color = bs->GetBits(2);
601  break;
602  default: ;
603  }
604  }
605  if (MapTable)
606  color = MapTable[color];
607  DrawLine(Bitmap, px + x, py + y, color, rl);
608  x += rl;
609  return true;
610 }
611 
612 bool cSubtitleObject::Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
613 {
614  int rl = 0;
615  int color = 0;
616  uchar code = bs->GetBits(4);
617  if (code) {
618  color = code;
619  rl = 1;
620  }
621  else if (bs->GetBit() == 0) { // switch_1
622  code = bs->GetBits(3);
623  if (code)
624  rl = code + 2; //color 0
625  else
626  return false;
627  }
628  else if (bs->GetBit() == 0) { // switch_2
629  rl = bs->GetBits(2) + 4;
630  color = bs->GetBits(4);
631  }
632  else {
633  switch (bs->GetBits(2)) { // switch_3
634  case 0: // color 0
635  rl = 1;
636  break;
637  case 1: // color 0
638  rl = 2;
639  break;
640  case 2:
641  rl = bs->GetBits(4) + 9;
642  color = bs->GetBits(4);
643  break;
644  case 3:
645  rl = bs->GetBits(8) + 25;
646  color = bs->GetBits(4);
647  break;
648  }
649  }
650  if (MapTable)
651  color = MapTable[color];
652  DrawLine(Bitmap, px + x, py + y, color, rl);
653  x += rl;
654  return true;
655 }
656 
657 bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
658 {
659  int rl = 0;
660  int color = 0;
661  uchar code = bs->GetBits(8);
662  if (code) {
663  color = code;
664  rl = 1;
665  }
666  else if (bs->GetBit()) {
667  rl = bs->GetBits(7);
668  color = bs->GetBits(8);
669  }
670  else {
671  code = bs->GetBits(7);
672  if (code)
673  rl = code; // color 0
674  else
675  return false;
676  }
677  DrawLine(Bitmap, px + x, py + y, color, rl);
678  x += rl;
679  return true;
680 }
681 
682 bool cSubtitleObject::DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
683 {
684  while (!bs->IsEOF()) {
685  int color = bs->GetBits(8);
686  int rl = 1;
687  if (!color) {
688  int flags = bs->GetBits(8);
689  rl = flags & 0x3f;
690  if (flags & 0x40)
691  rl = (rl << 8) + bs->GetBits(8);
692  color = flags & 0x80 ? bs->GetBits(8) : 0;
693  }
694  if (rl > 0) {
695  DrawLine(Bitmap, px + x, py + y, color, rl);
696  x += rl;
697  }
698  else if (!rl)
699  return true;
700  }
701  return false;
702 }
703 
704 void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
705 {
706  if (objectCodingMethod == 0) { // coding of pixels
707  DecodeSubBlock(Bitmap, px, py, topData, topLength, true);
708  if (botLength)
709  DecodeSubBlock(Bitmap, px, py, botData, botLength, false);
710  else
711  DecodeSubBlock(Bitmap, px, py, topData, topLength, false);
712  }
713  else if (objectCodingMethod == 1) { // coded as a string of characters
714  if (txtData) {
715  //TODO couldn't we draw the text directly into Bitmap?
717  cBitmap tmp(font->Width(txtData), font->Height(), Bitmap->Bpp());
718  double factor = (double)lineHeight / font->Height();
719  tmp.DrawText(0, 0, txtData, Bitmap->Color(IndexFg), Bitmap->Color(IndexBg), font);
720  cBitmap *scaled = tmp.Scaled(factor, factor, true);
721  Bitmap->DrawBitmap(px, py, *scaled);
722  delete scaled;
723  delete font;
724  }
725  }
726 }
727 
728 // --- cSubtitleObjects ------------------------------------------------------
729 
730 class cSubtitleObjects : public cList<cSubtitleObject> {
731 public:
732  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
733  };
734 
736 {
737  for (cSubtitleObject *so = First(); so; so = Next(so)) {
738  if (so->ObjectId() == ObjectId)
739  return so;
740  }
741  if (!New)
742  return NULL;
743  cSubtitleObject *Object = new cSubtitleObject(ObjectId);
744  Add(Object);
745  return Object;
746 }
747 
748 // --- cSubtitleObjectRef ----------------------------------------------------
749 
751 protected:
752  int objectId;
759 public:
760  cSubtitleObjectRef(void);
762  int ObjectId(void) { return objectId; }
763  int ObjectType(void) { return objectType; }
764  int ObjectProviderFlag(void) { return objectProviderFlag; }
765  int ObjectHorizontalPosition(void) { return objectHorizontalPosition; }
766  int ObjectVerticalPosition(void) { return objectVerticalPosition; }
767  int ForegroundPixelCode(void) { return foregroundPixelCode; }
768  int BackgroundPixelCode(void) { return backgroundPixelCode; }
769  };
770 
772 {
773  objectId = 0;
774  objectType = 0;
775  objectProviderFlag = 0;
776  objectHorizontalPosition = 0;
777  objectVerticalPosition = 0;
778  foregroundPixelCode = 0;
779  backgroundPixelCode = 0;
780 }
781 
783 {
784  objectId = bs.GetBits(16);
785  objectType = bs.GetBits(2);
786  objectProviderFlag = bs.GetBits(2);
787  objectHorizontalPosition = bs.GetBits(12);
788  bs.SkipBits(4); // reserved
789  objectVerticalPosition = bs.GetBits(12);
790  if (objectType == 0x01 || objectType == 0x02) {
791  foregroundPixelCode = bs.GetBits(8);
792  backgroundPixelCode = bs.GetBits(8);
793  }
794  else {
795  foregroundPixelCode = 0;
796  backgroundPixelCode = 0;
797  }
798  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);
799 }
800 
801 // --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
802 
804 private:
805  int windowId;
807  int cropX;
808  int cropY;
809  int cropW;
810  int cropH;
811 public:
813 };
814 
817 {
818  objectId = bs.GetBits(16);
819  windowId = bs.GetBits(8);
820  compositionFlag = bs.GetBits(8);
821  bs.SkipBits(32); // skip absolute position, object is aligned to region
822  if ((compositionFlag & 0x80) != 0) {
823  cropX = bs.GetBits(16);
824  cropY = bs.GetBits(16);
825  cropW = bs.GetBits(16);
826  cropH = bs.GetBits(16);
827  }
828  else
829  cropX = cropY = cropW = cropH = 0;
830  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);
831 }
832 
833 // --- cSubtitleRegion -------------------------------------------------------
834 
835 class cSubtitleRegion : public cListObject {
836 private:
837  int regionId;
844  int clutId;
849 public:
850  cSubtitleRegion(int RegionId);
851  void Parse(cBitStream &bs);
852  void ParsePgs(cBitStream &bs);
853  void SetDimensions(int Width, int Height);
854  int RegionId(void) { return regionId; }
855  int RegionVersionNumber(void) { return regionVersionNumber; }
856  bool RegionFillFlag(void) { return regionFillFlag; }
857  int RegionWidth(void) { return regionWidth; }
858  int RegionHeight(void) { return regionHeight; }
859  int RegionLevelOfCompatibility(void) { return regionLevelOfCompatibility; }
860  int RegionDepth(void) { return regionDepth; }
861  int ClutId(void) { return clutId; }
862  void Render(cBitmap *Bitmap, cSubtitleObjects *Objects);
863  };
864 
866 {
867  regionId = RegionId;
868  regionVersionNumber = -1;
869  regionFillFlag = false;
870  regionWidth = 0;
871  regionHeight = 0;
872  regionLevelOfCompatibility = 0;
873  regionDepth = 0;
874  clutId = -1;
875  region8bitPixelCode = 0;
876  region4bitPixelCode = 0;
877  region2bitPixelCode = 0;
878 }
879 
881 {
882  int Version = bs.GetBits(4);
883 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
884  if (regionVersionNumber == Version)
885  return; // no update
886 #endif
887  regionVersionNumber = Version;
888  regionFillFlag = bs.GetBit();
889  bs.SkipBits(3); // reserved
890  regionWidth = bs.GetBits(16);
891  regionHeight = bs.GetBits(16);
892  regionLevelOfCompatibility = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
893  regionDepth = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
894  bs.SkipBits(2); // reserved
895  clutId = bs.GetBits(8);
896  region8bitPixelCode = bs.GetBits(8);
897  region4bitPixelCode = bs.GetBits(4);
898  region2bitPixelCode = bs.GetBits(2);
899  bs.SkipBits(2); // reserved
900  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);
901  // no objectRefs.Clear() here!
902  while (!bs.IsEOF())
903  objectRefs.Add(new cSubtitleObjectRef(bs));
904 }
905 
907 {
908  regionDepth = 8;
909  bs.SkipBits(8); // skip palette update flag
910  clutId = bs.GetBits(8);
911  dbgregions("<b>region</b> id %d version %d clutId %d<br>\n", regionId, regionVersionNumber, clutId);
912  int objects = bs.GetBits(8);
913  while (objects--)
914  objectRefs.Add(new cSubtitleObjectRefPgs(bs));
915 }
916 
917 void cSubtitleRegion::SetDimensions(int Width, int Height)
918 {
919  regionWidth = Width;
920  regionHeight = Height;
921  dbgregions("<b>region</b> id %d width %d height %d<br>\n", regionId, regionWidth, regionHeight);
922 }
923 
925 {
926  if (regionFillFlag) {
927  switch (Bitmap->Bpp()) {
928  case 2: Bitmap->Fill(region2bitPixelCode); break;
929  case 4: Bitmap->Fill(region4bitPixelCode); break;
930  case 8: Bitmap->Fill(region8bitPixelCode); break;
931  default: dbgregions("unknown bpp %d (%s %d)<br>\n", Bitmap->Bpp(), __FUNCTION__, __LINE__);
932  }
933  }
934  for (cSubtitleObjectRef *sor = objectRefs.First(); sor; sor = objectRefs.Next(sor)) {
935  if (cSubtitleObject *so = Objects->GetObjectById(sor->ObjectId())) {
936  so->Render(Bitmap, sor->ObjectHorizontalPosition(), sor->ObjectVerticalPosition(), sor->ForegroundPixelCode(), sor->BackgroundPixelCode());
937  }
938  }
939 }
940 
941 // --- cSubtitleRegionRef ----------------------------------------------------
942 
944 private:
945  int regionId;
948 public:
949  cSubtitleRegionRef(int id, int x, int y);
951  int RegionId(void) { return regionId; }
952  int RegionHorizontalAddress(void) { return regionHorizontalAddress; }
953  int RegionVerticalAddress(void) { return regionVerticalAddress; }
954  };
955 
957 {
958  regionId = id;
959  regionHorizontalAddress = x;
960  regionVerticalAddress = y;
961  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
962 }
964 {
965  regionId = bs.GetBits(8);
966  bs.SkipBits(8); // reserved
967  regionHorizontalAddress = bs.GetBits(16);
968  regionVerticalAddress = bs.GetBits(16);
969  dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
970 }
971 
972 // --- cDvbSubtitlePage ------------------------------------------------------
973 
975 private:
976  int pageId;
980  int64_t pts;
981  bool pending;
986 public:
987  cDvbSubtitlePage(int PageId);
988  void Parse(int64_t Pts, cBitStream &bs);
989  void ParsePgs(int64_t Pts, cBitStream &bs);
990  int PageId(void) { return pageId; }
991  int PageTimeout(void) { return pageTimeout; }
992  int PageVersionNumber(void) { return pageVersionNumber; }
993  int PageState(void) { return pageState; }
994  int64_t Pts(void) const { return pts; }
995  bool Pending(void) { return pending; }
996  cSubtitleObjects *Objects(void) { return &objects; }
997  tArea *GetAreas(int &NumAreas, double FactorX, double FactorY);
998  cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
999  cSubtitleClut *GetClutById(int ClutId, bool New = false);
1000  cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
1001  cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
1002  void AddRegionRef(cSubtitleRegionRef *rf) { regionRefs.Add(rf); }
1003  void SetPending(bool Pending) { pending = Pending; }
1004  };
1005 
1007 {
1008  pageId = PageId;
1009  pageTimeout = 0;
1010  pageVersionNumber = -1;
1011  pageState = -1;
1012  pts = -1;
1013  pending = false;
1014 }
1015 
1016 void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
1017 {
1018  if (Pts >= 0)
1019  pts = Pts;
1020  pageTimeout = bs.GetBits(8);
1021  int Version = bs.GetBits(4);
1022 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1023  if (pageVersionNumber == Version)
1024  return; // no update
1025 #endif
1026  pageVersionNumber = Version;
1027  pageState = bs.GetBits(2);
1028  switch (pageState) {
1029  case 0: // normal case - page update
1030  break;
1031  case 1: // acquisition point - page refresh
1032  regions.Clear();
1033  objects.Clear();
1034  break;
1035  case 2: // mode change - new page
1036  regions.Clear();
1037  cluts.Clear();
1038  objects.Clear();
1039  break;
1040  case 3: // reserved
1041  break;
1042  default: dbgpages("unknown page state: %d<br>\n", pageState);
1043  }
1044  bs.SkipBits(2); // reserved
1045  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1046  regionRefs.Clear();
1047  while (!bs.IsEOF())
1048  regionRefs.Add(new cSubtitleRegionRef(bs));
1049  pending = true;
1050 }
1051 
1053 {
1054  if (Pts >= 0)
1055  pts = Pts;
1056  pageTimeout = 60000;
1057  int Version = bs.GetBits(16);
1058  if (pageVersionNumber == Version)
1059  return;
1060  pageVersionNumber = Version;
1061  pageState = bs.GetBits(2);
1062  switch (pageState) {
1063  case 0: // normal case - page update
1064  regions.Clear();
1065  break;
1066  case 1: // acquisition point - page refresh
1067  case 2: // epoch start - new page
1068  case 3: // epoch continue - new page
1069  regions.Clear();
1070  cluts.Clear();
1071  objects.Clear();
1072  break;
1073  default: dbgpages("unknown page state: %d<br>\n", pageState);
1074  }
1075  bs.SkipBits(6);
1076  dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1077  regionRefs.Clear();
1078  pending = true;
1079 }
1080 
1081 tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY)
1082 {
1083  if (regions.Count() > 0) {
1084  NumAreas = regionRefs.Count();
1085  tArea *Areas = new tArea[NumAreas];
1086  tArea *a = Areas;
1087  for (cSubtitleRegionRef *srr = regionRefs.First(); srr; srr = regionRefs.Next(srr)) {
1088  if (cSubtitleRegion *sr = GetRegionById(srr->RegionId())) {
1089  a->x1 = int(round(FactorX * srr->RegionHorizontalAddress()));
1090  a->y1 = int(round(FactorY * srr->RegionVerticalAddress()));
1091  a->x2 = int(round(FactorX * (srr->RegionHorizontalAddress() + sr->RegionWidth() - 1)));
1092  a->y2 = int(round(FactorY * (srr->RegionVerticalAddress() + sr->RegionHeight() - 1)));
1093  a->bpp = sr->RegionDepth();
1094  while ((a->Width() & 3) != 0)
1095  a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
1096  }
1097  else
1098  a->x1 = a->y1 = a->x2 = a->y2 = a->bpp = 0;
1099  a++;
1100  }
1101  return Areas;
1102  }
1103  NumAreas = 0;
1104  return NULL;
1105 }
1106 
1108 {
1109  for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
1110  if (sc->ClutId() == ClutId)
1111  return sc;
1112  }
1113  if (!New)
1114  return NULL;
1115  cSubtitleClut *Clut = new cSubtitleClut(ClutId);
1116  cluts.Add(Clut);
1117  return Clut;
1118 }
1119 
1121 {
1122  for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
1123  if (sr->RegionId() == RegionId)
1124  return sr;
1125  }
1126  if (!New)
1127  return NULL;
1128  cSubtitleRegion *Region = new cSubtitleRegion(RegionId);
1129  regions.Add(Region);
1130  return Region;
1131 }
1132 
1134 {
1135  return objects.GetObjectById(ObjectId, New);
1136 }
1137 
1138 // --- cDvbSubtitleAssembler -------------------------------------------------
1139 
1141 private:
1143  int length;
1144  int pos;
1145  int size;
1146  bool Realloc(int Size);
1147 public:
1148  cDvbSubtitleAssembler(void);
1149  virtual ~cDvbSubtitleAssembler();
1150  void Reset(void);
1151  unsigned char *Get(int &Length);
1152  void Put(const uchar *Data, int Length);
1153  };
1154 
1156 {
1157  data = NULL;
1158  size = 0;
1159  Reset();
1160 }
1161 
1163 {
1164  free(data);
1165 }
1166 
1168 {
1169  length = 0;
1170  pos = 0;
1171 }
1172 
1174 {
1175  if (Size > size) {
1176  Size = max(Size, 2048);
1177  if (uchar *NewBuffer = (uchar *)realloc(data, Size)) {
1178  size = Size;
1179  data = NewBuffer;
1180  }
1181  else {
1182  esyslog("ERROR: can't allocate memory for subtitle assembler");
1183  length = 0;
1184  size = 0;
1185  free(data);
1186  data = NULL;
1187  return false;
1188  }
1189  }
1190  return true;
1191 }
1192 
1193 unsigned char *cDvbSubtitleAssembler::Get(int &Length)
1194 {
1195  if (length > pos + 5) {
1196  Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
1197  if (length >= pos + Length) {
1198  unsigned char *result = data + pos;
1199  pos += Length;
1200  return result;
1201  }
1202  }
1203  return NULL;
1204 }
1205 
1206 void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
1207 {
1208  if (Length && Realloc(length + Length)) {
1209  memcpy(data + length, Data, Length);
1210  length += Length;
1211  }
1212 }
1213 
1214 // --- cDvbSubtitleBitmaps ---------------------------------------------------
1215 
1217 private:
1218  int state;
1219  int64_t pts;
1220  int timeout;
1223  double osdFactorX;
1224  double osdFactorY;
1226 public:
1227  cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
1229  int State(void) { return state; }
1230  int64_t Pts(void) { return pts; }
1231  int Timeout(void) { return timeout; }
1232  void AddBitmap(cBitmap *Bitmap);
1233  bool HasBitmaps(void) { return bitmaps.Size(); }
1234  void Draw(cOsd *Osd);
1235  void DbgDump(int WindowWidth, int WindowHeight);
1236  };
1237 
1238 cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
1239 {
1240  state = State;
1241  pts = Pts;
1242  timeout = Timeout;
1243  areas = Areas;
1244  numAreas = NumAreas;
1245  osdFactorX = OsdFactorX;
1246  osdFactorY = OsdFactorY;
1247 }
1248 
1250 {
1251  delete[] areas;
1252  for (int i = 0; i < bitmaps.Size(); i++)
1253  delete bitmaps[i];
1254 }
1255 
1257 {
1258  bitmaps.Append(Bitmap);
1259 }
1260 
1262 {
1263  bool Scale = !(DoubleEqual(osdFactorX, 1.0) && DoubleEqual(osdFactorY, 1.0));
1264  bool AntiAlias = true;
1265  if (Scale && osdFactorX > 1.0 || osdFactorY > 1.0) {
1266  // Upscaling requires 8bpp:
1267  int Bpp[MAXOSDAREAS];
1268  for (int i = 0; i < numAreas; i++) {
1269  Bpp[i] = areas[i].bpp;
1270  areas[i].bpp = 8;
1271  }
1272  if (Osd->CanHandleAreas(areas, numAreas) != oeOk) {
1273  for (int i = 0; i < numAreas; i++)
1274  areas[i].bpp = Bpp[i];
1275  AntiAlias = false;
1276  }
1277  }
1278  if (State() == 0 || Osd->SetAreas(areas, numAreas) == oeOk) {
1279  for (int i = 0; i < bitmaps.Size(); i++) {
1280  cBitmap *b = bitmaps[i];
1281  Osd->DrawScaledBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b, osdFactorX, osdFactorY, AntiAlias);
1282  }
1283  Osd->Flush();
1284  }
1285 }
1286 
1287 void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
1288 {
1289  if (!SD.Active())
1290  return;
1291  SD.SetFirstPts(Pts());
1292  double STC = double(cDevice::PrimaryDevice()->GetSTC() - SD.FirstPts()) / 90000;
1293  double Start = double(Pts() - SD.FirstPts()) / 90000;
1294  double Duration = Timeout();
1295  double End = Start + Duration;
1296  cBitmap Bitmap(WindowWidth, WindowHeight, 8);
1297 #define DBGBACKGROUND 0xA0A0A0
1298  Bitmap.DrawRectangle(0, 0, WindowWidth - 1, WindowHeight - 1, DBGBACKGROUND);
1299  for (int i = 0; i < bitmaps.Size(); i++) {
1300  cBitmap *b = bitmaps[i];
1301  Bitmap.DrawBitmap(b->X0(), b->Y0(), *b);
1302  }
1303  cString ImgName = SD.WriteJpeg(&Bitmap);
1304 #define BORDER //" border=1"
1305  SD.WriteHtml("<p>%s<br>", State() == 0 ? "page update" : State() == 1 ? "page refresh" : State() == 2 ? "new page" : "???");
1306  SD.WriteHtml("<table" BORDER "><tr><td>");
1307  SD.WriteHtml("%.2f", STC);
1308  SD.WriteHtml("</td><td>");
1309  SD.WriteHtml("<img src=\"%s\">", *ImgName);
1310  SD.WriteHtml("</td><td style=\"height:100%%\"><table" BORDER " style=\"height:100%%\">");
1311  SD.WriteHtml("<tr><td valign=top><b>%.2f</b></td></tr>", Start);
1312  SD.WriteHtml("<tr><td valign=middle>%.2f</td></tr>", Duration);
1313  SD.WriteHtml("<tr><td valign=bottom>%.2f</td></tr>", End);
1314  SD.WriteHtml("</table></td>");
1315  SD.WriteHtml("</tr></table>\n");
1316 }
1317 
1318 // --- cDvbSubtitleConverter -------------------------------------------------
1319 
1321 
1323 :cThread("subtitle converter")
1324 {
1326  osd = NULL;
1327  frozen = false;
1328  ddsVersionNumber = -1;
1329  displayWidth = windowWidth = 720;
1330  displayHeight = windowHeight = 576;
1335  SD.Reset();
1336  Start();
1337 }
1338 
1340 {
1341  Cancel(3);
1342  delete dvbSubtitleAssembler;
1343  delete osd;
1344  delete bitmaps;
1345  delete pages;
1346 }
1347 
1349 {
1350  setupLevel++;
1351 }
1352 
1354 {
1355  dbgconverter("converter reset -----------------------<br>\n");
1357  Lock();
1358  pages->Clear();
1359  bitmaps->Clear();
1360  DELETENULL(osd);
1361  frozen = false;
1362  ddsVersionNumber = -1;
1363  displayWidth = windowWidth = 720;
1364  displayHeight = windowHeight = 576;
1367  Unlock();
1368 }
1369 
1371 {
1372  if (Data && Length > 8) {
1373  int PayloadOffset = PesPayloadOffset(Data);
1374  int SubstreamHeaderLength = 4;
1375  bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1376 
1377  // Compatibility mode for old subtitles plugin:
1378  if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1379  PayloadOffset--;
1380  SubstreamHeaderLength = 1;
1381  ResetSubtitleAssembler = Data[8] >= 5;
1382  }
1383 
1384  if (Length > PayloadOffset + SubstreamHeaderLength) {
1385  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1386  if (pts >= 0)
1387  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1388  const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1389  int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1390  if (ResetSubtitleAssembler)
1392 
1393  if (length > 3) {
1394  if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1395  dvbSubtitleAssembler->Put(data + 2, length - 2);
1396  else
1397  dvbSubtitleAssembler->Put(data, length);
1398 
1399  int Count;
1400  while (true) {
1401  unsigned char *b = dvbSubtitleAssembler->Get(Count);
1402  if (b && b[0] == 0x0F) {
1403  if (ExtractSegment(b, Count, pts) == -1)
1404  break;
1405  }
1406  else
1407  break;
1408  }
1409  }
1410  }
1411  return Length;
1412  }
1413  return 0;
1414 }
1415 
1416 int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1417 {
1418  if (Data && Length > 8) {
1419  int PayloadOffset = PesPayloadOffset(Data);
1420  if (Length > PayloadOffset) {
1421  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1422  if (pts >= 0)
1423  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1424  const uchar *data = Data + PayloadOffset;
1425  int length = Length - PayloadOffset;
1426  if (length > 0) {
1427  if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1428  data += 2;
1429  length -= 2;
1430  }
1431  const uchar *b = data;
1432  while (length > 0) {
1433  if (b[0] == STUFFING_SEGMENT)
1434  break;
1435  int n;
1436  if (b[0] == 0x0F)
1437  n = ExtractSegment(b, length, pts);
1438  else
1439  n = ExtractPgsSegment(b, length, pts);
1440  if (n < 0)
1441  break;
1442  b += n;
1443  length -= n;
1444  }
1445  }
1446  }
1447  return Length;
1448  }
1449  return 0;
1450 }
1451 
1452 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
1453 
1455 {
1456  int LastSetupLevel = setupLevel;
1457  cTimeMs Timeout;
1458  while (Running()) {
1459  int WaitMs = 100;
1460  if (!frozen) {
1461  LOCK_THREAD;
1462  if (osd) {
1463  int NewSetupLevel = setupLevel;
1464  if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
1465  dbgoutput("closing osd<br>\n");
1466  DELETENULL(osd);
1467  }
1468  LastSetupLevel = NewSetupLevel;
1469  }
1470  for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
1471  // Calculate the Delta between the STC (the current timestamp of the video)
1472  // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1473  // A negative Delta means that the bitmap will be presented in the future:
1474  int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1475  int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits
1476  if (Delta > (int64_t(1) << 31))
1477  Delta -= (int64_t(1) << 32);
1478  else if (Delta < -((int64_t(1) << 31) - 1))
1479  Delta += (int64_t(1) << 32);
1480  Delta /= 90; // STC and PTS are in 1/90000s
1481  if (Delta >= 0) { // found a bitmap that shall be displayed...
1482  if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
1483  if (!sb->HasBitmaps()) {
1484  Timeout.Set();
1485  WaitMs = 0;
1486  }
1487  else if (AssertOsd()) {
1488  dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1489  sb->Draw(osd);
1490  Timeout.Set(sb->Timeout() * 1000);
1491  dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
1492  }
1493  }
1494  else
1495  WaitMs = 0; // bitmap already timed out, so try next one immediately
1496  dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1497  bitmaps->Del(sb);
1498  break;
1499  }
1500  }
1501  }
1502  cCondWait::SleepMs(WaitMs);
1503  }
1504 }
1505 
1507 {
1508  int OsdWidth, OsdHeight;
1509  double OsdAspect;
1510  int VideoWidth, VideoHeight;
1511  double VideoAspect;
1512  cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1513  cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
1514  if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1515  osdFactorX = osdFactorY = 1.0;
1516  osdDeltaX = osdDeltaY = 0;
1517  }
1518  else {
1519  osdFactorX = VideoAspect * OsdHeight / displayWidth;
1520  osdFactorY = double(OsdHeight) / displayHeight;
1521  osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1522  osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1523  }
1524 }
1525 
1527 {
1528  LOCK_THREAD;
1529  if (!osd) {
1530  SetOsdData();
1532  }
1533  return osd != NULL;
1534 }
1535 
1537 {
1538  for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1539  if (sp->PageId() == PageId)
1540  return sp;
1541  }
1542  if (!New)
1543  return NULL;
1544  cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1545  pages->Add(Page);
1546  return Page;
1547 }
1548 
1549 int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1550 {
1551  cBitStream bs(Data, Length * 8);
1552  if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1553  int segmentType = bs.GetBits(8);
1554  if (segmentType == STUFFING_SEGMENT)
1555  return -1;
1556  LOCK_THREAD;
1557  cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1558  int segmentLength = bs.GetBits(16);
1559  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1560  return -1;
1561  switch (segmentType) {
1562  case PAGE_COMPOSITION_SEGMENT: {
1563  if (page->Pending()) {
1564  dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
1565  FinishPage(page);
1566  }
1567  dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
1568  page->Parse(Pts, bs);
1569  SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
1570  break;
1571  }
1573  dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
1574  cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1575  region->Parse(bs);
1576  break;
1577  }
1578  case CLUT_DEFINITION_SEGMENT: {
1579  dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
1580  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1581  clut->Parse(bs);
1582  break;
1583  }
1584  case OBJECT_DATA_SEGMENT: {
1585  dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
1586  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1587  object->Parse(bs);
1588  break;
1589  }
1591  dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
1592  int version = bs.GetBits(4);
1593 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1594  if (version == ddsVersionNumber)
1595  break; // no update
1596 #endif
1597  bool displayWindowFlag = bs.GetBit();
1600  bs.SkipBits(3); // reserved
1601  displayWidth = windowWidth = bs.GetBits(16) + 1;
1602  displayHeight = windowHeight = bs.GetBits(16) + 1;
1603  if (displayWindowFlag) {
1604  windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1605  windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1606  windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1607  windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1608  }
1609  SetOsdData();
1610  ddsVersionNumber = version;
1611  dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
1612  break;
1613  }
1615  dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
1616  bs.SkipBits(4); // dss_version_number
1617  bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1618  bs.SkipBits(3); // reserved
1619  bs.SkipBits(8); // page_default_disparity_shift
1620  if (disparity_shift_update_sequence_page_flag) {
1621  bs.SkipBits(8); // disparity_shift_update_sequence_length
1622  bs.SkipBits(24); // interval_duration[23..0]
1623  int division_period_count = bs.GetBits(8);
1624  for (int i = 0; i < division_period_count; ++i) {
1625  bs.SkipBits(8); // interval_count
1626  bs.SkipBits(8); // disparity_shift_update_integer_part
1627  }
1628  }
1629  while (!bs.IsEOF()) {
1630  bs.SkipBits(8); // region_id
1631  bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1632  bs.SkipBits(5); // reserved
1633  int number_of_subregions_minus_1 = bs.GetBits(2);
1634  for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1635  if (number_of_subregions_minus_1 > 0) {
1636  bs.SkipBits(16); // subregion_horizontal_position
1637  bs.SkipBits(16); // subregion_width
1638  }
1639  bs.SkipBits(8); // subregion_disparity_shift_integer_part
1640  bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1641  bs.SkipBits(4); // reserved
1642  if (disparity_shift_update_sequence_region_flag) {
1643  bs.SkipBits(8); // disparity_shift_update_sequence_length
1644  bs.SkipBits(24); // interval_duration[23..0]
1645  int division_period_count = bs.GetBits(8);
1646  for (int i = 0; i < division_period_count; ++i) {
1647  bs.SkipBits(8); // interval_count
1648  bs.SkipBits(8); // disparity_shift_update_integer_part
1649  }
1650  }
1651  }
1652  }
1653  break;
1654  }
1656  dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
1657  FinishPage(page);
1658  page->SetPending(false);
1659  break;
1660  }
1661  default:
1662  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1663  }
1664  return bs.Length() / 8;
1665  }
1666  return -1;
1667 }
1668 
1669 int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
1670 {
1671  cBitStream bs(Data, Length * 8);
1672  if (Length >= 3) {
1673  int segmentType = bs.GetBits(8);
1674  int segmentLength = bs.GetBits(16);
1675  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1676  return -1;
1677  LOCK_THREAD;
1678  cDvbSubtitlePage *page = GetPageById(0, true);
1679  switch (segmentType) {
1680  case PGS_PRESENTATION_SEGMENT: {
1681  if (page->Pending()) {
1682  dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
1683  FinishPage(page);
1684  }
1685  dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
1686  displayWidth = windowWidth = bs.GetBits(16);
1687  displayHeight = windowHeight = bs.GetBits(16);
1688  bs.SkipBits(8);
1689  page->ParsePgs(Pts, bs);
1690  SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
1691  cSubtitleRegion *region = page->GetRegionById(0, true);
1692  region->ParsePgs(bs);
1693  break;
1694  }
1695  case PGS_WINDOW_SEGMENT: {
1696  bs.SkipBits(16);
1697  int regionHorizontalAddress = bs.GetBits(16);
1698  int regionVerticalAddress = bs.GetBits(16);
1699  int regionWidth = bs.GetBits(16);
1700  int regionHeight = bs.GetBits(16);
1701  cSubtitleRegion *region = page->GetRegionById(0, true);
1702  region->SetDimensions(regionWidth, regionHeight);
1703  page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
1704  dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
1705  break;
1706  }
1707  case PGS_PALETTE_SEGMENT: {
1708  dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
1709  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1710  clut->ParsePgs(bs);
1711  break;
1712  }
1713  case PGS_OBJECT_SEGMENT: {
1714  dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
1715  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1716  object->ParsePgs(bs);
1717  break;
1718  }
1719  case PGS_DISPLAY_SEGMENT: {
1720  dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
1721  FinishPage(page);
1722  page->SetPending(false);
1723  break;
1724  }
1725  default:
1726  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1727  return -1;
1728  }
1729  return bs.Length() / 8;
1730  }
1731  return -1;
1732 }
1733 
1735 {
1736  if (!AssertOsd())
1737  return;
1738  int NumAreas;
1739  tArea *Areas = Page->GetAreas(NumAreas, osdFactorX, osdFactorY);
1740  int Bpp = 8;
1741  bool Reduced = false;
1742  while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) {
1743  dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(Areas, NumAreas));
1744  int HalfBpp = Bpp / 2;
1745  if (HalfBpp >= 2) {
1746  for (int i = 0; i < NumAreas; i++) {
1747  if (Areas[i].bpp >= Bpp) {
1748  Areas[i].bpp = HalfBpp;
1749  Reduced = true;
1750  }
1751  }
1752  Bpp = HalfBpp;
1753  }
1754  else
1755  return; // unable to draw bitmaps
1756  }
1757  cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY);
1758  bitmaps->Add(Bitmaps);
1759  for (int i = 0; i < NumAreas; i++) {
1760  if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1761  if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1762  if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1763  cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1764  bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1765  sr->Render(bm, Page->Objects());
1766  if (Reduced) {
1767  if (sr->RegionDepth() != Areas[i].bpp) {
1768  if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1769  //TODO this is untested - didn't have any such subtitle stream
1770  cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1771  dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1772  bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1773  }
1774  else {
1775  dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1776  bm->ShrinkBpp(Areas[i].bpp);
1777  }
1778  }
1779  }
1780  bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1781  Bitmaps->AddBitmap(bm);
1782  }
1783  }
1784  }
1785  }
1786  if (DebugPages)
1787  Bitmaps->DbgDump(windowWidth, windowHeight);
1788 }
void SetFirstPts(int64_t FirstPts)
Definition: dvbsubtitle.c:78
int RegionDepth(void)
Definition: dvbsubtitle.c:860
cVector< cBitmap * > bitmaps
Definition: dvbsubtitle.c:1225
void ReduceBpp(const cPalette &Palette)
Reduces the color depth of the bitmap to that of the given Palette.
Definition: osd.c:765
int ObjectCodingMethod(void)
Definition: dvbsubtitle.c:368
int y2
Definition: osd.h:299
unsigned char uchar
Definition: tools.h:30
int RegionLevelOfCompatibility(void)
Definition: dvbsubtitle.c:859
#define DBGMAXBITMAPS
Definition: dvbsubtitle.c:59
int64_t FirstPts(void)
Definition: dvbsubtitle.c:77
static bool DebugNormal
Definition: dvbsubtitle.c:37
int ObjectId(void)
Definition: dvbsubtitle.c:762
int ForegroundPixelCode(void)
Definition: dvbsubtitle.c:767
cList< cDvbSubtitlePage > * pages
Definition: dvbsubtitle.h:40
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:277
bool NonModifyingColorFlag(void)
Definition: dvbsubtitle.c:369
void Parse(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1016
void Fill(tIndex Index)
Fills the bitmap data with the given Index.
Definition: osd.c:515
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
static bool DebugConverter
Definition: dvbsubtitle.c:43
static bool DebugCluts
Definition: dvbsubtitle.c:46
int Index(void) const
Definition: tools.c:1989
#define dbgdisplay(a...)
Definition: dvbsubtitle.c:49
int ObjectProviderFlag(void)
Definition: dvbsubtitle.c:764
cSubtitleRegionRef(int id, int x, int y)
Definition: dvbsubtitle.c:956
void Set(int Ms=0)
Definition: tools.c:738
cPalette palette2
Definition: dvbsubtitle.c:147
void SetFactor(double Factor)
Definition: dvbsubtitle.c:79
int PesPayloadOffset(const uchar *p)
Definition: remux.h:173
void Clean(void)
Marks the dirty area as clean.
Definition: osd.c:354
#define dbgconverter(a...)
Definition: dvbsubtitle.c:53
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:230
uint32_t GetBits(int n)
Definition: tools.c:1355
int PageId(void)
Definition: dvbsubtitle.c:990
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
int Convert(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1416
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition: osd.c:1823
int PageVersionNumber(void)
Definition: dvbsubtitle.c:992
void SetOffset(int X0, int Y0)
Sets the offset of this bitmap to the given values.
Definition: osd.h:195
#define CLUT_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:21
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:1932
#define DISPARITY_SIGNALING_SEGMENT
Definition: dvbsubtitle.c:24
int x1
Definition: osd.h:299
int RegionWidth(void)
Definition: dvbsubtitle.c:857
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:435
cSubtitleObjects objects
Definition: dvbsubtitle.c:982
const uint8_t * GetData(void) const
Definition: tools.h:330
const char * getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte)
Definition: si.c:354
static const char * SystemCharacterTable(void)
Definition: tools.h:164
#define dbgcluts(a...)
Definition: dvbsubtitle.c:56
#define dbgobjects(a...)
Definition: dvbsubtitle.c:52
int64_t PesGetPts(const uchar *p)
Definition: remux.h:188
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
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:1454
void SkipBit(void)
Definition: tools.h:325
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:395
#define REGION_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:20
void Draw(cOsd *Osd)
Definition: dvbsubtitle.c:1261
void DbgDump(int WindowWidth, int WindowHeight)
Definition: dvbsubtitle.c:1287
#define esyslog(a...)
Definition: tools.h:34
static void SetupChanged(void)
Definition: dvbsubtitle.c:1348
#define OBJECT_DATA_SEGMENT
Definition: dvbsubtitle.c:22
int bpp
Definition: osd.h:300
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition: dvbsubtitle.h:26
T * Get(int Index) const
Definition: tools.h:491
tColor yuv2rgb(int Y, int Cb, int Cr)
Definition: dvbsubtitle.c:302
bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:657
#define LOG_ERROR_STR(s)
Definition: tools.h:39
T max(T a, T b)
Definition: tools.h:55
Definition: tools.h:489
void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
Definition: dvbsubtitle.c:564
bool PesHasPts(const uchar *p)
Definition: remux.h:178
int ClutId(void)
Definition: dvbsubtitle.c:156
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:442
void Reset(void)
Definition: dvbsubtitle.c:84
static bool DebugPixel
Definition: dvbsubtitle.c:45
static bool DebugSegments
Definition: dvbsubtitle.c:44
bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:612
int Count(void) const
Definition: tools.h:485
cSubtitleClut * GetClutById(int ClutId, bool New=false)
Definition: dvbsubtitle.c:1107
void Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
Definition: dvbsubtitle.c:924
void WordAlign(void)
Definition: tools.c:1370
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:1133
tArea * GetAreas(int &NumAreas, double FactorX, double FactorY)
Definition: dvbsubtitle.c:1081
cSubtitleClut(int ClutId)
Definition: dvbsubtitle.c:161
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:955
void SetDimensions(int Width, int Height)
Definition: dvbsubtitle.c:917
unsigned char * Get(int &Length)
Definition: dvbsubtitle.c:1193
bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:572
int ClutVersionNumber(void)
Definition: dvbsubtitle.c:157
cList< cSubtitleRegion > regions
Definition: dvbsubtitle.c:984
void DecodeCharacterString(const uchar *Data, int NumberOfCodes)
Definition: dvbsubtitle.c:466
int Bpp(void) const
Definition: osd.h:111
#define DBGBITMAPWIDTH
Definition: dvbsubtitle.c:60
static bool DebugPages
Definition: dvbsubtitle.c:40
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition: osd.c:1982
int Length(void) const
Definition: tools.h:328
#define MALLOC(type, size)
Definition: tools.h:46
virtual void Clear(void)
Definition: tools.c:2087
Definition: osd.h:169
int Width(void) const
Definition: osd.h:188
cSubtitleRegion * GetRegionById(int RegionId, bool New=false)
Definition: dvbsubtitle.c:1120
bool Pending(void)
Definition: dvbsubtitle.c:995
int ObjectId(void)
Definition: dvbsubtitle.c:366
cList< cSubtitleClut > cluts
Definition: dvbsubtitle.c:983
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:443
bool RegionFillFlag(void)
Definition: dvbsubtitle.c:856
static bool DebugObjects
Definition: dvbsubtitle.c:42
void Unlock(void)
Definition: thread.h:93
void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
Definition: dvbsubtitle.c:704
int Width(void) const
Definition: osd.h:301
bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:682
void Replace(const cPalette &Palette)
Replaces the colors of this palette with the colors from the given palette.
Definition: osd.c:208
bool IsEOF(void) const
Definition: tools.h:326
T * Next(const T *object) const
Definition: tools.h:495
int ObjectType(void)
Definition: dvbsubtitle.c:763
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
#define PAGE_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:19
T constrain(T v, T l, T h)
Definition: tools.h:60
void ParsePgs(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1052
static bool DebugRegions
Definition: dvbsubtitle.c:41
int SubtitleFgTransparency
Definition: config.h:291
cString WriteJpeg(const cBitmap *Bitmap, int MaxX=0, int MaxY=0)
Definition: dvbsubtitle.c:92
#define dbgpages(a...)
Definition: dvbsubtitle.c:50
virtual ~cDvbSubtitleConverter()
Definition: dvbsubtitle.c:1339
int Height(void) const
Definition: osd.h:189
#define PGS_OBJECT_SEGMENT
Definition: dvbsubtitle.c:29
cListObject * Next(void) const
Definition: tools.h:468
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition: osd.c:1801
char FontOsd[MAXFONTNAME]
Definition: config.h:324
#define dbgpixel(a...)
Definition: dvbsubtitle.c:55
bool TimedOut(void) const
Definition: tools.c:743
cPalette palette8
Definition: dvbsubtitle.c:149
bool HasBitmaps(void)
Definition: dvbsubtitle.c:1233
void AddRegionRef(cSubtitleRegionRef *rf)
Definition: dvbsubtitle.c:1002
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:57
#define MAXOSDAREAS
Definition: osd.h:707
#define STUFFING_SEGMENT
Definition: dvbsubtitle.c:26
int ObjectVerticalPosition(void)
Definition: dvbsubtitle.c:766
int ObjectVersionNumber(void)
Definition: dvbsubtitle.c:367
int x2
Definition: osd.h:299
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 bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:273
#define dbgoutput(a...)
Definition: dvbsubtitle.c:57
The cOsd class is the interface to the "On Screen Display".
Definition: osd.h:720
cSubtitleObject(int ObjectId)
Definition: dvbsubtitle.c:373
int SubtitleOffset
Definition: config.h:290
cSetup Setup
Definition: config.c:373
int PageTimeout(void)
Definition: dvbsubtitle.c:991
static bool DebugOutput
Definition: dvbsubtitle.c:47
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:423
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:99
Definition: thread.h:63
void AddBitmap(cBitmap *Bitmap)
Definition: dvbsubtitle.c:1256
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
Definition: dvbsubtitle.c:1536
int Size(void) const
Definition: tools.h:551
void SkipBits(int n)
Definition: tools.h:324
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
void SetColor(int Index, tColor Color)
Sets the palette entry at Index to Color.
Definition: osd.c:172
cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
Definition: dvbsubtitle.c:1238
int ClutId(void)
Definition: dvbsubtitle.c:861
int Y0(void) const
Definition: osd.h:187
#define PGS_WINDOW_SEGMENT
Definition: dvbsubtitle.c:31
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
int64_t Pts(void)
Definition: dvbsubtitle.c:1230
#define PGS_PALETTE_SEGMENT
Definition: dvbsubtitle.c:28
tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
Definition: osd.h:58
cList< cSubtitleRegionRef > regionRefs
Definition: dvbsubtitle.c:985
int ObjectHorizontalPosition(void)
Definition: dvbsubtitle.c:765
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:1251
int64_t firstPts
Definition: dvbsubtitle.c:70
#define OSD_LEVEL_SUBTITLES
Definition: osd.h:22
int RegionHeight(void)
Definition: dvbsubtitle.c:858
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:735
int BackgroundPixelCode(void)
Definition: dvbsubtitle.c:768
T * First(void) const
Definition: tools.h:492
void WriteHtml(const char *Format,...)
Definition: dvbsubtitle.c:124
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
void SetColor(int Bpp, int Index, tColor Color)
Definition: dvbsubtitle.c:318
#define BORDER
int y1
Definition: osd.h:299
Definition: osd.h:88
bool Realloc(int Size)
Definition: dvbsubtitle.c:1173
Definition: osd.h:298
void FinishPage(cDvbSubtitlePage *Page)
Definition: dvbsubtitle.c:1734
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:137
virtual int Width(uint c) const =0
Returns the width of the given character in pixel.
cDvbSubtitlePage(int PageId)
Definition: dvbsubtitle.c:1006
#define LimitTo32Bit(n)
Definition: dvbsubtitle.c:1452
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1669
cList< cSubtitleObjectRef > objectRefs
Definition: dvbsubtitle.c:848
int regionLevelOfCompatibility
Definition: dvbsubtitle.c:842
#define DISPLAY_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:23
void DELETENULL(T *&p)
Definition: tools.h:48
void SetPending(bool Pending)
Definition: dvbsubtitle.c:1003
cSubtitleDebug(void)
Definition: dvbsubtitle.c:74
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles...
Definition: device.c:1123
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1549
int GetBit(void)
Definition: tools.c:1346
#define PGS_DISPLAY_SEGMENT
Definition: dvbsubtitle.c:32
Definition: thread.h:77
virtual ~cDvbSubtitleAssembler()
Definition: dvbsubtitle.c:1162
int RegionVerticalAddress(void)
Definition: dvbsubtitle.c:953
int FontOsdSize
Definition: config.h:330
#define PGS_PRESENTATION_SEGMENT
Definition: dvbsubtitle.c:30
Definition: osd.h:44
cList< cDvbSubtitleBitmaps > * bitmaps
Definition: dvbsubtitle.h:41
tColor Color(int Index) const
Returns the color at the given Index.
Definition: osd.h:119
tColor GetColor(int x, int y) const
Returns the color at the given coordinates.
Definition: osd.h:277
Definition: tools.h:333
#define END_OF_DISPLAY_SET_SEGMENT
Definition: dvbsubtitle.c:25
int64_t Pts(void) const
Definition: dvbsubtitle.c:994
bool SetLength(int Length)
Definition: tools.c:1377
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:880
virtual int Height(void) const =0
Returns the height of this font in pixel (all characters have the same height).
static bool DebugDisplay
Definition: dvbsubtitle.c:39
bool DoubleEqual(double a, double b)
Definition: tools.h:87
void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
Definition: dvbsubtitle.c:495
#define dbgsegments(a...)
Definition: dvbsubtitle.c:54
const cPalette * GetPalette(int Bpp)
Definition: dvbsubtitle.c:328
void Put(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1206
#define LOCK_THREAD
Definition: thread.h:165
int X0(void) const
Definition: osd.h:186
static cSubtitleDebug SD
Definition: dvbsubtitle.c:139
cPalette palette4
Definition: dvbsubtitle.c:148
uint8_t tIndex
Definition: font.h:31
int Index(void) const
Definition: tools.h:329
bool Active(void)
Definition: dvbsubtitle.c:76
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
int clutVersionNumber
Definition: dvbsubtitle.c:146
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:906
cSubtitleObjectRefPgs(cBitStream &bs)
Definition: dvbsubtitle.c:815
static bool DebugVerbose
Definition: dvbsubtitle.c:38
cSubtitleObjects * Objects(void)
Definition: dvbsubtitle.c:996
void SetIndex(int x, int y, tIndex Index)
Sets the index at the given coordinates to Index.
Definition: osd.c:500
#define DBGBACKGROUND
int RegionId(void)
Definition: dvbsubtitle.c:951
int RegionId(void)
Definition: dvbsubtitle.c:854
int ConvertFragments(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1370
static int setupLevel
Definition: dvbsubtitle.h:25
int SubtitleBgTransparency
Definition: config.h:291
Definition: font.h:37
void ByteAlign(void)
Definition: tools.c:1363
#define dbgregions(a...)
Definition: dvbsubtitle.c:51
Definition: tools.h:168
int PageState(void)
Definition: dvbsubtitle.c:993
int RegionVersionNumber(void)
Definition: dvbsubtitle.c:855
int RegionHorizontalAddress(void)
Definition: dvbsubtitle.c:952
uint32_t tColor
Definition: font.h:29
void Lock(void)
Definition: thread.h:92
cSubtitleRegionRef * GetRegionRefByIndex(int RegionRefIndex)
Definition: dvbsubtitle.c:1001
bool nonModifyingColorFlag
Definition: dvbsubtitle.c:346
cSubtitleRegion(int RegionId)
Definition: dvbsubtitle.c:865
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:2006