vdr
1.7.27
|
00001 /* 00002 * dvbsdffosd.c: Implementation of the DVB SD Full Featured On Screen Display 00003 * 00004 * See the README file for copyright information and how to reach the author. 00005 * 00006 * $Id: dvbsdffosd.c 2.3 2011/04/17 12:55:09 kls Exp $ 00007 */ 00008 00009 #include "dvbsdffosd.h" 00010 #include <linux/dvb/osd.h> 00011 #include <signal.h> 00012 #include <sys/ioctl.h> 00013 #include <sys/unistd.h> 00014 #include <vdr/tools.h> 00015 00016 // --- cDvbSdFfOsd ----------------------------------------------------------- 00017 00018 #define MAXNUMWINDOWS 7 // OSD windows are counted 1...7 00019 #define MAXOSDMEMORY 92000 // number of bytes available to the OSD (for unmodified DVB cards) 00020 00021 class cDvbSdFfOsd : public cOsd { 00022 private: 00023 int osdDev; 00024 int osdMem; 00025 bool shown; 00026 void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); 00027 protected: 00028 virtual void SetActive(bool On); 00029 public: 00030 cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level); 00031 virtual ~cDvbSdFfOsd(); 00032 virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); 00033 virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); 00034 virtual void Flush(void); 00035 }; 00036 00037 cDvbSdFfOsd::cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level) 00038 :cOsd(Left, Top, Level) 00039 { 00040 osdDev = OsdDev; 00041 shown = false; 00042 if (osdDev < 0) 00043 esyslog("ERROR: invalid OSD device handle (%d)!", osdDev); 00044 else { 00045 osdMem = MAXOSDMEMORY; 00046 #ifdef OSD_CAP_MEMSIZE 00047 // modified DVB cards may have more OSD memory: 00048 osd_cap_t cap; 00049 cap.cmd = OSD_CAP_MEMSIZE; 00050 if (ioctl(osdDev, OSD_GET_CAPABILITY, &cap) == 0) 00051 osdMem = cap.val; 00052 #endif 00053 } 00054 } 00055 00056 cDvbSdFfOsd::~cDvbSdFfOsd() 00057 { 00058 SetActive(false); 00059 } 00060 00061 void cDvbSdFfOsd::SetActive(bool On) 00062 { 00063 if (On != Active()) { 00064 cOsd::SetActive(On); 00065 if (On) { 00066 // must clear all windows here to avoid flashing effects - doesn't work if done 00067 // in Flush() only for the windows that are actually used... 00068 for (int i = 0; i < MAXNUMWINDOWS; i++) { 00069 Cmd(OSD_SetWindow, 0, i + 1); 00070 Cmd(OSD_Clear); 00071 } 00072 if (GetBitmap(0)) // only flush here if there are already bitmaps 00073 Flush(); 00074 } 00075 else if (shown) { 00076 for (int i = 0; GetBitmap(i); i++) { 00077 Cmd(OSD_SetWindow, 0, i + 1); 00078 Cmd(OSD_Close); 00079 } 00080 shown = false; 00081 } 00082 } 00083 } 00084 00085 eOsdError cDvbSdFfOsd::CanHandleAreas(const tArea *Areas, int NumAreas) 00086 { 00087 eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); 00088 if (Result == oeOk) { 00089 if (NumAreas > MAXNUMWINDOWS) 00090 return oeTooManyAreas; 00091 int TotalMemory = 0; 00092 for (int i = 0; i < NumAreas; i++) { 00093 if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) 00094 return oeBppNotSupported; 00095 if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0) 00096 return oeWrongAlignment; 00097 if (Areas[i].Width() < 1 || Areas[i].Height() < 1 || Areas[i].Width() > 720 || Areas[i].Height() > 576) 00098 return oeWrongAreaSize; 00099 TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp); 00100 } 00101 if (TotalMemory > osdMem) 00102 return oeOutOfMemory; 00103 } 00104 return Result; 00105 } 00106 00107 eOsdError cDvbSdFfOsd::SetAreas(const tArea *Areas, int NumAreas) 00108 { 00109 if (shown) { 00110 for (int i = 0; GetBitmap(i); i++) { 00111 Cmd(OSD_SetWindow, 0, i + 1); 00112 Cmd(OSD_Close); 00113 } 00114 shown = false; 00115 } 00116 return cOsd::SetAreas(Areas, NumAreas); 00117 } 00118 00119 void cDvbSdFfOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data) 00120 { 00121 if (osdDev >= 0) { 00122 osd_cmd_t dc; 00123 dc.cmd = cmd; 00124 dc.color = color; 00125 dc.x0 = x0; 00126 dc.y0 = y0; 00127 dc.x1 = x1; 00128 dc.y1 = y1; 00129 dc.data = (void *)data; 00130 ioctl(osdDev, OSD_SEND_CMD, &dc); 00131 } 00132 } 00133 00134 void cDvbSdFfOsd::Flush(void) 00135 { 00136 if (!Active()) 00137 return; 00138 cBitmap *Bitmap; 00139 for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { 00140 Cmd(OSD_SetWindow, 0, i + 1); 00141 if (!shown) 00142 Cmd(OSD_Open, Bitmap->Bpp(), Left() + Bitmap->X0(), Top() + Bitmap->Y0(), Left() + Bitmap->X0() + Bitmap->Width() - 1, Top() + Bitmap->Y0() + Bitmap->Height() - 1, (void *)1); // initially hidden! 00143 int x1 = 0, y1 = 0, x2 = 0, y2 = 0; 00144 if (!shown || Bitmap->Dirty(x1, y1, x2, y2)) { 00145 if (!shown) { 00146 x1 = y1 = 0; 00147 x2 = Bitmap->Width() - 1; 00148 y2 = Bitmap->Height() - 1; 00149 } 00150 //TODO Workaround: apparently the bitmap sent to the driver always has to be a multiple 00151 //TODO of 8 bits wide, and (dx * dy) also has to be a multiple of 8. 00152 //TODO Fix driver (should be able to handle any size bitmaps!) 00153 while ((x1 > 0 || x2 < Bitmap->Width() - 1) && ((x2 - x1) & 7) != 7) { 00154 if (x2 < Bitmap->Width() - 1) 00155 x2++; 00156 else if (x1 > 0) 00157 x1--; 00158 } 00159 //TODO "... / 2" <==> Bpp??? 00160 while ((y1 > 0 || y2 < Bitmap->Height() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { 00161 if (y2 < Bitmap->Height() - 1) 00162 y2++; 00163 else if (y1 > 0) 00164 y1--; 00165 } 00166 while ((x1 > 0 || x2 < Bitmap->Width() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { 00167 if (x2 < Bitmap->Width() - 1) 00168 x2++; 00169 else if (x1 > 0) 00170 x1--; 00171 } 00172 // commit colors: 00173 int NumColors; 00174 const tColor *Colors = Bitmap->Colors(NumColors); 00175 if (Colors) { 00176 //TODO this should be fixed in the driver! 00177 tColor colors[NumColors]; 00178 for (int i = 0; i < NumColors; i++) { 00179 // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way): 00180 colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16); 00181 } 00182 Colors = colors; 00183 //TODO end of stuff that should be fixed in the driver 00184 Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors); 00185 } 00186 // commit modified data: 00187 Cmd(OSD_SetBlock, Bitmap->Width(), x1, y1, x2, y2, Bitmap->Data(x1, y1)); 00188 } 00189 Bitmap->Clean(); 00190 } 00191 if (!shown) { 00192 // Showing the windows in a separate loop to avoid seeing them come up one after another 00193 for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { 00194 Cmd(OSD_SetWindow, 0, i + 1); 00195 Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0()); 00196 } 00197 shown = true; 00198 } 00199 } 00200 00201 // --- cDvbOsdProvider ------------------------------------------------------- 00202 00203 cDvbOsdProvider::cDvbOsdProvider(int OsdDev) 00204 { 00205 osdDev = OsdDev; 00206 } 00207 00208 cOsd *cDvbOsdProvider::CreateOsd(int Left, int Top, uint Level) 00209 { 00210 return new cDvbSdFfOsd(Left, Top, osdDev, Level); 00211 }