trunk 20080912
[gitenigma.git] / lib / dvb / subtitling.cpp
blobb34dadde887c5d97ff668183c3c18c5678cc630d
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <memory.h>
6 #include <sys/ioctl.h>
7 #include <sys/wait.h>
8 #include <signal.h>
9 #include <errno.h>
11 #include <lib/gdi/gfbdc.h>
12 #include <lib/dvb/decoder.h>
13 #include <lib/gui/eskin.h>
15 #include <config.h>
16 #if HAVE_DVB_API_VERSION < 3
17 #include <ost/dmx.h>
18 #include <ost/video.h>
19 #include <ost/audio.h>
20 #define VIDEO_DEV "/dev/dvb/card0/video0"
21 #define AUDIO_DEV "/dev/dvb/card0/audio0"
22 #define DEMUX_DEV "/dev/dvb/card0/demux0"
23 #else
24 #include <linux/dvb/dmx.h>
25 #include <linux/dvb/video.h>
26 #include <linux/dvb/audio.h>
27 #define VIDEO_DEV "/dev/dvb/adapter0/video0"
28 #define AUDIO_DEV "/dev/dvb/adapter0/audio0"
29 #define DEMUX_DEV "/dev/dvb/adapter0/demux0"
30 #define audioStatus audio_status
31 #define videoStatus video_status
32 #define pesType pes_type
33 #define playState play_state
34 #define audioStreamSource_t audio_stream_source_t
35 #define videoStreamSource_t video_stream_source_t
36 #define streamSource stream_source
37 #define dmxPesFilterParams dmx_pes_filter_params
38 #endif
40 #include <lib/dvb/subtitling.h>
42 #ifndef TUXTXT_CFG_STANDALONE
43 #include <tuxtxt/tuxtxt_def.h>
44 extern "C" tstPageinfo* tuxtxt_DecodePage(int showl25, unsigned char* page_char, tstPageAttr *page_atrb, int hintmode, int showflof);
45 extern "C" void tuxtxt_RenderPage(tstRenderInfo* renderinfo);
46 extern "C" int tuxtxt_SetRenderingDefaults(tstRenderInfo* renderinfo);
47 extern "C" tuxtxt_cache_struct tuxtxt_cache;
48 extern "C" int tuxtxt_InitRendering(tstRenderInfo* renderinfo,int setTVFormat);
49 extern "C" void tuxtxt_EndRendering(tstRenderInfo* renderinfo);
50 tstRenderInfo renderinfo;
51 #endif
53 eSubtitleWidget *eSubtitleWidget::instance;
55 static int extractPTS(unsigned long long &pts, unsigned char *pkt)
57 pkt += 7;
58 int flags = *pkt++;
60 pkt++; // header length
62 if (flags & 0x80) /* PTS present? */
64 /* damn gcc bug */
65 pts = ((unsigned long long)(((pkt[0] >> 1) & 7))) << 30;
66 pts |= pkt[1] << 22;
67 pts |= (pkt[2]>>1) << 15;
68 pts |= pkt[3] << 7;
69 pts |= (pkt[5]>>1);
71 return 0;
72 } else
73 return -1;
76 void eSubtitleWidget::processPESPacket(unsigned char *pkt, int len)
78 unsigned long long current = 0;
79 if (Decoder::getSTC(current))
80 eDebug("bloed, going unsyced");
81 eDebug("DEMUX STC: %08llx", current);
83 unsigned long long pts = 0;
85 int enqueue = !queue.empty();
87 if (!extractPTS(pts, pkt))
89 eDebug("PES STC: %08llx", pts);
90 signed long long int diff = pts - current;
91 eDebug(" diff: %lld(%lldms)", diff, diff/90);
92 if (diff > 1800)
93 enqueue = 1;
94 else if (enqueue) // this should not happen !!
96 eDebug("showing instantly, diff small enough... but queue not empy!!!!");
97 enqueue = 0;
99 else
100 eDebug("showing instantly, diff small enough...!");
103 if (enqueue)
105 int wasempty = queue.empty();
106 struct pes_packet_s pes;
107 pes.pts = pts;
108 pes.pkt = new unsigned char[len];
109 memcpy(pes.pkt, pkt, len);
110 pes.len = len;
111 queue.push(pes);
112 eDebug("enqueue");
113 if (wasempty)
115 eDebug("setting timer to %lld ms!\n", (pes.pts - current) / 90);
116 timer.start((pes.pts - current) / 90, 1);
118 else
119 eDebug("");
121 return;
123 subtitle_process_pes(subtitle, pesbuffer, peslen);
126 void eSubtitleWidget::displaying_timeout()
128 eDebug("displaying timeout reached... hide visible subtitles");
129 subtitle_reset(subtitle);
130 if ( isVisible() )
131 subtitle_clear_screen(subtitle);
134 void eSubtitleWidget::processNext()
136 #ifndef TUXTXT_CFG_STANDALONE
137 if (ttx_running) // using teletext subtitles
139 renderinfo.pageinfo = tuxtxt_DecodePage(0,renderinfo.page_char,renderinfo.page_atrb,0,0);
140 if (renderinfo.pageinfo)
142 tuxtxt_RenderPage(&renderinfo);
144 return;
146 #endif
147 if (queue.empty())
149 eWarning("Subtitle queue is empty, but timer was called!");
150 return;
153 unsigned long long fpts=0;
154 int first = 1;
155 while (!queue.empty())
157 pes_packet_s pes = queue.front();
158 if (pes.pts && !first)
159 break;
160 if (first)
161 fpts = pes.pts;
162 first = 0;
163 queue.pop();
165 subtitle_process_pes(subtitle, pes.pkt, pes.len);
167 delete [] pes.pkt;
170 unsigned long long current = 0;
172 if (Decoder::getSTC(current))
174 eWarning("getSTC failed, dropping all Subtitle packets!");
175 while (!queue.empty())
177 pes_packet_s pkt = queue.front();
178 queue.pop();
179 delete [] pkt.pkt;
181 return;
184 eDebug("by the way, actual delay was %lld(%lld msek)", current - fpts, (current-fpts)/90 );
186 if (!queue.empty()) {
187 signed long long int diff = queue.front().pts - current;
188 timer.start(diff / 90, 1);
189 eDebug("setting timer to %lld ms!\n", diff / 90);
191 else
192 eDebug("");
195 void eSubtitleWidget::gotData(int what)
197 while (1)
199 unsigned char packet[1024];
200 int l;
201 l=::read(fd, packet, 1024);
202 if (l <= 0)
203 break;
205 unsigned char *p = packet;
207 while (l)
209 if (pos >= 6) // length ok?
211 int max = peslen - pos;
212 if (max > l)
213 max = l;
214 memcpy(pesbuffer + pos, p, max);
215 pos += max;
216 p += max;
218 l -= max;
220 if (pos == peslen)
222 processPESPacket(pesbuffer, pos);
223 pos = 0;
225 } else
227 if (pos < 4)
228 if (*p != "\x00\x00\x01\xbd"[pos])
230 pos = 0;
231 p++;
232 l--;
233 continue;
235 pesbuffer[pos++] = *p++; l--;
236 if (pos == 6)
238 peslen = ((pesbuffer[4] << 8) | pesbuffer[5]) + 6;
245 int eSubtitleWidget::eventHandler(const eWidgetEvent &event)
247 switch (event.type)
249 case eWidgetEvent::willShow:
250 // eDebug("willShow!!!");
251 #ifndef TUXTXT_CFG_STANDALONE
252 startttx(ttxpage);
253 #endif
254 isvisible = 1;
255 subtitle_screen_enable(subtitle, 1);
256 break;
257 case eWidgetEvent::willHide:
258 // eDebug("willHide!!!");
259 #ifndef TUXTXT_CFG_STANDALONE
260 stopttx();
261 #endif
263 isvisible = 0;
264 subtitle_screen_enable(subtitle, 0);
265 //restore old palette
266 eSkin::getActive()->setPalette(gFBDC::getInstance());
267 break;
268 default:
269 return eWidget::eventHandler(event);;
271 return 0;
274 #ifndef TUXTXT_CFG_STANDALONE
275 void eSubtitleWidget::startttx(int page)
277 if (page == 0|| ttx_running) return;
278 if (page < 0) page = ttxpage;
279 if (page == 0) return;
280 eDebug("Starting teletext subtitling:%x",page);
281 stop();
282 if (isvisible)
283 subtitle_screen_enable(subtitle, 1);
284 rememberttxpage = tuxtxt_cache.page;
285 rememberttxsubpage = tuxtxt_cache.subpage;
286 tuxtxt_cache.page = ttxpage = page;
287 ttx_running = 1;
288 this->pid = -ttxpage;
289 tuxtxt_cache.subpage = 0;
290 tuxtxt_SetRenderingDefaults(&renderinfo);
291 int left=20, top=20, right=699, bottom=555;
292 eConfig::getInstance()->getKey("/enigma/plugins/needoffsets/left", left);
293 eConfig::getInstance()->getKey("/enigma/plugins/needoffsets/top", top);
294 eConfig::getInstance()->getKey("/enigma/plugins/needoffsets/right", right);
295 eConfig::getInstance()->getKey("/enigma/plugins/needoffsets/bottom", bottom);
296 renderinfo.sx = left;
297 renderinfo.sy = top;
298 renderinfo.ex = right;
299 renderinfo.ey = bottom;
300 renderinfo.fb =fbClass::getInstance()->lock();
301 if (tuxtxt_InitRendering(&renderinfo,0))
303 renderinfo.pageinfo = tuxtxt_DecodePage(0,renderinfo.page_char,renderinfo.page_atrb,0,0);
304 if (renderinfo.pageinfo)
306 tuxtxt_cache.pageupdate=1; // force complete redraw of page
307 tuxtxt_RenderPage(&renderinfo);
309 timer.start(250, false);
312 #endif
313 void eSubtitleWidget::start(int pid, const std::set<int> &ppageids)
315 pageids = ppageids;
316 stop();
317 if (isvisible)
318 subtitle_screen_enable(subtitle, 1);
319 fd = open(DEMUX_DEV, O_RDWR|O_NONBLOCK);
320 if (fd == -1)
322 eWarning("failed to open " DEMUX_DEV ": %m");
323 return;
326 sn = new eSocketNotifier(eApp, fd, eSocketNotifier::Read);
327 CONNECT(sn->activated, eSubtitleWidget::gotData);
329 struct dmxPesFilterParams f;
330 this->pid = f.pid = pid;
331 f.input = DMX_IN_FRONTEND;
332 f.output = DMX_OUT_TAP;
333 f.pesType = DMX_PES_OTHER;
334 f.flags = DMX_IMMEDIATE_START;
335 if (::ioctl(fd, DMX_SET_PES_FILTER, &f) == -1)
336 eWarning("DMX_SET_PES_FILTER: %m (subtitling)");
337 else
338 eDebug("started subtitling filter..");
340 pos = 0;
343 static void subtitle_set_palette(struct subtitle_clut *pal)
345 static gRGB def_palette[16];
346 static bool def_palette_initialized;
348 gPainter p(*gFBDC::getInstance());
349 if ( !pal )// use default pallette
351 if ( !def_palette_initialized ) // fill default palette
353 for (int i=0; i < 16; ++i)
355 if (!i)
356 def_palette[i].a = 0xFF;
357 else if (i&8)
359 if (i & 1)
360 def_palette[i].r = 0x80;
361 if (i & 2)
362 def_palette[i].g = 0x80;
363 if (i & 4)
364 def_palette[i].b = 0x80;
366 else
368 if (i & 1)
369 def_palette[i].r = 0xFF;
370 if (i & 2)
371 def_palette[i].g = 0xFF;
372 if (i & 4)
373 def_palette[i].b = 0xFF;
375 // eDebug("%d %02x%02x%02x%02x",
376 // i, def_palette[i].r, def_palette[i].g, def_palette[i].b, def_palette[i].a);
378 def_palette_initialized=1;
380 p.setPalette(def_palette, 240, 16);
382 else
384 // eDebug("updating palette!");
385 gRGB palette[pal->size];
387 for (int i=0; i<pal->size; ++i)
389 int y = pal->entries[i].Y, cr = pal->entries[i].Cr, cb = pal->entries[i].Cb;
391 if (y > 0)
393 y -= 16;
394 cr -= 128;
395 cb -= 128;
396 #if 1
397 // let's try a bit different conversion method
398 palette[i].r = MAX(MIN(((298 * y + 460 * cr) / 256), 255), 0);
399 palette[i].g = MAX(MIN(((298 * y - 55 * cb - 137 * cr) / 256), 255), 0);
400 palette[i].b = MAX(MIN(((298 * y + 543 * cb ) / 256), 255), 0);
401 #else
402 palette[i].r = ((1164 * y + 1596 * cr) + 500) / 1000;
403 palette[i].g = ((1164 * y - 813 * cr - 392 * cb) + 500) / 1000;
404 palette[i].b = ((1164 * y + 2017 * cb) + 500) / 1000;
405 #endif
406 palette[i].a = (pal->entries[i].T) & 0xFF;
407 } else
409 palette[i].r = 0;
410 palette[i].g = 0;
411 palette[i].b = 0;
412 palette[i].a = 0xFF;
414 // eDebug("%d: %d %d %d %d", i, palette[i].r, palette[i].g, palette[i].b, palette[i].a);
416 p.setPalette(palette, 240, pal->size);
418 // eDebug("palette changed");
421 eSubtitleWidget::eSubtitleWidget()
422 :timer(eApp), timeout(eApp)
424 instance = this;
425 fd = -1;
426 sn = 0;
427 #ifndef TUXTXT_CFG_STANDALONE
428 ttxpage = 0;
429 ttx_running= 0;
430 #endif
431 subtitle = new subtitle_ctx;
432 subtitle->pages = 0;
433 subtitle->bbox_left = 0;
434 subtitle->bbox_right = 0;
435 subtitle->bbox_top = 0;
436 subtitle->bbox_bottom = 0;
437 subtitle->screen_enabled = 0;
438 subtitle->timeout_timer = &timeout;
440 gFBDC *fbdc = gFBDC::getInstance();
441 gPixmap *pixmap = &fbdc->getPixmap();
443 subtitle->screen_width = pixmap->x;
444 subtitle->screen_height = pixmap->y;
445 subtitle->screen_buffer = (__u8*)pixmap->data;
446 subtitle->set_palette = subtitle_set_palette;
448 CONNECT(timer.timeout, eSubtitleWidget::processNext);
449 CONNECT(timeout.timeout, eSubtitleWidget::displaying_timeout);
450 CONNECT(eWidget::globalFocusChanged, eSubtitleWidget::globalFocusHasChanged);
453 eSubtitleWidget::~eSubtitleWidget()
455 stop();
456 delete subtitle;
459 int eSubtitleWidget::getCurPid()
461 return pid;
464 void eSubtitleWidget::stop()
466 //eDebug("stopping subtitling, queue size:%d",queue.size());
467 while (!queue.empty())
469 pes_packet_s pkt = queue.front();
470 queue.pop();
471 delete [] pkt.pkt;
473 if ( sn )
475 pid=-1;
476 delete sn;
477 sn = 0;
478 subtitle_screen_enable(subtitle, 0);
479 subtitle_reset(subtitle);
480 if (fd != -1)
482 ::close(fd);
483 fd = -1;
485 eSkin::getActive()->setPalette(gFBDC::getInstance());
487 #ifndef TUXTXT_CFG_STANDALONE
488 stopttx();
489 ttxpage = 0;
490 pid=-1;
491 #endif
494 #ifndef TUXTXT_CFG_STANDALONE
495 void eSubtitleWidget::stopttx()
497 if (ttx_running)
499 eDebug("Stopping teletext subtitling:%x",ttxpage);
500 tuxtxt_EndRendering(&renderinfo);
501 tuxtxt_cache.page = rememberttxpage;
502 tuxtxt_cache.subpage = rememberttxsubpage;
503 ttx_running = 0;
504 timer.stop();
505 fbClass::getInstance()->unlock();
506 eSkin::getActive()->setPalette(gFBDC::getInstance());
510 #endif
512 void eSubtitleWidget::globalFocusHasChanged(const eWidget* newFocus)
514 if ( !sn && !ttxpage) // not running
515 return;
516 if ( newFocus )
517 hide();
518 else
519 show();