Bug Summary

File:dvbsubtitle.c
Location:line 377, column 9
Description:Null pointer passed as an argument to a 'nonnull' parameter

Annotated Source Code

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.4 2013/09/07 10:39:46 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_SEGMENT0x10 0x10
20#define REGION_COMPOSITION_SEGMENT0x11 0x11
21#define CLUT_DEFINITION_SEGMENT0x12 0x12
22#define OBJECT_DATA_SEGMENT0x13 0x13
23#define DISPLAY_DEFINITION_SEGMENT0x14 0x14
24#define DISPARITY_SIGNALING_SEGMENT0x15 0x15 // DVB BlueBook A156
25#define END_OF_DISPLAY_SET_SEGMENT0x80 0x80
26#define STUFFING_SEGMENT0xFF 0xFF
27
28// Set these to 'true' for debug output, which is written into the file dbg-log.htm
29// in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
30// used to display the subtitles.
31static bool DebugNormal = false; // shows pages, regions and objects
32static bool DebugVerbose = false; // shows everything
33static bool DebugDisplay = DebugVerbose || DebugNormal;
34static bool DebugPages = DebugVerbose || DebugNormal;
35static bool DebugRegions = DebugVerbose || DebugNormal;
36static bool DebugObjects = DebugVerbose || DebugNormal;
37static bool DebugBitmaps = DebugVerbose || DebugNormal;
38static bool DebugConverter = DebugVerbose;
39static bool DebugSegments = DebugVerbose;
40static bool DebugPixel = DebugVerbose;
41static bool DebugCluts = DebugVerbose;
42static bool DebugOutput = DebugVerbose;
43
44#define dbgdisplay(a...)if (DebugDisplay) SD.WriteHtml(a...) if (DebugDisplay) SD.WriteHtml(a)
45#define dbgpages(a...)if (DebugPages) SD.WriteHtml(a...) if (DebugPages) SD.WriteHtml(a)
46#define dbgregions(a...)if (DebugRegions) SD.WriteHtml(a...) if (DebugRegions) SD.WriteHtml(a)
47#define dbgobjects(a...)if (DebugObjects) SD.WriteHtml(a...) if (DebugObjects) SD.WriteHtml(a)
48#define dbgbitmaps(a...)if (DebugBitmaps) SD.WriteHtml(a...) if (DebugBitmaps) SD.WriteHtml(a)
49#define dbgconverter(a...)if (DebugConverter) SD.WriteHtml(a...) if (DebugConverter) SD.WriteHtml(a)
50#define dbgsegments(a...)if (DebugSegments) SD.WriteHtml(a...) if (DebugSegments) SD.WriteHtml(a)
51#define dbgpixel(a...)if (DebugPixel) SD.WriteHtml(a...) if (DebugPixel) SD.WriteHtml(a)
52#define dbgcluts(a...)if (DebugCluts) SD.WriteHtml(a...) if (DebugCluts) SD.WriteHtml(a)
53#define dbgoutput(a...)if (DebugOutput) SD.WriteHtml(a...) if (DebugOutput) SD.WriteHtml(a)
54
55#define DBGMAXBITMAPS100 100 // debug output will be stopped after this many bitmaps
56#define DBGBITMAPWIDTH400 400
57
58// --- cSubtitleDebug --------------------------------------------------------
59
60class cSubtitleDebug {
61private:
62 cMutex mutex;
63 int imgCnt;
64 int64_t firstPts;
65 bool newFile;
66 double factor;
67public:
68 cSubtitleDebug(void) { Reset(); }
69 void Reset(void);
70 bool Active(void) { return imgCnt < DBGMAXBITMAPS100; }
71 int64_t FirstPts(void) { return firstPts; }
72 void SetFirstPts(int64_t FirstPts) { if (firstPts < 0) firstPts = FirstPts; }
73 void SetFactor(double Factor) { factor = Factor; }
74 cString WriteJpeg(const cBitmap *Bitmap, int MaxX = 0, int MaxY = 0);
75 void WriteHtml(const char *Format, ...);
76 };
77
78void cSubtitleDebug::Reset(void)
79{
80 imgCnt = 0;
81 firstPts = -1;
82 newFile = true;
83 factor = 1.0;
84}
85
86cString cSubtitleDebug::WriteJpeg(const cBitmap *Bitmap, int MaxX, int MaxY)
87{
88 if (!Active())
89 return NULL__null;
90 cMutexLock MutexLock(&mutex);
91 cBitmap *Scaled = Bitmap->Scaled(factor, factor, true);
92 int w = MaxX ? int(round(MaxX * factor)) : Scaled->Width();
93 int h = MaxY ? int(round(MaxY * factor)) : Scaled->Height();
94 uchar mem[w * h * 3];
95 for (int x = 0; x < w; x++) {
96 for (int y = 0; y < h; y++) {
97 tColor c = Scaled->GetColor(x, y);
98 int o = (y * w + x) * 3;
99 mem[o++] = (c & 0x00FF0000) >> 16;
100 mem[o++] = (c & 0x0000FF00) >> 8;
101 mem[o] = (c & 0x000000FF);
102 }
103 }
104 delete Scaled;
105 int Size = 0;
106 uchar *Jpeg = RgbToJpeg(mem, w, h, Size);
107 cString ImgName = cString::sprintf("dbg-%03d.jpg", imgCnt++);
108 int f = open(ImgName, O_WRONLY01 | O_CREAT0100, DEFFILEMODE(0400|0200|(0400 >> 3)|(0200 >> 3)|((0400 >>
3) >> 3)|((0200 >> 3) >> 3))
);
109 if (f >= 0) {
110 if (write(f, Jpeg, Size) < 0)
111 LOG_ERROR_STR(*ImgName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %s: %m"
, "dvbsubtitle.c", 111, *ImgName) : void() )
;
112 close(f);
113 }
114 free(Jpeg);
115 return ImgName;
116}
117
118void cSubtitleDebug::WriteHtml(const char *Format, ...)
119{
120 if (!Active())
121 return;
122 cMutexLock MutexLock(&mutex);
123 if (FILE *f = fopen("dbg-log.htm", newFile ? "w" : "a")) {
124 va_list ap;
125 va_start(ap, Format)__builtin_va_start(ap, Format);
126 vfprintf(f, Format, ap);
127 va_end(ap)__builtin_va_end(ap);
128 fclose(f);
129 newFile = false;
130 }
131}
132
133static cSubtitleDebug SD;
134
135// --- cSubtitleClut ---------------------------------------------------------
136
137class cSubtitleClut : public cListObject {
138private:
139 int clutId;
140 int clutVersionNumber;
141 cPalette palette2;
142 cPalette palette4;
143 cPalette palette8;
144 tColor yuv2rgb(int Y, int Cb, int Cr);
145 void SetColor(int Bpp, int Index, tColor Color);
146public:
147 cSubtitleClut(int ClutId);
148 void Parse(cBitStream &bs);
149 int ClutId(void) { return clutId; }
150 int ClutVersionNumber(void) { return clutVersionNumber; }
151 const cPalette *GetPalette(int Bpp);
152 };
153
154cSubtitleClut::cSubtitleClut(int ClutId)
155:palette2(2)
156,palette4(4)
157,palette8(8)
158{
159 int a = 0, r = 0, g = 0, b = 0;
160 clutId = ClutId;
161 clutVersionNumber = -1;
162 // ETSI EN 300 743 10.3: 4-entry CLUT default contents
163 palette2.SetColor(0, ArgbToColor( 0, 0, 0, 0));
164 palette2.SetColor(1, ArgbToColor(255, 255, 255, 255));
165 palette2.SetColor(2, ArgbToColor(255, 0, 0, 0));
166 palette2.SetColor(3, ArgbToColor(255, 127, 127, 127));
167 // ETSI EN 300 743 10.2: 16-entry CLUT default contents
168 palette4.SetColor(0, ArgbToColor(0, 0, 0, 0));
169 for (int i = 1; i < 16; ++i) {
170 if (i < 8) {
171 r = (i & 1) ? 255 : 0;
172 g = (i & 2) ? 255 : 0;
173 b = (i & 4) ? 255 : 0;
174 }
175 else {
176 r = (i & 1) ? 127 : 0;
177 g = (i & 2) ? 127 : 0;
178 b = (i & 4) ? 127 : 0;
179 }
180 palette4.SetColor(i, ArgbToColor(255, r, g, b));
181 }
182 // ETSI EN 300 743 10.1: 256-entry CLUT default contents
183 palette8.SetColor(0, ArgbToColor(0, 0, 0, 0));
184 for (int i = 1; i < 256; ++i) {
185 if (i < 8) {
186 r = (i & 1) ? 255 : 0;
187 g = (i & 2) ? 255 : 0;
188 b = (i & 4) ? 255 : 0;
189 a = 63;
190 }
191 else {
192 switch (i & 0x88) {
193 case 0x00:
194 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
195 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
196 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
197 a = 255;
198 break;
199 case 0x08:
200 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
201 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
202 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
203 a = 127;
204 break;
205 case 0x80:
206 r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
207 g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
208 b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
209 a = 255;
210 break;
211 case 0x88:
212 r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
213 g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
214 b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
215 a = 255;
216 break;
217 }
218 }
219 palette8.SetColor(i, ArgbToColor(a, r, g, b));
220 }
221}
222
223void cSubtitleClut::Parse(cBitStream &bs)
224{
225 int Version = bs.GetBits(4);
226 if (clutVersionNumber == Version)
227 return; // no update
228 clutVersionNumber = Version;
229 bs.SkipBits(4); // reserved
230 dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber)if (DebugCluts) SD.WriteHtml("<b>clut</b> id %d version %d<br>\n"
, clutId, clutVersionNumber)
;
231 while (!bs.IsEOF()) {
232 uchar clutEntryId = bs.GetBits(8);
233 bool entryClut2Flag = bs.GetBit();
234 bool entryClut4Flag = bs.GetBit();
235 bool entryClut8Flag = bs.GetBit();
236 bs.SkipBits(4); // reserved
237 uchar yval;
238 uchar crval;
239 uchar cbval;
240 uchar tval;
241 if (bs.GetBit()) { // full_range_flag
242 yval = bs.GetBits(8);
243 crval = bs.GetBits(8);
244 cbval = bs.GetBits(8);
245 tval = bs.GetBits(8);
246 }
247 else {
248 yval = bs.GetBits(6) << 2;
249 crval = bs.GetBits(4) << 4;
250 cbval = bs.GetBits(4) << 4;
251 tval = bs.GetBits(2) << 6;
252 }
253 tColor value = 0;
254 if (yval) {
255 value = yuv2rgb(yval, cbval, crval);
256 value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
257 }
258 dbgcluts("%2d %d %d %d %08X<br>\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value)if (DebugCluts) SD.WriteHtml("%2d %d %d %d %08X<br>\n",
clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0,
entryClut8Flag ? 8 : 0, value)
;
259 if (entryClut2Flag)
260 SetColor(2, clutEntryId, value);
261 if (entryClut4Flag)
262 SetColor(4, clutEntryId, value);
263 if (entryClut8Flag)
264 SetColor(8, clutEntryId, value);
265 }
266}
267
268tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr)
269{
270 int Ey, Epb, Epr;
271 int Eg, Eb, Er;
272
273 Ey = (Y - 16);
274 Epb = (Cb - 128);
275 Epr = (Cr - 128);
276 /* ITU-R 709 */
277 Er = constrain((298 * Ey + 460 * Epr) / 256, 0, 255);
278 Eg = constrain((298 * Ey - 55 * Epb - 137 * Epr) / 256, 0, 255);
279 Eb = constrain((298 * Ey + 543 * Epb ) / 256, 0, 255);
280
281 return (Er << 16) | (Eg << 8) | Eb;
282}
283
284void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color)
285{
286 switch (Bpp) {
287 case 2: palette2.SetColor(Index, Color); break;
288 case 4: palette4.SetColor(Index, Color); break;
289 case 8: palette8.SetColor(Index, Color); break;
290 default: esyslog("ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)", Bpp, Index, Color)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)"
, Bpp, Index, Color) : void() )
;
291 }
292}
293
294const cPalette *cSubtitleClut::GetPalette(int Bpp)
295{
296 switch (Bpp) {
297 case 2: return &palette2;
298 case 4: return &palette4;
299 case 8: return &palette8;
300 default: esyslog("ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)", Bpp)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)"
, Bpp) : void() )
;
301 }
302 return &palette8;
303}
304
305// --- cSubtitleObject -------------------------------------------------------
306
307class cSubtitleObject : public cListObject {
308private:
309 int objectId;
310 int objectVersionNumber;
311 int objectCodingMethod;
312 bool nonModifyingColorFlag;
313 int topLength;
314 int botLength;
315 uchar *topData;
316 uchar *botData;
317 char *txtData;
318 int lineHeight;
319 void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length);
320 bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
321 bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
322 bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
323 void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even);
324 void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
325public:
326 cSubtitleObject(int ObjectId);
327 ~cSubtitleObject();
328 void Parse(cBitStream &bs);
329 int ObjectId(void) { return objectId; }
330 int ObjectVersionNumber(void) { return objectVersionNumber; }
331 int ObjectCodingMethod(void) { return objectCodingMethod; }
332 bool NonModifyingColorFlag(void) { return nonModifyingColorFlag; }
333 void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg);
334 };
335
336cSubtitleObject::cSubtitleObject(int ObjectId)
337{
338 objectId = ObjectId;
339 objectVersionNumber = -1;
340 objectCodingMethod = -1;
341 nonModifyingColorFlag = false;
342 topLength = 0;
343 botLength = 0;
344 topData = NULL__null;
345 botData = NULL__null;
346 txtData = NULL__null;
347 lineHeight = 26; // configurable subtitling font size?
348}
349
350cSubtitleObject::~cSubtitleObject()
351{
352 free(topData);
353 free(botData);
354 free(txtData);
355}
356
357void cSubtitleObject::Parse(cBitStream &bs)
358{
359 int Version = bs.GetBits(4);
360 if (objectVersionNumber == Version)
1
Taking false branch
361 return; // no update
362 objectVersionNumber = Version;
363 objectCodingMethod = bs.GetBits(2);
364 nonModifyingColorFlag = bs.GetBit();
365 bs.SkipBit(); // reserved
366 dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag)if (DebugObjects) SD.WriteHtml("<b>object</b> id %d version %d method %d modify %d"
, objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag
)
; // no "<br>\n" here, DecodeCharacterString() may add data
367 if (objectCodingMethod == 0) { // coding of pixels
2
Taking true branch
368 topLength = bs.GetBits(16);
369 botLength = bs.GetBits(16);
370 free(topData);
371 if ((topData = MALLOC(uchar, topLength)(uchar *)malloc(sizeof(uchar) * (topLength))) != NULL__null)
3
Taking false branch
372 memcpy(topData, bs.GetData(), topLength);
373 else
374 topLength = 0;
375 free(botData);
376 if ((botData = MALLOC(uchar, botLength)(uchar *)malloc(sizeof(uchar) * (botLength))) != NULL__null)
4
Taking true branch
377 memcpy(botData, bs.GetData() + topLength, botLength);
5
Null pointer passed as an argument to a 'nonnull' parameter
378 else
379 botLength = 0;
380 bs.WordAlign();
381 }
382 else if (objectCodingMethod == 1) { // coded as a string of characters
383 int numberOfCodes = bs.GetBits(8);
384 DecodeCharacterString(bs.GetData(), numberOfCodes);
385 }
386 dbgobjects("<br>\n")if (DebugObjects) SD.WriteHtml("<br>\n");
387 if (DebugObjects) {
388 // We can't get the actual clut here, so we use a default one. This may lead to
389 // funny colors, but we just want to get a rough idea of what's in the object, anyway.
390 cSubtitleClut Clut(0);
391 cBitmap b(1920, 1080, 8);
392 b.Replace(*Clut.GetPalette(b.Bpp()));
393 b.Clean();
394 Render(&b, 0, 0, 0, 1);
395 int x1, y1, x2, y2;
396 if (b.Dirty(x1, y1, x2, y2)) {
397 cString ImgName = SD.WriteJpeg(&b, x2, y2);
398 dbgobjects("<img src=\"%s\"><br>\n", *ImgName)if (DebugObjects) SD.WriteHtml("<img src=\"%s\"><br>\n"
, *ImgName)
;
399 }
400 }
401}
402
403void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
404{
405 // "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
406 // character_code to be a 16-bit index number into the character table identified
407 // in the subtitle_descriptor. However, the "subtitling_descriptor" <sic> according to
408 // "ETSI EN 300 468 V1.13.1 (2012-04)" doesn't contain a "character table identifier".
409 // It only contains a three letter language code, without any specification as to how
410 // this is related to a specific character table.
411 // Apparently the first "code" in textual subtitles contains the character table
412 // identifier, and all codes are 8-bit only. So let's first make Data a string of
413 // 8-bit characters:
414 if (NumberOfCodes > 0) {
415 char txt[NumberOfCodes + 1];
416 for (int i = 0; i < NumberOfCodes; i++)
417 txt[i] = Data[i * 2 + 1];
418 txt[NumberOfCodes] = 0;
419 bool singleByte;
420 const uchar *from = (uchar *)txt;
421 int len = NumberOfCodes;
422 const char *CharacterTable = SI::getCharacterTable(from, len, &singleByte);
423 dbgobjects(" table %s single %d raw '%s'", CharacterTable, singleByte, from)if (DebugObjects) SD.WriteHtml(" table %s single %d raw '%s'"
, CharacterTable, singleByte, from)
;
424 cCharSetConv conv(CharacterTable, cCharSetConv::SystemCharacterTable());
425 const char *s = conv.Convert((const char *)from);
426 dbgobjects(" conv '%s'", s)if (DebugObjects) SD.WriteHtml(" conv '%s'", s);
427 free(txtData);
428 txtData = strdup(s);
429 }
430}
431
432void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
433{
434 int x = 0;
435 int y = Even ? 0 : 1;
436 uint8_t map2to4[ 4] = { 0x00, 0x07, 0x08, 0x0F };
437 uint8_t map2to8[ 4] = { 0x00, 0x77, 0x88, 0xFF };
438 uint8_t map4to8[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
439 const uint8_t *mapTable = NULL__null;
440 cBitStream bs(Data, Length * 8);
441 while (!bs.IsEOF()) {
442 switch (bs.GetBits(8)) {
443 case 0x10:
444 dbgpixel("2-bit / pixel code string<br>\n")if (DebugPixel) SD.WriteHtml("2-bit / pixel code string<br>\n"
)
;
445 switch (Bitmap->Bpp()) {
446 case 8: mapTable = map2to8; break;
447 case 4: mapTable = map2to4; break;
448 default: mapTable = NULL__null; break;
449 }
450 while (Decode2BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
451 ;
452 bs.ByteAlign();
453 break;
454 case 0x11:
455 dbgpixel("4-bit / pixel code string<br>\n")if (DebugPixel) SD.WriteHtml("4-bit / pixel code string<br>\n"
)
;
456 switch (Bitmap->Bpp()) {
457 case 8: mapTable = map4to8; break;
458 default: mapTable = NULL__null; break;
459 }
460 while (Decode4BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
461 ;
462 bs.ByteAlign();
463 break;
464 case 0x12:
465 dbgpixel("8-bit / pixel code string<br>\n")if (DebugPixel) SD.WriteHtml("8-bit / pixel code string<br>\n"
)
;
466 while (Decode8BppCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF())
467 ;
468 break;
469 case 0x20:
470 dbgpixel("sub block 2 to 4 map<br>\n")if (DebugPixel) SD.WriteHtml("sub block 2 to 4 map<br>\n"
)
;
471 for (int i = 0; i < 4; ++i)
472 map2to4[i] = bs.GetBits(4);
473 break;
474 case 0x21:
475 dbgpixel("sub block 2 to 8 map<br>\n")if (DebugPixel) SD.WriteHtml("sub block 2 to 8 map<br>\n"
)
;
476 for (int i = 0; i < 4; ++i)
477 map2to8[i] = bs.GetBits(8);
478 break;
479 case 0x22:
480 dbgpixel("sub block 4 to 8 map<br>\n")if (DebugPixel) SD.WriteHtml("sub block 4 to 8 map<br>\n"
)
;
481 for (int i = 0; i < 16; ++i)
482 map4to8[i] = bs.GetBits(8);
483 break;
484 case 0xF0:
485 dbgpixel("end of object line<br>\n")if (DebugPixel) SD.WriteHtml("end of object line<br>\n"
)
;
486 x = 0;
487 y += 2;
488 break;
489 default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__)if (DebugPixel) SD.WriteHtml("unknown sub block %s %d<br>\n"
, __FUNCTION__, 489)
;
490 }
491 }
492}
493
494void cSubtitleObject::DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
495{
496 if (nonModifyingColorFlag && Index == 1)
497 return;
498 for (int pos = x; pos < x + Length; pos++)
499 Bitmap->SetIndex(pos, y, Index);
500}
501
502bool cSubtitleObject::Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
503{
504 int rl = 0;
505 int color = 0;
506 uchar code = bs->GetBits(2);
507 if (code) {
508 color = code;
509 rl = 1;
510 }
511 else if (bs->GetBit()) { // switch_1
512 rl = bs->GetBits(3) + 3;
513 color = bs->GetBits(2);
514 }
515 else if (bs->GetBit()) // switch_2
516 rl = 1; //color 0
517 else {
518 switch (bs->GetBits(2)) { // switch_3
519 case 0:
520 return false;
521 case 1:
522 rl = 2; //color 0
523 break;
524 case 2:
525 rl = bs->GetBits(4) + 12;
526 color = bs->GetBits(2);
527 break;
528 case 3:
529 rl = bs->GetBits(8) + 29;
530 color = bs->GetBits(2);
531 break;
532 default: ;
533 }
534 }
535 if (MapTable)
536 color = MapTable[color];
537 DrawLine(Bitmap, px + x, py + y, color, rl);
538 x += rl;
539 return true;
540}
541
542bool cSubtitleObject::Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
543{
544 int rl = 0;
545 int color = 0;
546 uchar code = bs->GetBits(4);
547 if (code) {
548 color = code;
549 rl = 1;
550 }
551 else if (bs->GetBit() == 0) { // switch_1
552 code = bs->GetBits(3);
553 if (code)
554 rl = code + 2; //color 0
555 else
556 return false;
557 }
558 else if (bs->GetBit() == 0) { // switch_2
559 rl = bs->GetBits(2) + 4;
560 color = bs->GetBits(4);
561 }
562 else {
563 switch (bs->GetBits(2)) { // switch_3
564 case 0: // color 0
565 rl = 1;
566 break;
567 case 1: // color 0
568 rl = 2;
569 break;
570 case 2:
571 rl = bs->GetBits(4) + 9;
572 color = bs->GetBits(4);
573 break;
574 case 3:
575 rl = bs->GetBits(8) + 25;
576 color = bs->GetBits(4);
577 break;
578 }
579 }
580 if (MapTable)
581 color = MapTable[color];
582 DrawLine(Bitmap, px + x, py + y, color, rl);
583 x += rl;
584 return true;
585}
586
587bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
588{
589 int rl = 0;
590 int color = 0;
591 uchar code = bs->GetBits(8);
592 if (code) {
593 color = code;
594 rl = 1;
595 }
596 else if (bs->GetBit()) {
597 rl = bs->GetBits(7);
598 color = bs->GetBits(8);
599 }
600 else {
601 code = bs->GetBits(7);
602 if (code)
603 rl = code; // color 0
604 else
605 return false;
606 }
607 DrawLine(Bitmap, px + x, py + y, color, rl);
608 x += rl;
609 return true;
610}
611
612void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
613{
614 if (objectCodingMethod == 0) { // coding of pixels
615 DecodeSubBlock(Bitmap, px, py, topData, topLength, true);
616 if (botLength)
617 DecodeSubBlock(Bitmap, px, py, botData, botLength, false);
618 else
619 DecodeSubBlock(Bitmap, px, py, topData, topLength, false);
620 }
621 else if (objectCodingMethod == 1) { // coded as a string of characters
622 if (txtData) {
623 //TODO couldn't we draw the text directly into Bitmap?
624 cFont *font = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize);
625 cBitmap tmp(font->Width(txtData), font->Height(), Bitmap->Bpp());
626 double factor = (double)lineHeight / font->Height();
627 tmp.DrawText(0, 0, txtData, Bitmap->Color(IndexFg), Bitmap->Color(IndexBg), font);
628 cBitmap *scaled = tmp.Scaled(factor, factor, true);
629 Bitmap->DrawBitmap(px, py, *scaled);
630 delete scaled;
631 delete font;
632 }
633 }
634}
635
636// --- cSubtitleObjects ------------------------------------------------------
637
638class cSubtitleObjects : public cList<cSubtitleObject> {
639public:
640 cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
641 };
642
643cSubtitleObject *cSubtitleObjects::GetObjectById(int ObjectId, bool New)
644{
645 for (cSubtitleObject *so = First(); so; so = Next(so)) {
646 if (so->ObjectId() == ObjectId)
647 return so;
648 }
649 if (!New)
650 return NULL__null;
651 cSubtitleObject *Object = new cSubtitleObject(ObjectId);
652 Add(Object);
653 return Object;
654}
655
656// --- cSubtitleObjectRef ----------------------------------------------------
657
658class cSubtitleObjectRef : public cListObject {
659private:
660 int objectId;
661 int objectType;
662 int objectProviderFlag;
663 int objectHorizontalPosition;
664 int objectVerticalPosition;
665 int foregroundPixelCode;
666 int backgroundPixelCode;
667public:
668 cSubtitleObjectRef(cBitStream &bs);
669 int ObjectId(void) { return objectId; }
670 int ObjectType(void) { return objectType; }
671 int ObjectProviderFlag(void) { return objectProviderFlag; }
672 int ObjectHorizontalPosition(void) { return objectHorizontalPosition; }
673 int ObjectVerticalPosition(void) { return objectVerticalPosition; }
674 int ForegroundPixelCode(void) { return foregroundPixelCode; }
675 int BackgroundPixelCode(void) { return backgroundPixelCode; }
676 };
677
678cSubtitleObjectRef::cSubtitleObjectRef(cBitStream &bs)
679{
680 objectId = bs.GetBits(16);
681 objectType = bs.GetBits(2);
682 objectProviderFlag = bs.GetBits(2);
683 objectHorizontalPosition = bs.GetBits(12);
684 bs.SkipBits(4); // reserved
685 objectVerticalPosition = bs.GetBits(12);
686 if (objectType == 0x01 || objectType == 0x02) {
687 foregroundPixelCode = bs.GetBits(8);
688 backgroundPixelCode = bs.GetBits(8);
689 }
690 else {
691 foregroundPixelCode = 0;
692 backgroundPixelCode = 0;
693 }
694 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)if (DebugRegions) SD.WriteHtml("<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
)
;
695}
696
697// --- cSubtitleRegion -------------------------------------------------------
698
699class cSubtitleRegion : public cListObject {
700private:
701 int regionId;
702 int regionVersionNumber;
703 bool regionFillFlag;
704 int regionWidth;
705 int regionHeight;
706 int regionLevelOfCompatibility;
707 int regionDepth;
708 int clutId;
709 int region8bitPixelCode;
710 int region4bitPixelCode;
711 int region2bitPixelCode;
712 cList<cSubtitleObjectRef> objectRefs;
713public:
714 cSubtitleRegion(int RegionId);
715 void Parse(cBitStream &bs);
716 int RegionId(void) { return regionId; }
717 int RegionVersionNumber(void) { return regionVersionNumber; }
718 bool RegionFillFlag(void) { return regionFillFlag; }
719 int RegionWidth(void) { return regionWidth; }
720 int RegionHeight(void) { return regionHeight; }
721 int RegionLevelOfCompatibility(void) { return regionLevelOfCompatibility; }
722 int RegionDepth(void) { return regionDepth; }
723 int ClutId(void) { return clutId; }
724 void Render(cBitmap *Bitmap, cSubtitleObjects *Objects);
725 };
726
727cSubtitleRegion::cSubtitleRegion(int RegionId)
728{
729 regionId = RegionId;
730 regionVersionNumber = -1;
731 regionFillFlag = false;
732 regionWidth = 0;
733 regionHeight = 0;
734 regionLevelOfCompatibility = 0;
735 regionDepth = 0;
736 clutId = -1;
737 region8bitPixelCode = 0;
738 region4bitPixelCode = 0;
739 region2bitPixelCode = 0;
740}
741
742void cSubtitleRegion::Parse(cBitStream &bs)
743{
744 int Version = bs.GetBits(4);
745 if (regionVersionNumber == Version)
746 return; // no update
747 regionVersionNumber = Version;
748 regionFillFlag = bs.GetBit();
749 bs.SkipBits(3); // reserved
750 regionWidth = bs.GetBits(16);
751 regionHeight = bs.GetBits(16);
752 regionLevelOfCompatibility = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
753 regionDepth = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
754 bs.SkipBits(2); // reserved
755 clutId = bs.GetBits(8);
756 region8bitPixelCode = bs.GetBits(8);
757 region4bitPixelCode = bs.GetBits(4);
758 region2bitPixelCode = bs.GetBits(2);
759 bs.SkipBits(2); // reserved
760 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)if (DebugRegions) SD.WriteHtml("<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
)
;
761 // no objectRefs.Clear() here!
762 while (!bs.IsEOF())
763 objectRefs.Add(new cSubtitleObjectRef(bs));
764}
765
766void cSubtitleRegion::Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
767{
768 if (regionFillFlag) {
769 switch (Bitmap->Bpp()) {
770 case 2: Bitmap->Fill(region2bitPixelCode); break;
771 case 4: Bitmap->Fill(region4bitPixelCode); break;
772 case 8: Bitmap->Fill(region8bitPixelCode); break;
773 default: dbgregions("unknown bpp %d (%s %d)<br>\n", Bitmap->Bpp(), __FUNCTION__, __LINE__)if (DebugRegions) SD.WriteHtml("unknown bpp %d (%s %d)<br>\n"
, Bitmap->Bpp(), __FUNCTION__, 773)
;
774 }
775 }
776 for (cSubtitleObjectRef *sor = objectRefs.First(); sor; sor = objectRefs.Next(sor)) {
777 if (cSubtitleObject *so = Objects->GetObjectById(sor->ObjectId())) {
778 so->Render(Bitmap, sor->ObjectHorizontalPosition(), sor->ObjectVerticalPosition(), sor->ForegroundPixelCode(), sor->BackgroundPixelCode());
779 }
780 }
781}
782
783// --- cSubtitleRegionRef ----------------------------------------------------
784
785class cSubtitleRegionRef : public cListObject {
786private:
787 int regionId;
788 int regionHorizontalAddress;
789 int regionVerticalAddress;
790public:
791 cSubtitleRegionRef(cBitStream &bs);
792 int RegionId(void) { return regionId; }
793 int RegionHorizontalAddress(void) { return regionHorizontalAddress; }
794 int RegionVerticalAddress(void) { return regionVerticalAddress; }
795 };
796
797cSubtitleRegionRef::cSubtitleRegionRef(cBitStream &bs)
798{
799 regionId = bs.GetBits(8);
800 bs.SkipBits(8); // reserved
801 regionHorizontalAddress = bs.GetBits(16);
802 regionVerticalAddress = bs.GetBits(16);
803 dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress)if (DebugPages) SD.WriteHtml("<b>regionref</b> id %d tx %d y %d<br>\n"
, regionId, regionHorizontalAddress, regionVerticalAddress)
;
804}
805
806// --- cDvbSubtitlePage ------------------------------------------------------
807
808class cDvbSubtitlePage : public cListObject {
809private:
810 int pageId;
811 int pageTimeout;
812 int pageVersionNumber;
813 int pageState;
814 int64_t pts;
815 bool pending;
816 cSubtitleObjects objects;
817 cList<cSubtitleClut> cluts;
818 cList<cSubtitleRegion> regions;
819 cList<cSubtitleRegionRef> regionRefs;
820public:
821 cDvbSubtitlePage(int PageId);
822 void Parse(int64_t Pts, cBitStream &bs);
823 int PageId(void) { return pageId; }
824 int PageTimeout(void) { return pageTimeout; }
825 int PageVersionNumber(void) { return pageVersionNumber; }
826 int PageState(void) { return pageState; }
827 int64_t Pts(void) const { return pts; }
828 bool Pending(void) { return pending; }
829 cSubtitleObjects *Objects(void) { return &objects; }
830 tArea *GetAreas(int &NumAreas, double FactorX, double FactorY);
831 cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
832 cSubtitleClut *GetClutById(int ClutId, bool New = false);
833 cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
834 cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
835 void SetPending(bool Pending) { pending = Pending; }
836 };
837
838cDvbSubtitlePage::cDvbSubtitlePage(int PageId)
839{
840 pageId = PageId;
841 pageTimeout = 0;
842 pageVersionNumber = -1;
843 pageState = -1;
844 pts = -1;
845 pending = false;
846}
847
848void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
849{
850 if (Pts >= 0)
851 pts = Pts;
852 pageTimeout = bs.GetBits(8);
853 int Version = bs.GetBits(4);
854 if (pageVersionNumber == Version)
855 return; // no update
856 pageVersionNumber = Version;
857 pageState = bs.GetBits(2);
858 switch (pageState) {
859 case 0: // normal case - page update
860 break;
861 case 1: // acquisition point - page refresh
862 regions.Clear();
863 objects.Clear();
864 break;
865 case 2: // mode change - new page
866 regions.Clear();
867 cluts.Clear();
868 objects.Clear();
869 break;
870 case 3: // reserved
871 break;
872 default: dbgpages("unknown page state: %d<br>\n", pageState)if (DebugPages) SD.WriteHtml("unknown page state: %d<br>\n"
, pageState)
;
873 }
874 bs.SkipBits(2); // reserved
875 dbgpages("<hr>\n<b>page</b> id %d version %d pts %"PRId64" timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState)if (DebugPages) SD.WriteHtml("<hr>\n<b>page</b> id %d version %d pts %"
"l" "d"" timeout %d state %d<br>\n", pageId, pageVersionNumber
, pts, pageTimeout, pageState)
;
876 regionRefs.Clear();
877 while (!bs.IsEOF())
878 regionRefs.Add(new cSubtitleRegionRef(bs));
879 pending = true;
880}
881
882tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY)
883{
884 if (regions.Count() > 0) {
885 NumAreas = regionRefs.Count();
886 tArea *Areas = new tArea[NumAreas];
887 tArea *a = Areas;
888 for (cSubtitleRegionRef *srr = regionRefs.First(); srr; srr = regionRefs.Next(srr)) {
889 if (cSubtitleRegion *sr = GetRegionById(srr->RegionId())) {
890 a->x1 = int(round(FactorX * srr->RegionHorizontalAddress()));
891 a->y1 = int(round(FactorY * srr->RegionVerticalAddress()));
892 a->x2 = int(round(FactorX * (srr->RegionHorizontalAddress() + sr->RegionWidth() - 1)));
893 a->y2 = int(round(FactorY * (srr->RegionVerticalAddress() + sr->RegionHeight() - 1)));
894 a->bpp = sr->RegionDepth();
895 while ((a->Width() & 3) != 0)
896 a->x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
897 }
898 else
899 a->x1 = a->y1 = a->x2 = a->y2 = a->bpp = 0;
900 a++;
901 }
902 return Areas;
903 }
904 NumAreas = 0;
905 return NULL__null;
906}
907
908cSubtitleClut *cDvbSubtitlePage::GetClutById(int ClutId, bool New)
909{
910 for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
911 if (sc->ClutId() == ClutId)
912 return sc;
913 }
914 if (!New)
915 return NULL__null;
916 cSubtitleClut *Clut = new cSubtitleClut(ClutId);
917 cluts.Add(Clut);
918 return Clut;
919}
920
921cSubtitleRegion *cDvbSubtitlePage::GetRegionById(int RegionId, bool New)
922{
923 for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
924 if (sr->RegionId() == RegionId)
925 return sr;
926 }
927 if (!New)
928 return NULL__null;
929 cSubtitleRegion *Region = new cSubtitleRegion(RegionId);
930 regions.Add(Region);
931 return Region;
932}
933
934cSubtitleObject *cDvbSubtitlePage::GetObjectById(int ObjectId, bool New)
935{
936 return objects.GetObjectById(ObjectId, New);
937}
938
939// --- cDvbSubtitleAssembler -------------------------------------------------
940
941class cDvbSubtitleAssembler {
942private:
943 uchar *data;
944 int length;
945 int pos;
946 int size;
947 bool Realloc(int Size);
948public:
949 cDvbSubtitleAssembler(void);
950 virtual ~cDvbSubtitleAssembler();
951 void Reset(void);
952 unsigned char *Get(int &Length);
953 void Put(const uchar *Data, int Length);
954 };
955
956cDvbSubtitleAssembler::cDvbSubtitleAssembler(void)
957{
958 data = NULL__null;
959 size = 0;
960 Reset();
961}
962
963cDvbSubtitleAssembler::~cDvbSubtitleAssembler()
964{
965 free(data);
966}
967
968void cDvbSubtitleAssembler::Reset(void)
969{
970 length = 0;
971 pos = 0;
972}
973
974bool cDvbSubtitleAssembler::Realloc(int Size)
975{
976 if (Size > size) {
977 Size = max(Size, 2048);
978 if (uchar *NewBuffer = (uchar *)realloc(data, Size)) {
979 size = Size;
980 data = NewBuffer;
981 }
982 else {
983 esyslog("ERROR: can't allocate memory for subtitle assembler")void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't allocate memory for subtitle assembler"
) : void() )
;
984 length = 0;
985 size = 0;
986 free(data);
987 data = NULL__null;
988 return false;
989 }
990 }
991 return true;
992}
993
994unsigned char *cDvbSubtitleAssembler::Get(int &Length)
995{
996 if (length > pos + 5) {
997 Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
998 if (length >= pos + Length) {
999 unsigned char *result = data + pos;
1000 pos += Length;
1001 return result;
1002 }
1003 }
1004 return NULL__null;
1005}
1006
1007void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
1008{
1009 if (Length && Realloc(length + Length)) {
1010 memcpy(data + length, Data, Length);
1011 length += Length;
1012 }
1013}
1014
1015// --- cDvbSubtitleBitmaps ---------------------------------------------------
1016
1017class cDvbSubtitleBitmaps : public cListObject {
1018private:
1019 int state;
1020 int64_t pts;
1021 int timeout;
1022 tArea *areas;
1023 int numAreas;
1024 double osdFactorX;
1025 double osdFactorY;
1026 cVector<cBitmap *> bitmaps;
1027public:
1028 cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY);
1029 ~cDvbSubtitleBitmaps();
1030 int State(void) { return state; }
1031 int64_t Pts(void) { return pts; }
1032 int Timeout(void) { return timeout; }
1033 void AddBitmap(cBitmap *Bitmap);
1034 bool HasBitmaps(void) { return bitmaps.Size(); }
1035 void Draw(cOsd *Osd);
1036 void DbgDump(int WindowWidth, int WindowHeight);
1037 };
1038
1039cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY)
1040{
1041 state = State;
1042 pts = Pts;
1043 timeout = Timeout;
1044 areas = Areas;
1045 numAreas = NumAreas;
1046 osdFactorX = OsdFactorX;
1047 osdFactorY = OsdFactorY;
1048}
1049
1050cDvbSubtitleBitmaps::~cDvbSubtitleBitmaps()
1051{
1052 delete[] areas;
1053 for (int i = 0; i < bitmaps.Size(); i++)
1054 delete bitmaps[i];
1055}
1056
1057void cDvbSubtitleBitmaps::AddBitmap(cBitmap *Bitmap)
1058{
1059 bitmaps.Append(Bitmap);
1060}
1061
1062void cDvbSubtitleBitmaps::Draw(cOsd *Osd)
1063{
1064 bool Scale = !(DoubleEqual(osdFactorX, 1.0) && DoubleEqual(osdFactorY, 1.0));
1065 bool AntiAlias = true;
1066 if (Scale && osdFactorX > 1.0 || osdFactorY > 1.0) {
1067 // Upscaling requires 8bpp:
1068 int Bpp[MAXOSDAREAS16];
1069 for (int i = 0; i < numAreas; i++) {
1070 Bpp[i] = areas[i].bpp;
1071 areas[i].bpp = 8;
1072 }
1073 if (Osd->CanHandleAreas(areas, numAreas) != oeOk) {
1074 for (int i = 0; i < numAreas; i++)
1075 areas[i].bpp = Bpp[i];
1076 AntiAlias = false;
1077 }
1078 }
1079 if (State() == 0 || Osd->SetAreas(areas, numAreas) == oeOk) {
1080 for (int i = 0; i < bitmaps.Size(); i++) {
1081 cBitmap *b = bitmaps[i];
1082 if (Scale)
1083 b = b->Scaled(osdFactorX, osdFactorY, AntiAlias);
1084 Osd->DrawBitmap(int(round(b->X0() * osdFactorX)), int(round(b->Y0() * osdFactorY)), *b);
1085 if (b != bitmaps[i])
1086 delete b;
1087 }
1088 Osd->Flush();
1089 }
1090}
1091
1092void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
1093{
1094 if (!SD.Active())
1095 return;
1096 SD.SetFirstPts(Pts());
1097 double STC = double(cDevice::PrimaryDevice()->GetSTC() - SD.FirstPts()) / 90000;
1098 double Start = double(Pts() - SD.FirstPts()) / 90000;
1099 double Duration = Timeout();
1100 double End = Start + Duration;
1101 cBitmap Bitmap(WindowWidth, WindowHeight, 8);
1102#define DBGBACKGROUND0xA0A0A0 0xA0A0A0
1103 Bitmap.DrawRectangle(0, 0, WindowWidth - 1, WindowHeight - 1, DBGBACKGROUND0xA0A0A0);
1104 for (int i = 0; i < bitmaps.Size(); i++) {
1105 cBitmap *b = bitmaps[i];
1106 Bitmap.DrawBitmap(b->X0(), b->Y0(), *b);
1107 }
1108 cString ImgName = SD.WriteJpeg(&Bitmap);
1109#define BORDER //" border=1"
1110 SD.WriteHtml("<p>%s<br>", State() == 0 ? "page update" : State() == 1 ? "page refresh" : State() == 2 ? "new page" : "???");
1111 SD.WriteHtml("<table" BORDER "><tr><td>");
1112 SD.WriteHtml("%.2f", STC);
1113 SD.WriteHtml("</td><td>");
1114 SD.WriteHtml("<img src=\"%s\">", *ImgName);
1115 SD.WriteHtml("</td><td style=\"height:100%%\"><table" BORDER " style=\"height:100%%\">");
1116 SD.WriteHtml("<tr><td valign=top><b>%.2f</b></td></tr>", Start);
1117 SD.WriteHtml("<tr><td valign=middle>%.2f</td></tr>", Duration);
1118 SD.WriteHtml("<tr><td valign=bottom>%.2f</td></tr>", End);
1119 SD.WriteHtml("</table></td>");
1120 SD.WriteHtml("</tr></table>\n");
1121}
1122
1123// --- cDvbSubtitleConverter -------------------------------------------------
1124
1125int cDvbSubtitleConverter::setupLevel = 0;
1126
1127cDvbSubtitleConverter::cDvbSubtitleConverter(void)
1128:cThread("subtitleConverter")
1129{
1130 dvbSubtitleAssembler = new cDvbSubtitleAssembler;
1131 osd = NULL__null;
1132 frozen = false;
1133 ddsVersionNumber = -1;
1134 displayWidth = windowWidth = 720;
1135 displayHeight = windowHeight = 576;
1136 windowHorizontalOffset = 0;
1137 windowVerticalOffset = 0;
1138 pages = new cList<cDvbSubtitlePage>;
1139 bitmaps = new cList<cDvbSubtitleBitmaps>;
1140 SD.Reset();
1141 Start();
1142}
1143
1144cDvbSubtitleConverter::~cDvbSubtitleConverter()
1145{
1146 Cancel(3);
1147 delete dvbSubtitleAssembler;
1148 delete osd;
1149 delete bitmaps;
1150 delete pages;
1151}
1152
1153void cDvbSubtitleConverter::SetupChanged(void)
1154{
1155 setupLevel++;
1156}
1157
1158void cDvbSubtitleConverter::Reset(void)
1159{
1160 dbgconverter("converter reset -----------------------<br>\n")if (DebugConverter) SD.WriteHtml("converter reset -----------------------<br>\n"
)
;
1161 dvbSubtitleAssembler->Reset();
1162 Lock();
1163 pages->Clear();
1164 bitmaps->Clear();
1165 DELETENULL(osd);
1166 frozen = false;
1167 ddsVersionNumber = -1;
1168 displayWidth = windowWidth = 720;
1169 displayHeight = windowHeight = 576;
1170 windowHorizontalOffset = 0;
1171 windowVerticalOffset = 0;
1172 Unlock();
1173}
1174
1175int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length)
1176{
1177 if (Data && Length > 8) {
1178 int PayloadOffset = PesPayloadOffset(Data);
1179 int SubstreamHeaderLength = 4;
1180 bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1181
1182 // Compatibility mode for old subtitles plugin:
1183 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1184 PayloadOffset--;
1185 SubstreamHeaderLength = 1;
1186 ResetSubtitleAssembler = Data[8] >= 5;
1187 }
1188
1189 if (Length > PayloadOffset + SubstreamHeaderLength) {
1190 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1191 if (pts >= 0)
1192 dbgconverter("converter PTS: %"PRId64"<br>\n", pts)if (DebugConverter) SD.WriteHtml("converter PTS: %""l" "d""<br>\n"
, pts)
;
1193 const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1194 int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1195 if (ResetSubtitleAssembler)
1196 dvbSubtitleAssembler->Reset();
1197
1198 if (length > 3) {
1199 if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1200 dvbSubtitleAssembler->Put(data + 2, length - 2);
1201 else
1202 dvbSubtitleAssembler->Put(data, length);
1203
1204 int Count;
1205 while (true) {
1206 unsigned char *b = dvbSubtitleAssembler->Get(Count);
1207 if (b && b[0] == 0x0F) {
1208 if (ExtractSegment(b, Count, pts) == -1)
1209 break;
1210 }
1211 else
1212 break;
1213 }
1214 }
1215 }
1216 return Length;
1217 }
1218 return 0;
1219}
1220
1221int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1222{
1223 if (Data && Length > 8) {
1224 int PayloadOffset = PesPayloadOffset(Data);
1225 if (Length > PayloadOffset) {
1226 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1227 if (pts >= 0)
1228 dbgconverter("converter PTS: %"PRId64"<br>\n", pts)if (DebugConverter) SD.WriteHtml("converter PTS: %""l" "d""<br>\n"
, pts)
;
1229 const uchar *data = Data + PayloadOffset;
1230 int length = Length - PayloadOffset;
1231 if (length > 3) {
1232 if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1233 data += 2;
1234 length -= 2;
1235 }
1236 const uchar *b = data;
1237 while (length > 0) {
1238 if (b[0] == 0x0F) {
1239 int n = ExtractSegment(b, length, pts);
1240 if (n < 0)
1241 break;
1242 b += n;
1243 length -= n;
1244 }
1245 else
1246 break;
1247 }
1248 }
1249 }
1250 return Length;
1251 }
1252 return 0;
1253}
1254
1255#define LimitTo32Bit(n)((n) & 0x00000000FFFFFFFFL) ((n) & 0x00000000FFFFFFFFL)
1256
1257void cDvbSubtitleConverter::Action(void)
1258{
1259 int LastSetupLevel = setupLevel;
1260 cTimeMs Timeout;
1261 while (Running()) {
1262 int WaitMs = 100;
1263 if (!frozen) {
1264 LOCK_THREADcThreadLock ThreadLock(this);
1265 if (osd) {
1266 int NewSetupLevel = setupLevel;
1267 if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
1268 dbgoutput("closing osd<br>\n")if (DebugOutput) SD.WriteHtml("closing osd<br>\n");
1269 DELETENULL(osd);
1270 }
1271 LastSetupLevel = NewSetupLevel;
1272 }
1273 for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
1274 // Calculate the Delta between the STC (the current timestamp of the video)
1275 // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1276 // A negative Delta means that the bitmap will be presented in the future:
1277 int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1278 int64_t Delta = LimitTo32Bit(STC)((STC) & 0x00000000FFFFFFFFL) - LimitTo32Bit(sb->Pts())((sb->Pts()) & 0x00000000FFFFFFFFL); // some devices only deliver 32 bits
1279 if (Delta > (int64_t(1) << 31))
1280 Delta -= (int64_t(1) << 32);
1281 else if (Delta < -((int64_t(1) << 31) - 1))
1282 Delta += (int64_t(1) << 32);
1283 Delta /= 90; // STC and PTS are in 1/90000s
1284 if (Delta >= 0) { // found a bitmap that shall be displayed...
1285 if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
1286 if (!sb->HasBitmaps()) {
1287 Timeout.Set();
1288 WaitMs = 0;
1289 }
1290 else if (AssertOsd()) {
1291 dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count())if (DebugOutput) SD.WriteHtml("showing bitmap #%d of %d<br>\n"
, sb->Index() + 1, bitmaps->Count())
;
1292 sb->Draw(osd);
1293 Timeout.Set(sb->Timeout() * 1000);
1294 dbgconverter("PTS: %"PRId64" STC: %"PRId64" (%"PRId64") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout())if (DebugConverter) SD.WriteHtml("PTS: %""l" "d"" STC: %""l"
"d"" (%""l" "d"") timeout: %d<br>\n", sb->Pts(), STC
, Delta, sb->Timeout())
;
1295 }
1296 }
1297 else
1298 WaitMs = 0; // bitmap already timed out, so try next one immediately
1299 dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count())if (DebugOutput) SD.WriteHtml("deleting bitmap #%d of %d<br>\n"
, sb->Index() + 1, bitmaps->Count())
;
1300 bitmaps->Del(sb);
1301 break;
1302 }
1303 }
1304 }
1305 cCondWait::SleepMs(WaitMs);
1306 }
1307}
1308
1309void cDvbSubtitleConverter::SetOsdData(void)
1310{
1311 int OsdWidth, OsdHeight;
1312 double OsdAspect;
1313 int VideoWidth, VideoHeight;
1314 double VideoAspect;
1315 cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1316 cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
1317 if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1318 osdFactorX = osdFactorY = 1.0;
1319 osdDeltaX = osdDeltaY = 0;
1320 }
1321 else {
1322 osdFactorX = VideoAspect * OsdHeight / displayWidth;
1323 osdFactorY = double(OsdHeight) / displayHeight;
1324 osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1325 osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1326 }
1327}
1328
1329bool cDvbSubtitleConverter::AssertOsd(void)
1330{
1331 LOCK_THREADcThreadLock ThreadLock(this);
1332 if (!osd) {
1333 SetOsdData();
1334 osd = cOsdProvider::NewOsd(int(round(osdFactorX * windowHorizontalOffset + osdDeltaX)), int(round(osdFactorY * windowVerticalOffset + osdDeltaY)) + Setup.SubtitleOffset, OSD_LEVEL_SUBTITLES10);
1335 }
1336 return osd != NULL__null;
1337}
1338
1339cDvbSubtitlePage *cDvbSubtitleConverter::GetPageById(int PageId, bool New)
1340{
1341 for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1342 if (sp->PageId() == PageId)
1343 return sp;
1344 }
1345 if (!New)
1346 return NULL__null;
1347 cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1348 pages->Add(Page);
1349 return Page;
1350}
1351
1352int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1353{
1354 cBitStream bs(Data, Length * 8);
1355 if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1356 int segmentType = bs.GetBits(8);
1357 if (segmentType == STUFFING_SEGMENT0xFF)
1358 return -1;
1359 LOCK_THREADcThreadLock ThreadLock(this);
1360 cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1361 int segmentLength = bs.GetBits(16);
1362 if (!bs.SetLength(bs.Index() + segmentLength * 8))
1363 return -1;
1364 switch (segmentType) {
1365 case PAGE_COMPOSITION_SEGMENT0x10: {
1366 if (page->Pending()) {
1367 dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n")if (DebugSegments) SD.WriteHtml("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n"
)
;
1368 FinishPage(page);
1369 }
1370 dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("PAGE_COMPOSITION_SEGMENT<br>\n"
)
;
1371 page->Parse(Pts, bs);
1372 SD.SetFactor(double(DBGBITMAPWIDTH400) / windowWidth);
1373 break;
1374 }
1375 case REGION_COMPOSITION_SEGMENT0x11: {
1376 dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("REGION_COMPOSITION_SEGMENT<br>\n"
)
;
1377 cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1378 region->Parse(bs);
1379 break;
1380 }
1381 case CLUT_DEFINITION_SEGMENT0x12: {
1382 dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("CLUT_DEFINITION_SEGMENT<br>\n"
)
;
1383 cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1384 clut->Parse(bs);
1385 break;
1386 }
1387 case OBJECT_DATA_SEGMENT0x13: {
1388 dbgsegments("OBJECT_DATA_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("OBJECT_DATA_SEGMENT<br>\n"
)
;
1389 cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1390 object->Parse(bs);
1391 break;
1392 }
1393 case DISPLAY_DEFINITION_SEGMENT0x14: {
1394 dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("DISPLAY_DEFINITION_SEGMENT<br>\n"
)
;
1395 int version = bs.GetBits(4);
1396 if (version != ddsVersionNumber) {
1397 bool displayWindowFlag = bs.GetBit();
1398 windowHorizontalOffset = 0;
1399 windowVerticalOffset = 0;
1400 bs.SkipBits(3); // reserved
1401 displayWidth = windowWidth = bs.GetBits(16) + 1;
1402 displayHeight = windowHeight = bs.GetBits(16) + 1;
1403 if (displayWindowFlag) {
1404 windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1405 windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1406 windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1407 windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1408 }
1409 SetOsdData();
1410 ddsVersionNumber = version;
1411 dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset)if (DebugDisplay) SD.WriteHtml("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n"
, ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight
, windowHorizontalOffset, windowVerticalOffset)
;
1412 }
1413 break;
1414 }
1415 case DISPARITY_SIGNALING_SEGMENT0x15: {
1416 dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("DISPARITY_SIGNALING_SEGMENT<br>\n"
)
;
1417 bs.SkipBits(4); // dss_version_number
1418 bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1419 bs.SkipBits(3); // reserved
1420 bs.SkipBits(8); // page_default_disparity_shift
1421 if (disparity_shift_update_sequence_page_flag) {
1422 bs.SkipBits(8); // disparity_shift_update_sequence_length
1423 bs.SkipBits(24); // interval_duration[23..0]
1424 int division_period_count = bs.GetBits(8);
1425 for (int i = 0; i < division_period_count; ++i) {
1426 bs.SkipBits(8); // interval_count
1427 bs.SkipBits(8); // disparity_shift_update_integer_part
1428 }
1429 }
1430 while (!bs.IsEOF()) {
1431 bs.SkipBits(8); // region_id
1432 bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1433 bs.SkipBits(5); // reserved
1434 int number_of_subregions_minus_1 = bs.GetBits(2);
1435 for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1436 if (number_of_subregions_minus_1 > 0) {
1437 bs.SkipBits(16); // subregion_horizontal_position
1438 bs.SkipBits(16); // subregion_width
1439 }
1440 bs.SkipBits(8); // subregion_disparity_shift_integer_part
1441 bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1442 bs.SkipBits(4); // reserved
1443 if (disparity_shift_update_sequence_region_flag) {
1444 bs.SkipBits(8); // disparity_shift_update_sequence_length
1445 bs.SkipBits(24); // interval_duration[23..0]
1446 int division_period_count = bs.GetBits(8);
1447 for (int i = 0; i < division_period_count; ++i) {
1448 bs.SkipBits(8); // interval_count
1449 bs.SkipBits(8); // disparity_shift_update_integer_part
1450 }
1451 }
1452 }
1453 }
1454 break;
1455 }
1456 case END_OF_DISPLAY_SET_SEGMENT0x80: {
1457 dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n")if (DebugSegments) SD.WriteHtml("END_OF_DISPLAY_SET_SEGMENT<br>\n"
)
;
1458 FinishPage(page);
1459 page->SetPending(false);
1460 break;
1461 }
1462 default:
1463 dbgsegments("*** unknown segment type: %02X<br>\n", segmentType)if (DebugSegments) SD.WriteHtml("*** unknown segment type: %02X<br>\n"
, segmentType)
;
1464 }
1465 return bs.Length() / 8;
1466 }
1467 return -1;
1468}
1469
1470void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
1471{
1472 if (!AssertOsd())
1473 return;
1474 int NumAreas;
1475 tArea *Areas = Page->GetAreas(NumAreas, osdFactorX, osdFactorY);
1476 int Bpp = 8;
1477 bool Reduced = false;
1478 while (osd && osd->CanHandleAreas(Areas, NumAreas) != oeOk) {
1479 dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(Areas, NumAreas))if (DebugOutput) SD.WriteHtml("CanHandleAreas: %d<br>\n"
, osd->CanHandleAreas(Areas, NumAreas))
;
1480 int HalfBpp = Bpp / 2;
1481 if (HalfBpp >= 2) {
1482 for (int i = 0; i < NumAreas; i++) {
1483 if (Areas[i].bpp >= Bpp) {
1484 Areas[i].bpp = HalfBpp;
1485 Reduced = true;
1486 }
1487 }
1488 Bpp = HalfBpp;
1489 }
1490 else
1491 return; // unable to draw bitmaps
1492 }
1493 cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY);
1494 bitmaps->Add(Bitmaps);
1495 for (int i = 0; i < NumAreas; i++) {
1496 if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1497 if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1498 if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1499 cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1500 bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1501 sr->Render(bm, Page->Objects());
1502 if (Reduced) {
1503 if (sr->RegionDepth() != Areas[i].bpp) {
1504 if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1505 //TODO this is untested - didn't have any such subtitle stream
1506 cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1507 dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp)if (DebugRegions) SD.WriteHtml("reduce region %d bpp %d level %d area bpp %d<br>\n"
, sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility
(), Areas[i].bpp)
;
1508 bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1509 }
1510 else {
1511 dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp)if (DebugRegions) SD.WriteHtml("condense region %d bpp %d level %d area bpp %d<br>\n"
, sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility
(), Areas[i].bpp)
;
1512 bm->ShrinkBpp(Areas[i].bpp);
1513 }
1514 }
1515 }
1516 bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1517 Bitmaps->AddBitmap(bm);
1518 }
1519 }
1520 }
1521 }
1522 if (DebugPages)
1523 Bitmaps->DbgDump(windowWidth, windowHeight);
1524}