vdr  1.7.27
dvbspu.c
Go to the documentation of this file.
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 }