vdr
1.7.27
|
00001 /* 00002 * SPU decoder for DVB devices 00003 * 00004 * Copyright (C) 2001.2002 Andreas Schultz <aschultz@warp10.net> 00005 * 00006 * This code is distributed under the terms and conditions of the 00007 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. 00008 * 00009 * parts of this file are derived from the OMS program. 00010 * 00011 * $Id: dvbspu.c 2.9 2011/12/10 14:39:19 kls Exp $ 00012 */ 00013 00014 #include "dvbspu.h" 00015 #include <assert.h> 00016 #include <string.h> 00017 #include <inttypes.h> 00018 #include <math.h> 00019 00020 /* 00021 * cDvbSpubitmap: 00022 * 00023 * this is a bitmap of the full screen and two palettes 00024 * the normal palette for the background and the highlight palette 00025 * 00026 * Inputs: 00027 * - a SPU rle encoded image on creation, which will be decoded into 00028 * the full screen indexed bitmap 00029 * 00030 * Output: 00031 * - a minimal sized cDvbSpuBitmap a given palette, the indexed bitmap 00032 * will be scanned to get the smallest possible resulting bitmap considering 00033 * transparencies 00034 */ 00035 00036 // #define SPUDEBUG 00037 00038 #ifdef SPUDEBUG 00039 #define DEBUG(format, args...) printf (format, ## args) 00040 #else 00041 #define DEBUG(format, args...) 00042 #endif 00043 00044 // --- cDvbSpuPalette--------------------------------------------------------- 00045 00046 void cDvbSpuPalette::setPalette(const uint32_t * pal) 00047 { 00048 for (int i = 0; i < 16; i++) 00049 palette[i] = yuv2rgb(pal[i]); 00050 } 00051 00052 // --- cDvbSpuBitmap --------------------------------------------------------- 00053 00054 #define setMin(a, b) if (a > b) a = b 00055 #define setMax(a, b) if (a < b) a = b 00056 00057 #define spuXres 720 00058 #define spuYres 576 00059 00060 #define revRect(r1, r2) { r1.x1 = r2.x2; r1.y1 = r2.y2; r1.x2 = r2.x1; r1.y2 = r2.y1; } 00061 00062 cDvbSpuBitmap::cDvbSpuBitmap(sDvbSpuRect size, 00063 uint8_t * fodd, uint8_t * eodd, 00064 uint8_t * feven, uint8_t * eeven) 00065 { 00066 size.x1 = max(size.x1, 0); 00067 size.y1 = max(size.y1, 0); 00068 size.x2 = min(size.x2, spuXres - 1); 00069 size.y2 = min(size.y2, spuYres - 1); 00070 00071 bmpsize = size; 00072 revRect(minsize[0], size); 00073 revRect(minsize[1], size); 00074 revRect(minsize[2], size); 00075 revRect(minsize[3], size); 00076 00077 int MemSize = spuXres * spuYres * sizeof(uint8_t); 00078 bmp = new uint8_t[MemSize]; 00079 00080 if (bmp) 00081 memset(bmp, 0, MemSize); 00082 putFieldData(0, fodd, eodd); 00083 putFieldData(1, feven, eeven); 00084 } 00085 00086 cDvbSpuBitmap::~cDvbSpuBitmap() 00087 { 00088 delete[]bmp; 00089 } 00090 00091 cBitmap *cDvbSpuBitmap::getBitmap(const aDvbSpuPalDescr paldescr, 00092 const cDvbSpuPalette & pal, 00093 sDvbSpuRect & size) const 00094 { 00095 int h = size.height(); 00096 int w = size.width(); 00097 00098 if (size.y1 + h >= spuYres) 00099 h = spuYres - size.y1 - 1; 00100 if (size.x1 + w >= spuXres) 00101 w = spuXres - size.x1 - 1; 00102 00103 if (w & 0x03) 00104 w += 4 - (w & 0x03); 00105 00106 cBitmap *ret = new cBitmap(w, h, 2); 00107 00108 // set the palette 00109 for (int i = 0; i < 4; i++) { 00110 uint32_t color = 00111 pal.getColor(paldescr[i].index, paldescr[i].trans); 00112 ret->SetColor(i, (tColor) color); 00113 } 00114 00115 // set the content 00116 if (bmp) { 00117 for (int yp = 0; yp < h; yp++) { 00118 for (int xp = 0; xp < w; xp++) { 00119 uint8_t idx = bmp[(size.y1 + yp) * spuXres + size.x1 + xp]; 00120 ret->SetIndex(xp, yp, idx); 00121 } 00122 } 00123 } 00124 return ret; 00125 } 00126 00127 // find the minimum non-transparent area 00128 bool cDvbSpuBitmap::getMinSize(const aDvbSpuPalDescr paldescr, 00129 sDvbSpuRect & size) const 00130 { 00131 bool ret = false; 00132 for (int i = 0; i < 4; i++) { 00133 if (paldescr[i].trans != 0) { 00134 if (!ret) 00135 size = minsize[i]; 00136 else { 00137 setMin(size.x1, minsize[i].x1); 00138 setMin(size.y1, minsize[i].y1); 00139 setMax(size.x2, minsize[i].x2); 00140 setMax(size.y2, minsize[i].y2); 00141 } 00142 ret = true; 00143 } 00144 } 00145 if (ret) 00146 DEBUG("MinSize: (%d, %d) x (%d, %d)\n", 00147 size.x1, size.y1, size.x2, size.y2); 00148 if (size.x1 > size.x2 || size.y1 > size.y2) 00149 return false; 00150 00151 return ret; 00152 } 00153 00154 void cDvbSpuBitmap::putPixel(int xp, int yp, int len, uint8_t colorid) 00155 { 00156 if (bmp) 00157 memset(bmp + spuXres * yp + xp, colorid, len); 00158 setMin(minsize[colorid].x1, xp); 00159 setMin(minsize[colorid].y1, yp); 00160 setMax(minsize[colorid].x2, xp + len - 1); 00161 setMax(minsize[colorid].y2, yp); 00162 } 00163 00164 static uint8_t getBits(uint8_t * &data, uint8_t & bitf) 00165 { 00166 uint8_t ret = *data; 00167 if (bitf) 00168 ret >>= 4; 00169 else 00170 data++; 00171 bitf ^= 1; 00172 00173 return (ret & 0xf); 00174 } 00175 00176 void cDvbSpuBitmap::putFieldData(int field, uint8_t * data, uint8_t * endp) 00177 { 00178 int xp = bmpsize.x1; 00179 int yp = bmpsize.y1 + field; 00180 uint8_t bitf = 1; 00181 00182 while (data < endp) { 00183 uint16_t vlc = getBits(data, bitf); 00184 if (vlc < 0x0004) { 00185 vlc = (vlc << 4) | getBits(data, bitf); 00186 if (vlc < 0x0010) { 00187 vlc = (vlc << 4) | getBits(data, bitf); 00188 if (vlc < 0x0040) { 00189 vlc = (vlc << 4) | getBits(data, bitf); 00190 } 00191 } 00192 } 00193 00194 uint8_t color = vlc & 0x03; 00195 int len = vlc >> 2; 00196 00197 // if len == 0 -> end sequence - fill to end of line 00198 len = len ? len : bmpsize.x2 - xp + 1; 00199 putPixel(xp, yp, len, color); 00200 xp += len; 00201 00202 if (xp > bmpsize.x2) { 00203 // nextLine 00204 if (!bitf) 00205 data++; 00206 bitf = 1; 00207 xp = bmpsize.x1; 00208 yp += 2; 00209 if (yp > bmpsize.y2) 00210 return; 00211 } 00212 } 00213 } 00214 00215 // --- cDvbSpuDecoder--------------------------------------------------------- 00216 00217 #define CMD_SPU_MENU 0x00 00218 #define CMD_SPU_SHOW 0x01 00219 #define CMD_SPU_HIDE 0x02 00220 #define CMD_SPU_SET_PALETTE 0x03 00221 #define CMD_SPU_SET_ALPHA 0x04 00222 #define CMD_SPU_SET_SIZE 0x05 00223 #define CMD_SPU_SET_PXD_OFFSET 0x06 00224 #define CMD_SPU_CHG_COLCON 0x07 00225 #define CMD_SPU_EOF 0xff 00226 00227 #define spuU32(i) ((spu[i] << 8) + spu[i+1]) 00228 00229 cDvbSpuDecoder::cDvbSpuDecoder() 00230 { 00231 clean = true; 00232 scaleMode = eSpuNormal; 00233 spu = NULL; 00234 osd = NULL; 00235 spubmp = NULL; 00236 allowedShow = false; 00237 } 00238 00239 cDvbSpuDecoder::~cDvbSpuDecoder() 00240 { 00241 delete spubmp; 00242 delete spu; 00243 delete osd; 00244 } 00245 00246 void cDvbSpuDecoder::processSPU(uint32_t pts, uint8_t * buf, bool AllowedShow) 00247 { 00248 setTime(pts); 00249 00250 DEBUG("SPU pushData: pts: %d\n", pts); 00251 00252 delete spubmp; 00253 spubmp = NULL; 00254 delete[]spu; 00255 spu = buf; 00256 spupts = pts; 00257 00258 DCSQ_offset = cmdOffs(); 00259 prev_DCSQ_offset = 0; 00260 00261 clean = true; 00262 allowedShow = AllowedShow; 00263 } 00264 00265 void cDvbSpuDecoder::setScaleMode(cSpuDecoder::eScaleMode ScaleMode) 00266 { 00267 scaleMode = ScaleMode; 00268 } 00269 00270 void cDvbSpuDecoder::setPalette(uint32_t * pal) 00271 { 00272 palette.setPalette(pal); 00273 } 00274 00275 void cDvbSpuDecoder::setHighlight(uint16_t sx, uint16_t sy, 00276 uint16_t ex, uint16_t ey, 00277 uint32_t palette) 00278 { 00279 aDvbSpuPalDescr pld; 00280 for (int i = 0; i < 4; i++) { 00281 pld[i].index = 0xf & (palette >> (16 + 4 * i)); 00282 pld[i].trans = 0xf & (palette >> (4 * i)); 00283 } 00284 00285 bool ne = hlpsize.x1 != sx || hlpsize.y1 != sy || 00286 hlpsize.x2 != ex || hlpsize.y2 != ey || 00287 pld[0] != hlpDescr[0] || pld[1] != hlpDescr[1] || 00288 pld[2] != hlpDescr[2] || pld[3] != hlpDescr[3]; 00289 00290 if (ne) { 00291 DEBUG("setHighlight: %d,%d x %d,%d\n", sx, sy, ex, ey); 00292 hlpsize.x1 = sx; 00293 hlpsize.y1 = sy; 00294 hlpsize.x2 = ex; 00295 hlpsize.y2 = ey; 00296 memcpy(hlpDescr, pld, sizeof(aDvbSpuPalDescr)); 00297 highlight = true; 00298 clean = false; 00299 } 00300 } 00301 00302 void cDvbSpuDecoder::clearHighlight(void) 00303 { 00304 clean &= !highlight; 00305 highlight = false; 00306 hlpsize.x1 = -1; 00307 hlpsize.y1 = -1; 00308 hlpsize.x2 = -1; 00309 hlpsize.y2 = -1; 00310 } 00311 00312 sDvbSpuRect cDvbSpuDecoder::CalcAreaSize(sDvbSpuRect fgsize, cBitmap *fgbmp, sDvbSpuRect bgsize, cBitmap *bgbmp) 00313 { 00314 sDvbSpuRect size; 00315 if (fgbmp && bgbmp) { 00316 size.x1 = min(fgsize.x1, bgsize.x1); 00317 size.y1 = min(fgsize.y1, bgsize.y1); 00318 size.x2 = max(fgsize.x2, bgsize.x2); 00319 size.y2 = max(fgsize.y2, bgsize.y2); 00320 } 00321 else if (fgbmp) { 00322 size.x1 = fgsize.x1; 00323 size.y1 = fgsize.y1; 00324 size.x2 = fgsize.x2; 00325 size.y2 = fgsize.y2; 00326 } 00327 else if (bgbmp) { 00328 size.x1 = bgsize.x1; 00329 size.y1 = bgsize.y1; 00330 size.x2 = bgsize.x2; 00331 size.y2 = bgsize.y2; 00332 } 00333 else { 00334 size.x1 = 0; 00335 size.y1 = 0; 00336 size.x2 = 0; 00337 size.y2 = 0; 00338 } 00339 return size; 00340 } 00341 00342 int cDvbSpuBitmap::getMinBpp(const aDvbSpuPalDescr paldescr) 00343 { 00344 int col = 1; 00345 for (int i = 0; i < 4; i++) { 00346 if (paldescr[i].trans != 0) { 00347 col++; 00348 } 00349 } 00350 return col > 2 ? 2 : 1; 00351 } 00352 00353 int cDvbSpuDecoder::CalcAreaBpp(cBitmap *fgbmp, cBitmap *bgbmp) 00354 { 00355 int fgbpp = 0; 00356 int bgbpp = 0; 00357 int ret; 00358 if (fgbmp) { 00359 fgbpp = spubmp->getMinBpp(hlpDescr); 00360 } 00361 if (bgbmp) { 00362 bgbpp = spubmp->getMinBpp(palDescr); 00363 } 00364 ret = fgbpp + bgbpp; 00365 if (ret > 2) 00366 ret = 4; 00367 return ret; 00368 } 00369 00370 00371 void cDvbSpuDecoder::Draw(void) 00372 { 00373 cMutexLock MutexLock(&mutex); 00374 if (!spubmp) { 00375 Hide(); 00376 return; 00377 } 00378 sDvbSpuRect bgsize; 00379 cBitmap *fg = NULL; 00380 cBitmap *bg = NULL; 00381 00382 if (highlight) 00383 fg = spubmp->getBitmap(hlpDescr, palette, hlpsize); 00384 00385 if (spubmp->getMinSize(palDescr, bgsize)) 00386 bg = spubmp->getBitmap(palDescr, palette, bgsize); 00387 00388 if (!fg || !bg || !osd) 00389 Hide(); 00390 00391 if (osd == NULL) { 00392 restricted_osd = false; 00393 osd = cOsdProvider::NewOsd(0, 0); 00394 00395 tArea Area = { size.x1, size.y1, size.x2, size.y2, 4}; 00396 if (osd->CanHandleAreas(&Area, 1) != oeOk) 00397 restricted_osd = true; 00398 else 00399 osd->SetAreas(&Area, 1); 00400 } 00401 if (restricted_osd) { 00402 sDvbSpuRect hlsize; 00403 bool setarea = false; 00404 /* reduce fg area (only valid if there is no bg below) */ 00405 if (fg) { 00406 spubmp->getMinSize(hlpDescr,hlsize); 00407 /* clip to the highligh area */ 00408 setMax(hlsize.x1, hlpsize.x1); 00409 setMax(hlsize.y1, hlpsize.y1); 00410 setMin(hlsize.x2, hlpsize.x2); 00411 setMin(hlsize.y2, hlpsize.y2); 00412 if (hlsize.x1 > hlsize.x2 || hlsize.y1 > hlsize.y2) { 00413 hlsize.x1 = hlsize.x2 = hlsize.y1 = hlsize.y2 = 0; 00414 } 00415 } 00416 sDvbSpuRect areaSize = CalcAreaSize((fg && bg) ? hlpsize : hlsize, fg, bgsize, bg); 00417 00418 #define DIV(a, b) (a/b)?:1 00419 for (int d = 1; !setarea && d <= 2; d++) { 00420 00421 /* first try old behaviour */ 00422 tArea Area = { areaSize.x1, areaSize.y1, areaSize.x2, areaSize.y2, DIV(CalcAreaBpp(fg, bg), d) }; 00423 00424 if ((Area.Width() & 7) != 0) 00425 Area.x2 += 8 - (Area.Width() & 7); 00426 00427 if (osd->CanHandleAreas(&Area, 1) == oeOk && 00428 osd->SetAreas(&Area, 1) == oeOk) 00429 setarea = true; 00430 00431 /* second try to split area if there is both area */ 00432 if (!setarea && fg && bg) { 00433 tArea Area_Both [2] = { 00434 {bgsize.x1, bgsize.y1, bgsize.x2, bgsize.y2, DIV(CalcAreaBpp(0, bg), d)}, 00435 {hlpsize.x1, hlpsize.y1, hlpsize.x2, hlpsize.y2, DIV(CalcAreaBpp(fg, 0), d)} 00436 }; 00437 if (!Area_Both[0].Intersects(Area_Both[1])) { 00438 /* there is no intersection. We can reduce hl */ 00439 Area_Both[1].x1 = hlsize.x1; 00440 Area_Both[1].y1 = hlsize.y1; 00441 Area_Both[1].x2 = hlsize.x2; 00442 Area_Both[1].y2 = hlsize.y2; 00443 00444 if ((Area_Both[0].Width() & 7) != 0) 00445 Area_Both[0].x2 += 8 - (Area_Both[0].Width() & 7); 00446 if ((Area_Both[1].Width() & 7) != 0) 00447 Area_Both[1].x2 += 8 - (Area_Both[1].Width() & 7); 00448 if (osd->CanHandleAreas(Area_Both, 2) == oeOk && 00449 osd->SetAreas(Area_Both, 2) == oeOk) 00450 setarea = true; 00451 } 00452 } 00453 } 00454 if (!setarea) 00455 dsyslog("dvbspu: AreaSize (%d, %d) (%d, %d) Bpp %d", areaSize.x1, areaSize.y1, areaSize.x2, areaSize.y2, (fg && bg) ? 4 : 2 ); 00456 } 00457 00458 /* we could draw use DrawPixel on osd */ 00459 if (bg || fg) { 00460 if (bg) 00461 osd->DrawBitmap(bgsize.x1, bgsize.y1, *bg); 00462 if (fg) 00463 osd->DrawBitmap(hlpsize.x1, hlpsize.y1, *fg); 00464 delete fg; 00465 delete bg; 00466 00467 osd->Flush(); 00468 } 00469 00470 clean = true; 00471 } 00472 00473 void cDvbSpuDecoder::Hide(void) 00474 { 00475 cMutexLock MutexLock(&mutex); 00476 delete osd; 00477 osd = NULL; 00478 } 00479 00480 void cDvbSpuDecoder::Empty(void) 00481 { 00482 Hide(); 00483 00484 delete spubmp; 00485 spubmp = NULL; 00486 00487 delete[]spu; 00488 spu = NULL; 00489 00490 clearHighlight(); 00491 clean = true; 00492 } 00493 00494 int cDvbSpuDecoder::setTime(uint32_t pts) 00495 { 00496 if (!spu) 00497 return 0; 00498 00499 if (!clean) 00500 Draw(); 00501 00502 while (DCSQ_offset != prev_DCSQ_offset) { /* Display Control Sequences */ 00503 int i = DCSQ_offset; 00504 state = spNONE; 00505 00506 uint32_t exec_time = spupts + spuU32(i) * 1024; 00507 if ((pts != 0) && (exec_time > pts)) 00508 return 0; 00509 DEBUG("offs = %d, rel = %d, time = %d, pts = %d, diff = %d\n", 00510 i, spuU32(i) * 1024, exec_time, pts, exec_time - pts); 00511 00512 if (pts != 0) { 00513 uint16_t feven = 0; 00514 uint16_t fodd = 0; 00515 00516 i += 2; 00517 00518 prev_DCSQ_offset = DCSQ_offset; 00519 DCSQ_offset = spuU32(i); 00520 DEBUG("offs = %d, DCSQ = %d, prev_DCSQ = %d\n", 00521 i, DCSQ_offset, prev_DCSQ_offset); 00522 i += 2; 00523 00524 while (spu[i] != CMD_SPU_EOF) { // Command Sequence 00525 switch (spu[i]) { 00526 case CMD_SPU_SHOW: // show subpicture 00527 DEBUG("\tshow subpicture\n"); 00528 state = spSHOW; 00529 i++; 00530 break; 00531 00532 case CMD_SPU_HIDE: // hide subpicture 00533 DEBUG("\thide subpicture\n"); 00534 state = spHIDE; 00535 i++; 00536 break; 00537 00538 case CMD_SPU_SET_PALETTE: // CLUT 00539 palDescr[0].index = spu[i + 2] & 0xf; 00540 palDescr[1].index = spu[i + 2] >> 4; 00541 palDescr[2].index = spu[i + 1] & 0xf; 00542 palDescr[3].index = spu[i + 1] >> 4; 00543 i += 3; 00544 break; 00545 00546 case CMD_SPU_SET_ALPHA: // transparency palette 00547 palDescr[0].trans = spu[i + 2] & 0xf; 00548 palDescr[1].trans = spu[i + 2] >> 4; 00549 palDescr[2].trans = spu[i + 1] & 0xf; 00550 palDescr[3].trans = spu[i + 1] >> 4; 00551 i += 3; 00552 break; 00553 00554 case CMD_SPU_SET_SIZE: // image coordinates 00555 size.x1 = (spu[i + 1] << 4) | (spu[i + 2] >> 4); 00556 size.x2 = ((spu[i + 2] & 0x0f) << 8) | spu[i + 3]; 00557 00558 size.y1 = (spu[i + 4] << 4) | (spu[i + 5] >> 4); 00559 size.y2 = ((spu[i + 5] & 0x0f) << 8) | spu[i + 6]; 00560 00561 DEBUG("\t(%d, %d) x (%d, %d)\n", 00562 size.x1, size.y1, size.x2, size.y2); 00563 i += 7; 00564 break; 00565 00566 case CMD_SPU_SET_PXD_OFFSET: // image 1 / image 2 offsets 00567 fodd = spuU32(i + 1); 00568 feven = spuU32(i + 3); 00569 DEBUG("\todd = %d even = %d\n", fodd, feven); 00570 i += 5; 00571 break; 00572 00573 case CMD_SPU_CHG_COLCON: { 00574 int size = spuU32(i + 1); 00575 i += 1 + size; 00576 } 00577 break; 00578 00579 case CMD_SPU_MENU: 00580 DEBUG("\tspu menu\n"); 00581 state = spMENU; 00582 00583 i++; 00584 break; 00585 00586 default: 00587 esyslog("invalid sequence in control header (%.2x)", 00588 spu[i]); 00589 Empty(); 00590 return 0; 00591 } 00592 } 00593 if (fodd != 0 && feven != 0) { 00594 Hide(); 00595 delete spubmp; 00596 spubmp = new cDvbSpuBitmap(size, spu + fodd, spu + feven, 00597 spu + feven, spu + cmdOffs()); 00598 } 00599 } else if (!clean) 00600 state = spSHOW; 00601 00602 if ((state == spSHOW && allowedShow) || state == spMENU) 00603 Draw(); 00604 00605 if (state == spHIDE) 00606 Hide(); 00607 00608 if (pts == 0) 00609 return 0; 00610 } 00611 00612 return 1; 00613 }