vdr  1.7.31
dvbspu.c
Go to the documentation of this file.
1 /*
2  * SPU decoder for DVB devices
3  *
4  * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net>
5  *
6  * This code is distributed under the terms and conditions of the
7  * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
8  *
9  * parts of this file are derived from the OMS program.
10  *
11  * $Id: dvbspu.c 2.9 2011/12/10 14:39:19 kls Exp $
12  */
13 
14 #include "dvbspu.h"
15 #include <assert.h>
16 #include <string.h>
17 #include <inttypes.h>
18 #include <math.h>
19 
20 /*
21  * cDvbSpubitmap:
22  *
23  * this is a bitmap of the full screen and two palettes
24  * the normal palette for the background and the highlight palette
25  *
26  * Inputs:
27  * - a SPU rle encoded image on creation, which will be decoded into
28  * the full screen indexed bitmap
29  *
30  * Output:
31  * - a minimal sized cDvbSpuBitmap a given palette, the indexed bitmap
32  * will be scanned to get the smallest possible resulting bitmap considering
33  * transparencies
34  */
35 
36 // #define SPUDEBUG
37 
38 #ifdef SPUDEBUG
39 #define DEBUG(format, args...) printf (format, ## args)
40 #else
41 #define DEBUG(format, args...)
42 #endif
43 
44 // --- cDvbSpuPalette---------------------------------------------------------
45 
46 void cDvbSpuPalette::setPalette(const uint32_t * pal)
47 {
48  for (int i = 0; i < 16; i++)
49  palette[i] = yuv2rgb(pal[i]);
50 }
51 
52 // --- cDvbSpuBitmap ---------------------------------------------------------
53 
54 #define setMin(a, b) if (a > b) a = b
55 #define setMax(a, b) if (a < b) a = b
56 
57 #define spuXres 720
58 #define spuYres 576
59 
60 #define revRect(r1, r2) { r1.x1 = r2.x2; r1.y1 = r2.y2; r1.x2 = r2.x1; r1.y2 = r2.y1; }
61 
63  uint8_t * fodd, uint8_t * eodd,
64  uint8_t * feven, uint8_t * eeven)
65 {
66  size.x1 = max(size.x1, 0);
67  size.y1 = max(size.y1, 0);
68  size.x2 = min(size.x2, spuXres - 1);
69  size.y2 = min(size.y2, spuYres - 1);
70 
71  bmpsize = size;
72  revRect(minsize[0], size);
73  revRect(minsize[1], size);
74  revRect(minsize[2], size);
75  revRect(minsize[3], size);
76 
77  int MemSize = spuXres * spuYres * sizeof(uint8_t);
78  bmp = new uint8_t[MemSize];
79 
80  if (bmp)
81  memset(bmp, 0, MemSize);
82  putFieldData(0, fodd, eodd);
83  putFieldData(1, feven, eeven);
84 }
85 
87 {
88  delete[]bmp;
89 }
90 
92  const cDvbSpuPalette & pal,
93  sDvbSpuRect & size) const
94 {
95  int h = size.height();
96  int w = size.width();
97 
98  if (size.y1 + h >= spuYres)
99  h = spuYres - size.y1 - 1;
100  if (size.x1 + w >= spuXres)
101  w = spuXres - size.x1 - 1;
102 
103  if (w & 0x03)
104  w += 4 - (w & 0x03);
105 
106  cBitmap *ret = new cBitmap(w, h, 2);
107 
108  // set the palette
109  for (int i = 0; i < 4; i++) {
110  uint32_t color =
111  pal.getColor(paldescr[i].index, paldescr[i].trans);
112  ret->SetColor(i, (tColor) color);
113  }
114 
115  // set the content
116  if (bmp) {
117  for (int yp = 0; yp < h; yp++) {
118  for (int xp = 0; xp < w; xp++) {
119  uint8_t idx = bmp[(size.y1 + yp) * spuXres + size.x1 + xp];
120  ret->SetIndex(xp, yp, idx);
121  }
122  }
123  }
124  return ret;
125 }
126 
127 // find the minimum non-transparent area
129  sDvbSpuRect & size) const
130 {
131  bool ret = false;
132  for (int i = 0; i < 4; i++) {
133  if (paldescr[i].trans != 0) {
134  if (!ret)
135  size = minsize[i];
136  else {
137  setMin(size.x1, minsize[i].x1);
138  setMin(size.y1, minsize[i].y1);
139  setMax(size.x2, minsize[i].x2);
140  setMax(size.y2, minsize[i].y2);
141  }
142  ret = true;
143  }
144  }
145  if (ret)
146  DEBUG("MinSize: (%d, %d) x (%d, %d)\n",
147  size.x1, size.y1, size.x2, size.y2);
148  if (size.x1 > size.x2 || size.y1 > size.y2)
149  return false;
150 
151  return ret;
152 }
153 
154 void cDvbSpuBitmap::putPixel(int xp, int yp, int len, uint8_t colorid)
155 {
156  if (bmp)
157  memset(bmp + spuXres * yp + xp, colorid, len);
158  setMin(minsize[colorid].x1, xp);
159  setMin(minsize[colorid].y1, yp);
160  setMax(minsize[colorid].x2, xp + len - 1);
161  setMax(minsize[colorid].y2, yp);
162 }
163 
164 static uint8_t getBits(uint8_t * &data, uint8_t & bitf)
165 {
166  uint8_t ret = *data;
167  if (bitf)
168  ret >>= 4;
169  else
170  data++;
171  bitf ^= 1;
172 
173  return (ret & 0xf);
174 }
175 
176 void cDvbSpuBitmap::putFieldData(int field, uint8_t * data, uint8_t * endp)
177 {
178  int xp = bmpsize.x1;
179  int yp = bmpsize.y1 + field;
180  uint8_t bitf = 1;
181 
182  while (data < endp) {
183  uint16_t vlc = getBits(data, bitf);
184  if (vlc < 0x0004) {
185  vlc = (vlc << 4) | getBits(data, bitf);
186  if (vlc < 0x0010) {
187  vlc = (vlc << 4) | getBits(data, bitf);
188  if (vlc < 0x0040) {
189  vlc = (vlc << 4) | getBits(data, bitf);
190  }
191  }
192  }
193 
194  uint8_t color = vlc & 0x03;
195  int len = vlc >> 2;
196 
197  // if len == 0 -> end sequence - fill to end of line
198  len = len ? len : bmpsize.x2 - xp + 1;
199  putPixel(xp, yp, len, color);
200  xp += len;
201 
202  if (xp > bmpsize.x2) {
203  // nextLine
204  if (!bitf)
205  data++;
206  bitf = 1;
207  xp = bmpsize.x1;
208  yp += 2;
209  if (yp > bmpsize.y2)
210  return;
211  }
212  }
213 }
214 
215 // --- cDvbSpuDecoder---------------------------------------------------------
216 
217 #define CMD_SPU_MENU 0x00
218 #define CMD_SPU_SHOW 0x01
219 #define CMD_SPU_HIDE 0x02
220 #define CMD_SPU_SET_PALETTE 0x03
221 #define CMD_SPU_SET_ALPHA 0x04
222 #define CMD_SPU_SET_SIZE 0x05
223 #define CMD_SPU_SET_PXD_OFFSET 0x06
224 #define CMD_SPU_CHG_COLCON 0x07
225 #define CMD_SPU_EOF 0xff
226 
227 #define spuU32(i) ((spu[i] << 8) + spu[i+1])
228 
230 {
231  clean = true;
233  spu = NULL;
234  osd = NULL;
235  spubmp = NULL;
236  allowedShow = false;
237 }
238 
240 {
241  delete spubmp;
242  delete spu;
243  delete osd;
244 }
245 
246 void cDvbSpuDecoder::processSPU(uint32_t pts, uint8_t * buf, bool AllowedShow)
247 {
248  setTime(pts);
249 
250  DEBUG("SPU pushData: pts: %d\n", pts);
251 
252  delete spubmp;
253  spubmp = NULL;
254  delete[]spu;
255  spu = buf;
256  spupts = pts;
257 
258  DCSQ_offset = cmdOffs();
259  prev_DCSQ_offset = 0;
260 
261  clean = true;
262  allowedShow = AllowedShow;
263 }
264 
266 {
267  scaleMode = ScaleMode;
268 }
269 
270 void cDvbSpuDecoder::setPalette(uint32_t * pal)
271 {
272  palette.setPalette(pal);
273 }
274 
275 void cDvbSpuDecoder::setHighlight(uint16_t sx, uint16_t sy,
276  uint16_t ex, uint16_t ey,
277  uint32_t palette)
278 {
279  aDvbSpuPalDescr pld;
280  for (int i = 0; i < 4; i++) {
281  pld[i].index = 0xf & (palette >> (16 + 4 * i));
282  pld[i].trans = 0xf & (palette >> (4 * i));
283  }
284 
285  bool ne = hlpsize.x1 != sx || hlpsize.y1 != sy ||
286  hlpsize.x2 != ex || hlpsize.y2 != ey ||
287  pld[0] != hlpDescr[0] || pld[1] != hlpDescr[1] ||
288  pld[2] != hlpDescr[2] || pld[3] != hlpDescr[3];
289 
290  if (ne) {
291  DEBUG("setHighlight: %d,%d x %d,%d\n", sx, sy, ex, ey);
292  hlpsize.x1 = sx;
293  hlpsize.y1 = sy;
294  hlpsize.x2 = ex;
295  hlpsize.y2 = ey;
296  memcpy(hlpDescr, pld, sizeof(aDvbSpuPalDescr));
297  highlight = true;
298  clean = false;
299  }
300 }
301 
303 {
304  clean &= !highlight;
305  highlight = false;
306  hlpsize.x1 = -1;
307  hlpsize.y1 = -1;
308  hlpsize.x2 = -1;
309  hlpsize.y2 = -1;
310 }
311 
313 {
315  if (fgbmp && bgbmp) {
316  size.x1 = min(fgsize.x1, bgsize.x1);
317  size.y1 = min(fgsize.y1, bgsize.y1);
318  size.x2 = max(fgsize.x2, bgsize.x2);
319  size.y2 = max(fgsize.y2, bgsize.y2);
320  }
321  else if (fgbmp) {
322  size.x1 = fgsize.x1;
323  size.y1 = fgsize.y1;
324  size.x2 = fgsize.x2;
325  size.y2 = fgsize.y2;
326  }
327  else if (bgbmp) {
328  size.x1 = bgsize.x1;
329  size.y1 = bgsize.y1;
330  size.x2 = bgsize.x2;
331  size.y2 = bgsize.y2;
332  }
333  else {
334  size.x1 = 0;
335  size.y1 = 0;
336  size.x2 = 0;
337  size.y2 = 0;
338  }
339  return size;
340 }
341 
343 {
344  int col = 1;
345  for (int i = 0; i < 4; i++) {
346  if (paldescr[i].trans != 0) {
347  col++;
348  }
349  }
350  return col > 2 ? 2 : 1;
351 }
352 
354 {
355  int fgbpp = 0;
356  int bgbpp = 0;
357  int ret;
358  if (fgbmp) {
359  fgbpp = spubmp->getMinBpp(hlpDescr);
360  }
361  if (bgbmp) {
362  bgbpp = spubmp->getMinBpp(palDescr);
363  }
364  ret = fgbpp + bgbpp;
365  if (ret > 2)
366  ret = 4;
367  return ret;
368 }
369 
370 
372 {
373  cMutexLock MutexLock(&mutex);
374  if (!spubmp) {
375  Hide();
376  return;
377  }
378  sDvbSpuRect bgsize;
379  cBitmap *fg = NULL;
380  cBitmap *bg = NULL;
381 
382  if (highlight)
384 
385  if (spubmp->getMinSize(palDescr, bgsize))
386  bg = spubmp->getBitmap(palDescr, palette, bgsize);
387 
388  if (!fg || !bg || !osd)
389  Hide();
390 
391  if (osd == NULL) {
392  restricted_osd = false;
393  osd = cOsdProvider::NewOsd(0, 0);
394 
395  tArea Area = { size.x1, size.y1, size.x2, size.y2, 4};
396  if (osd->CanHandleAreas(&Area, 1) != oeOk)
397  restricted_osd = true;
398  else
399  osd->SetAreas(&Area, 1);
400  }
401  if (restricted_osd) {
402  sDvbSpuRect hlsize;
403  bool setarea = false;
404  /* reduce fg area (only valid if there is no bg below) */
405  if (fg) {
406  spubmp->getMinSize(hlpDescr,hlsize);
407  /* clip to the highligh area */
408  setMax(hlsize.x1, hlpsize.x1);
409  setMax(hlsize.y1, hlpsize.y1);
410  setMin(hlsize.x2, hlpsize.x2);
411  setMin(hlsize.y2, hlpsize.y2);
412  if (hlsize.x1 > hlsize.x2 || hlsize.y1 > hlsize.y2) {
413  hlsize.x1 = hlsize.x2 = hlsize.y1 = hlsize.y2 = 0;
414  }
415  }
416  sDvbSpuRect areaSize = CalcAreaSize((fg && bg) ? hlpsize : hlsize, fg, bgsize, bg);
417 
418 #define DIV(a, b) (a/b)?:1
419  for (int d = 1; !setarea && d <= 2; d++) {
420 
421  /* first try old behaviour */
422  tArea Area = { areaSize.x1, areaSize.y1, areaSize.x2, areaSize.y2, DIV(CalcAreaBpp(fg, bg), d) };
423 
424  if ((Area.Width() & 7) != 0)
425  Area.x2 += 8 - (Area.Width() & 7);
426 
427  if (osd->CanHandleAreas(&Area, 1) == oeOk &&
428  osd->SetAreas(&Area, 1) == oeOk)
429  setarea = true;
430 
431  /* second try to split area if there is both area */
432  if (!setarea && fg && bg) {
433  tArea Area_Both [2] = {
434  {bgsize.x1, bgsize.y1, bgsize.x2, bgsize.y2, DIV(CalcAreaBpp(0, bg), d)},
436  };
437  if (!Area_Both[0].Intersects(Area_Both[1])) {
438  /* there is no intersection. We can reduce hl */
439  Area_Both[1].x1 = hlsize.x1;
440  Area_Both[1].y1 = hlsize.y1;
441  Area_Both[1].x2 = hlsize.x2;
442  Area_Both[1].y2 = hlsize.y2;
443 
444  if ((Area_Both[0].Width() & 7) != 0)
445  Area_Both[0].x2 += 8 - (Area_Both[0].Width() & 7);
446  if ((Area_Both[1].Width() & 7) != 0)
447  Area_Both[1].x2 += 8 - (Area_Both[1].Width() & 7);
448  if (osd->CanHandleAreas(Area_Both, 2) == oeOk &&
449  osd->SetAreas(Area_Both, 2) == oeOk)
450  setarea = true;
451  }
452  }
453  }
454  if (!setarea)
455  dsyslog("dvbspu: AreaSize (%d, %d) (%d, %d) Bpp %d", areaSize.x1, areaSize.y1, areaSize.x2, areaSize.y2, (fg && bg) ? 4 : 2 );
456  }
457 
458  /* we could draw use DrawPixel on osd */
459  if (bg || fg) {
460  if (bg)
461  osd->DrawBitmap(bgsize.x1, bgsize.y1, *bg);
462  if (fg)
463  osd->DrawBitmap(hlpsize.x1, hlpsize.y1, *fg);
464  delete fg;
465  delete bg;
466 
467  osd->Flush();
468  }
469 
470  clean = true;
471 }
472 
474 {
475  cMutexLock MutexLock(&mutex);
476  delete osd;
477  osd = NULL;
478 }
479 
481 {
482  Hide();
483 
484  delete spubmp;
485  spubmp = NULL;
486 
487  delete[]spu;
488  spu = NULL;
489 
490  clearHighlight();
491  clean = true;
492 }
493 
494 int cDvbSpuDecoder::setTime(uint32_t pts)
495 {
496  if (!spu)
497  return 0;
498 
499  if (!clean)
500  Draw();
501 
502  while (DCSQ_offset != prev_DCSQ_offset) { /* Display Control Sequences */
503  int i = DCSQ_offset;
504  state = spNONE;
505 
506  uint32_t exec_time = spupts + spuU32(i) * 1024;
507  if ((pts != 0) && (exec_time > pts))
508  return 0;
509  DEBUG("offs = %d, rel = %d, time = %d, pts = %d, diff = %d\n",
510  i, spuU32(i) * 1024, exec_time, pts, exec_time - pts);
511 
512  if (pts != 0) {
513  uint16_t feven = 0;
514  uint16_t fodd = 0;
515 
516  i += 2;
517 
519  DCSQ_offset = spuU32(i);
520  DEBUG("offs = %d, DCSQ = %d, prev_DCSQ = %d\n",
522  i += 2;
523 
524  while (spu[i] != CMD_SPU_EOF) { // Command Sequence
525  switch (spu[i]) {
526  case CMD_SPU_SHOW: // show subpicture
527  DEBUG("\tshow subpicture\n");
528  state = spSHOW;
529  i++;
530  break;
531 
532  case CMD_SPU_HIDE: // hide subpicture
533  DEBUG("\thide subpicture\n");
534  state = spHIDE;
535  i++;
536  break;
537 
538  case CMD_SPU_SET_PALETTE: // CLUT
539  palDescr[0].index = spu[i + 2] & 0xf;
540  palDescr[1].index = spu[i + 2] >> 4;
541  palDescr[2].index = spu[i + 1] & 0xf;
542  palDescr[3].index = spu[i + 1] >> 4;
543  i += 3;
544  break;
545 
546  case CMD_SPU_SET_ALPHA: // transparency palette
547  palDescr[0].trans = spu[i + 2] & 0xf;
548  palDescr[1].trans = spu[i + 2] >> 4;
549  palDescr[2].trans = spu[i + 1] & 0xf;
550  palDescr[3].trans = spu[i + 1] >> 4;
551  i += 3;
552  break;
553 
554  case CMD_SPU_SET_SIZE: // image coordinates
555  size.x1 = (spu[i + 1] << 4) | (spu[i + 2] >> 4);
556  size.x2 = ((spu[i + 2] & 0x0f) << 8) | spu[i + 3];
557 
558  size.y1 = (spu[i + 4] << 4) | (spu[i + 5] >> 4);
559  size.y2 = ((spu[i + 5] & 0x0f) << 8) | spu[i + 6];
560 
561  DEBUG("\t(%d, %d) x (%d, %d)\n",
562  size.x1, size.y1, size.x2, size.y2);
563  i += 7;
564  break;
565 
566  case CMD_SPU_SET_PXD_OFFSET: // image 1 / image 2 offsets
567  fodd = spuU32(i + 1);
568  feven = spuU32(i + 3);
569  DEBUG("\todd = %d even = %d\n", fodd, feven);
570  i += 5;
571  break;
572 
573  case CMD_SPU_CHG_COLCON: {
574  int size = spuU32(i + 1);
575  i += 1 + size;
576  }
577  break;
578 
579  case CMD_SPU_MENU:
580  DEBUG("\tspu menu\n");
581  state = spMENU;
582 
583  i++;
584  break;
585 
586  default:
587  esyslog("invalid sequence in control header (%.2x)",
588  spu[i]);
589  Empty();
590  return 0;
591  }
592  }
593  if (fodd != 0 && feven != 0) {
594  Hide();
595  delete spubmp;
596  spubmp = new cDvbSpuBitmap(size, spu + fodd, spu + feven,
597  spu + feven, spu + cmdOffs());
598  }
599  } else if (!clean)
600  state = spSHOW;
601 
602  if ((state == spSHOW && allowedShow) || state == spMENU)
603  Draw();
604 
605  if (state == spHIDE)
606  Hide();
607 
608  if (pts == 0)
609  return 0;
610  }
611 
612  return 1;
613 }