Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

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 1.3 2002/10/26 10:46:49 kls Exp $
00012  */
00013 
00014 #include <assert.h>
00015 #include <string.h>
00016 #include <inttypes.h>
00017 #include <math.h>
00018 
00019 #include "osd.h"
00020 #include "osdbase.h"
00021 #include "device.h"
00022 #include "dvbspu.h"
00023 
00024 /*
00025  * cDvbSpubitmap:
00026  *
00027  * this is a bitmap of the full screen and two palettes
00028  * the normal palette for the background and the highlight palette
00029  *
00030  * Inputs:
00031  *  - a SPU rle encoded image on creation, which will be decoded into
00032  *    the full screen indexed bitmap
00033  *  
00034  * Output:
00035  *  - a minimal sized cDvbSpuBitmap a given palette, the indexed bitmap
00036  *    will be scanned to get the smallest possible resulting bitmap considering
00037  *    transparencies
00038  */
00039 
00040 // #define SPUDEBUG
00041 
00042 #ifdef SPUDEBUG
00043 #define DEBUG(format, args...) printf (format, ## args)
00044 #else
00045 #define DEBUG(format, args...)
00046 #endif
00047 
00048 // --- cDvbSpuPalette----------------------------------
00049 
00050 void cDvbSpuPalette::setPalette(const uint32_t * pal)
00051 {
00052     for (int i = 0; i < 16; i++)
00053         palette[i] = yuv2rgb(pal[i]);
00054 }
00055 
00056 // --- cDvbSpuBitmap --------------------------------------------
00057 
00058 #define setMin(a, b) if (a > b) a = b
00059 #define setMax(a, b) if (a < b) a = b
00060 
00061 #define spuXres   720
00062 #define spuYres   576
00063 
00064 #define revRect(r1, r2) { r1.x1 = r2.x2; r1.y1 = r2.y2; r1.x2 = r2.x1; r1.y2 = r2.y1; }
00065 
00066 cDvbSpuBitmap::cDvbSpuBitmap(sDvbSpuRect size,
00067                              uint8_t * fodd, uint8_t * eodd,
00068                              uint8_t * feven, uint8_t * eeven)
00069 {
00070     if (size.x1 < 0 || size.y1 < 0 || size.x2 >= spuXres
00071         || size.y2 >= spuYres)
00072         throw;
00073 
00074     bmpsize = size;
00075     revRect(minsize[0], size);
00076     revRect(minsize[1], size);
00077     revRect(minsize[2], size);
00078     revRect(minsize[3], size);
00079 
00080     if (!(bmp = new uint8_t[spuXres * spuYres * sizeof(uint8_t)]))
00081         throw;
00082 
00083     memset(bmp, 0, spuXres * spuYres * sizeof(uint8_t));
00084     putFieldData(0, fodd, eodd);
00085     putFieldData(1, feven, eeven);
00086 }
00087 
00088 cDvbSpuBitmap::~cDvbSpuBitmap()
00089 {
00090     delete[]bmp;
00091 }
00092 
00093 cBitmap *cDvbSpuBitmap::getBitmap(const aDvbSpuPalDescr paldescr,
00094                                   const cDvbSpuPalette & pal,
00095                                   sDvbSpuRect & size) const
00096 {
00097     int h = size.height();
00098     int w = size.width();
00099 
00100     if (size.y1 + h >= spuYres)
00101         h = spuYres - size.y1 - 1;
00102     if (size.x1 + w >= spuXres)
00103         w = spuXres - size.x1 - 1;
00104 
00105     if (w & 0x03)
00106         w += 4 - (w & 0x03);
00107 
00108     cBitmap *ret = new cBitmap(w, h, 2, true);
00109 
00110     // set the palette
00111     for (int i = 0; i < 4; i++) {
00112         uint32_t color =
00113             pal.getColor(paldescr[i].index, paldescr[i].trans);
00114         ret->SetColor(i, (eDvbColor) color);
00115     }
00116 
00117     // set the content
00118     for (int yp = 0; yp < h; yp++) {
00119         for (int xp = 0; xp < w; xp++) {
00120             uint8_t idx = bmp[(size.y1 + yp) * spuXres + size.x1 + xp];
00121             ret->SetIndex(xp, yp, idx);
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 
00149     return ret;
00150 }
00151 
00152 void cDvbSpuBitmap::putPixel(int xp, int yp, int len, uint8_t colorid)
00153 {
00154     memset(bmp + spuXres * yp + xp, colorid, len);
00155     setMin(minsize[colorid].x1, xp);
00156     setMin(minsize[colorid].y1, yp);
00157     setMax(minsize[colorid].x2, xp + len - 1);
00158     setMax(minsize[colorid].y2, yp + len - 1);
00159 }
00160 
00161 static uint8_t getBits(uint8_t * &data, uint8_t & bitf)
00162 {
00163     uint8_t ret = *data;
00164     if (bitf)
00165         ret >>= 4;
00166     else
00167         data++;
00168     bitf ^= 1;
00169 
00170     return (ret & 0xf);
00171 }
00172 
00173 void cDvbSpuBitmap::putFieldData(int field, uint8_t * data, uint8_t * endp)
00174 {
00175     int xp = bmpsize.x1;
00176     int yp = bmpsize.y1 + field;
00177     uint8_t bitf = 1;
00178 
00179     while (data < endp) {
00180         uint16_t vlc = getBits(data, bitf);
00181         if (vlc < 0x0004) {
00182             vlc = (vlc << 4) | getBits(data, bitf);
00183             if (vlc < 0x0010) {
00184                 vlc = (vlc << 4) | getBits(data, bitf);
00185                 if (vlc < 0x0040) {
00186                     vlc = (vlc << 4) | getBits(data, bitf);
00187                 }
00188             }
00189         }
00190 
00191         uint8_t color = vlc & 0x03;
00192         int len = vlc >> 2;
00193 
00194         // if len == 0 -> end sequence - fill to end of line
00195         len = len ? len : bmpsize.x2 - xp + 1;
00196         putPixel(xp, yp, len, color);
00197         xp += len;
00198 
00199         if (xp > bmpsize.x2) {
00200             // nextLine
00201             if (!bitf)
00202                 data++;
00203             bitf = 1;
00204             xp = bmpsize.x1;
00205             yp += 2;
00206             if (yp > bmpsize.y2)
00207                 return;
00208         }
00209     }
00210 }
00211 
00212 // --- cDvbSpuDecoder-----------------------------
00213 
00214 #define CMD_SPU_MENU            0x00
00215 #define CMD_SPU_SHOW            0x01
00216 #define CMD_SPU_HIDE            0x02
00217 #define CMD_SPU_SET_PALETTE     0x03
00218 #define CMD_SPU_SET_ALPHA       0x04
00219 #define CMD_SPU_SET_SIZE        0x05
00220 #define CMD_SPU_SET_PXD_OFFSET  0x06
00221 #define CMD_SPU_EOF             0xff
00222 
00223 #define spuU32(i)  ((spu[i] << 8) + spu[i+1])
00224 
00225 cDvbSpuDecoder::cDvbSpuDecoder()
00226 {
00227     clean = true;
00228     scaleMode = eSpuNormal;
00229     spu = NULL;
00230     osd = NULL;
00231     spubmp = NULL;
00232 }
00233 
00234 cDvbSpuDecoder::~cDvbSpuDecoder()
00235 {
00236     delete spubmp;
00237     delete spu;
00238     delete osd;
00239 }
00240 
00241 void cDvbSpuDecoder::processSPU(uint32_t pts, uint8_t * buf)
00242 {
00243     setTime(pts);
00244 
00245     DEBUG("SPU pushData: pts: %d\n", pts);
00246 
00247     delete spubmp;
00248     spubmp = NULL;
00249     delete[]spu;
00250     spu = buf;
00251     spupts = pts;
00252 
00253     DCSQ_offset = cmdOffs();
00254     prev_DCSQ_offset = 0;
00255 
00256     clean = true;
00257 }
00258 
00259 void cDvbSpuDecoder::setScaleMode(cSpuDecoder::eScaleMode ScaleMode)
00260 {
00261     scaleMode = ScaleMode;
00262 }
00263 
00264 void cDvbSpuDecoder::setPalette(uint32_t * pal)
00265 {
00266     palette.setPalette(pal);
00267 }
00268 
00269 void cDvbSpuDecoder::setHighlight(uint16_t sx, uint16_t sy,
00270                                   uint16_t ex, uint16_t ey,
00271                                   uint32_t palette)
00272 {
00273     aDvbSpuPalDescr pld;
00274     for (int i = 0; i < 4; i++) {
00275         pld[i].index = 0xf & (palette >> (16 + 4 * i));
00276         pld[i].trans = 0xf & (palette >> (4 * i));
00277     }
00278 
00279     bool ne = hlpsize.x1 != sx || hlpsize.y1 != sy ||
00280         hlpsize.x2 != ex || hlpsize.y2 != ey ||
00281         pld[0] != hlpDescr[0] || pld[1] != hlpDescr[1] ||
00282         pld[2] != hlpDescr[2] || pld[3] != hlpDescr[3];
00283 
00284     if (ne) {
00285         DEBUG("setHighlight: %d,%d x %d,%d\n", sx, sy, ex, ey);
00286         hlpsize.x1 = sx;
00287         hlpsize.y1 = sy;
00288         hlpsize.x2 = ex;
00289         hlpsize.y2 = ey;
00290         memcpy(hlpDescr, pld, sizeof(aDvbSpuPalDescr));
00291         highlight = true;
00292         clean = false;
00293     }
00294 }
00295 
00296 void cDvbSpuDecoder::clearHighlight(void)
00297 {
00298     clean &= !highlight;
00299     highlight = false;
00300 }
00301 
00302 int cDvbSpuDecoder::ScaleYcoord(int value)
00303 {
00304     if (scaleMode == eSpuLetterBox)
00305         return lround((value * 3.0) / 4.0 + 72.0);
00306     else
00307         return value;
00308 }
00309 
00310 int cDvbSpuDecoder::ScaleYres(int value)
00311 {
00312     if (scaleMode == eSpuLetterBox)
00313         return lround((value * 3.0) / 4.0);
00314     else
00315         return value;
00316 }
00317 
00318 void cDvbSpuDecoder::DrawBmp(sDvbSpuRect & size, cBitmap * bmp)
00319 {
00320     osd->Create(size.x1, size.y1, size.width(), size.height(), 2, false);
00321     osd->SetBitmap(size.x1, size.y1, *bmp);
00322     delete bmp;
00323 }
00324 
00325 void cDvbSpuDecoder::Draw(void)
00326 {
00327     Hide();
00328 
00329     if (!spubmp)
00330         return;
00331 
00332     cBitmap *fg = NULL;
00333     cBitmap *bg = NULL;
00334     sDvbSpuRect bgsize;
00335     sDvbSpuRect hlsize;
00336 
00337     hlsize.x1 = hlpsize.x1;
00338     hlsize.y1 = ScaleYcoord(hlpsize.y1);
00339     hlsize.x2 = hlpsize.x2;
00340     hlsize.y2 = ScaleYcoord(hlpsize.y2);
00341 
00342     if (highlight)
00343         fg = spubmp->getBitmap(hlpDescr, palette, hlsize);
00344 
00345     if (spubmp->getMinSize(palDescr, bgsize)) {
00346         bg = spubmp->getBitmap(palDescr, palette, bgsize);
00347         if (scaleMode == eSpuLetterBox) {
00348             // the coordinates have to be modified for letterbox
00349             int y1 = ScaleYres(bgsize.y1) + bgsize.height();
00350             bgsize.y2 = y1 + bgsize.height();
00351             bgsize.y1 = y1;
00352         }
00353     }
00354 
00355     if (bg || fg) {
00356         if (osd == NULL)
00357             if ((osd = cOsd::OpenRaw(0, 0)) == NULL) {
00358                 dsyslog("OpenRaw failed\n");
00359                 return;
00360             }
00361 
00362         if (fg)
00363             DrawBmp(hlsize, fg);
00364 
00365         if (bg)
00366             DrawBmp(bgsize, bg);
00367 
00368         osd->Flush();
00369     }
00370 
00371     clean = true;
00372 }
00373 
00374 void cDvbSpuDecoder::Hide(void)
00375 {
00376     delete osd;
00377     osd = NULL;
00378 }
00379 
00380 void cDvbSpuDecoder::Empty(void)
00381 {
00382     Hide();
00383 
00384     delete spubmp;
00385     spubmp = NULL;
00386 
00387     delete[]spu;
00388     spu = NULL;
00389 
00390     clearHighlight();
00391     clean = true;
00392 }
00393 
00394 int cDvbSpuDecoder::setTime(uint32_t pts)
00395 {
00396     if (!spu)
00397         return 0;
00398 
00399     if (spu && !clean)
00400         Draw();
00401 
00402     while (DCSQ_offset != prev_DCSQ_offset) {   /* Display Control Sequences */
00403         int i = DCSQ_offset;
00404         state = spNONE;
00405 
00406         uint32_t exec_time = spupts + spuU32(i) * 1024;
00407         if ((pts != 0) && (exec_time > pts))
00408             return 0;
00409         DEBUG("offs = %d, rel = %d, time = %d, pts = %d, diff = %d\n",
00410               i, spuU32(i) * 1024, exec_time, pts, exec_time - pts);
00411 
00412         if (pts != 0) {
00413             uint16_t feven = 0;
00414             uint16_t fodd = 0;
00415 
00416             i += 2;
00417 
00418             prev_DCSQ_offset = DCSQ_offset;
00419             DCSQ_offset = spuU32(i);
00420             DEBUG("offs = %d, DCSQ = %d, prev_DCSQ = %d\n", 
00421                            i, DCSQ_offset, prev_DCSQ_offset);
00422             i += 2;
00423 
00424             while (spu[i] != CMD_SPU_EOF) {     // Command Sequence
00425                 switch (spu[i]) {
00426                 case CMD_SPU_SHOW:     // show subpicture
00427                     DEBUG("\tshow subpicture\n");
00428                     state = spSHOW;
00429                     i++;
00430                     break;
00431 
00432                 case CMD_SPU_HIDE:     // hide subpicture
00433                     DEBUG("\thide subpicture\n");
00434                     state = spHIDE;
00435                     i++;
00436                     break;
00437 
00438                 case CMD_SPU_SET_PALETTE:      // CLUT
00439                     palDescr[0].index = spu[i + 2] & 0xf;
00440                     palDescr[1].index = spu[i + 2] >> 4;
00441                     palDescr[2].index = spu[i + 1] & 0xf;
00442                     palDescr[3].index = spu[i + 1] >> 4;
00443                     i += 3;
00444                     break;
00445 
00446                 case CMD_SPU_SET_ALPHA:        // transparency palette
00447                     palDescr[0].trans = spu[i + 2] & 0xf;
00448                     palDescr[1].trans = spu[i + 2] >> 4;
00449                     palDescr[2].trans = spu[i + 1] & 0xf;
00450                     palDescr[3].trans = spu[i + 1] >> 4;
00451                     i += 3;
00452                     break;
00453 
00454                 case CMD_SPU_SET_SIZE: // image coordinates
00455                     size.x1 = (spu[i + 1] << 4) | (spu[i + 2] >> 4);
00456                     size.x2 = ((spu[i + 2] & 0x0f) << 8) | spu[i + 3];
00457 
00458                     size.y1 = (spu[i + 4] << 4) | (spu[i + 5] >> 4);
00459                     size.y2 = ((spu[i + 5] & 0x0f) << 8) | spu[i + 6];
00460 
00461                     DEBUG("\t(%d, %d) x (%d, %d)\n",
00462                           size.x1, size.y1, size.x2, size.y2);
00463                     i += 7;
00464                     break;
00465 
00466                 case CMD_SPU_SET_PXD_OFFSET:   // image 1 / image 2 offsets
00467                     fodd = spuU32(i + 1);
00468                     feven = spuU32(i + 3);
00469                     DEBUG("\todd = %d even = %d\n", fodd, feven);
00470                     i += 5;
00471                     break;
00472 
00473                 case CMD_SPU_MENU:
00474                     DEBUG("\tspu menu\n");
00475                     state = spMENU;
00476 
00477                     i++;
00478                     break;
00479 
00480                 default:
00481                     esyslog("invalid sequence in control header (%.2x)\n",
00482                             spu[i]);
00483                     assert(0);
00484                     i++;
00485                     break;
00486                 }
00487             }
00488             if (fodd != 0 && feven != 0) {
00489                 delete spubmp;
00490                 spubmp = new cDvbSpuBitmap(size, spu + fodd, spu + feven,
00491                                            spu + feven, spu + cmdOffs());
00492             }
00493         } else if (!clean)
00494             state = spSHOW;
00495 
00496         if (state == spSHOW || state == spMENU)
00497             Draw();
00498 
00499         if (state == spHIDE)
00500             Hide();
00501 
00502         if (pts == 0)
00503             return 0;
00504     }
00505 
00506     return 1;
00507 }

Generated on Wed Feb 5 23:30:08 2003 for VDR by doxygen1.3-rc2