]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Fix text corruption in channel number display on live tv
[vompclient.git] / vvideolivetv.cc
1 /*
2     Copyright 2007-2008 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "vvideolivetv.h"
22
23 #include "vchannellist.h"
24 #include "video.h"
25 #include "audio.h"
26 #include "playerlive.h"
27 #include "playerlivetv.h"
28 #include "playerliveradio.h"
29 #include "channel.h"
30 #include "boxstack.h"
31 #include "colour.h"
32 #include "osd.h"
33 #include "command.h"
34 #include "i18n.h"
35 #include "wtextbox.h"
36 #include "remote.h"
37 #include "vaudioselector.h"
38 #include "colour.h"
39 #include "event.h"
40 #include "timers.h"
41 #include "vepg.h"
42 #include "bitmap.h"
43 #include "log.h"
44 #include "vteletextview.h"
45 #include "vepgsummary.h"
46 #include "vepglistadvanced.h"
47 #include "staticartwork.h"
48 #include "demuxer.h"
49
50 #include <sstream>
51
52 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
53 {
54   vdr = VDR::getInstance();
55   boxstack = BoxStack::getInstance();
56   video = Video::getInstance();
57   
58   vas = NULL;
59
60   chanList = tchanList;
61   vchannelList = tvchannelList;
62   numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
63
64   currentChannelIndex = 0;
65   previousChannelIndex = 0;
66   osdChannelIndex = 0;
67   keying = 0;
68   preBuffering = 0;
69
70   playing = false;
71
72   // Convert channel number to index
73   UINT i;
74   for(i = 0; i < chanList->size(); i++)
75   {
76     if ((*chanList)[i]->number == (UINT)initialChannelNumber)
77     {
78       currentChannelIndex = i;
79       osdChannelIndex = i;
80       break;
81     }
82   }
83
84   eventList = NULL;
85
86   videoMode = video->getMode();
87   
88   if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
89   {
90     streamType = VDR::VIDEO;
91     player = new PlayerLiveTV(Command::getInstance(), this, this, chanList);
92   }
93   else
94   {
95     streamType = VDR::RADIO;
96     player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
97   }
98   player->init();
99
100   setSize(video->getScreenWidth(), video->getScreenHeight());
101   createBuffer();
102   DrawStyle transparent(0, 0, 0, 0);
103   setBackgroundColour(transparent);
104 #ifdef PAL_WSS
105   dowss = false;
106   char* optionWSS = vdr->configLoad("General", "WSS");
107   if (optionWSS)
108   {
109     if (strstr(optionWSS, "Yes")) dowss = true;
110     delete[] optionWSS;
111   }
112   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
113
114   if (dowss)
115   {
116     wss.setFormat(video->getFormat());
117     wss.setWide(true);
118     add(&wss);
119     
120     wssRegion.x = 0;
121     wssRegion.y = 6;
122     wssRegion.w = video->getScreenWidth();
123     wssRegion.h = 2;
124   }
125 #endif
126   // This variable is set to true if the user pressed OK to bring the OSD on screen
127   // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling
128   okTriggeredOSD = false;
129
130   TVMediaInfo info;
131   info.setChannelLogo(initialChannelNumber);
132   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
133
134   if (osdv && streamType == VDR::RADIO) {
135           radioChannelLogo.setPosition(video->getScreenWidth()/4,video->getScreenHeight()/4);
136           radioChannelLogo.setSize(video->getScreenWidth()/2,video->getScreenHeight()/2);
137           radioChannelLogo.setVisible(true);
138           radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
139           add(&radioChannelLogo);
140   }
141
142   osdposterbanner.setPosition(20,20);
143   //osdposterbanner.setBackgroundColour(DrawStyle::OSDBACKGROUND);
144   osdposterbanner.setSize(video->getScreenWidth()*4/10,video->getScreenHeight()*4/10);
145   osdposterbanner.setVisible(false);
146   add(&osdposterbanner);
147
148
149
150   osd.setBackgroundColour(DrawStyle::OSDBACKGROUND);
151   osd.setPosition(0, video->getScreenHeight() - 150);
152   osd.setSize(video->getScreenWidth(), 150);
153   osd.setVisible(false);
154   add(&osd);
155   
156   int channellogomove=0;
157   int boxdiff=166;
158   if (osdv) {
159           //osdChannelLogo.setBackgroundColour(DrawStyle::OSDBACKGROUND);
160           osdChannelLogo.setPosition(30,5);
161           osdChannelLogo.setSize(60,60);
162           osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
163           osd.add(&osdChannelLogo);
164           channellogomove=30-5;
165           boxdiff=145;
166   }
167
168
169   //clock.setBackgroundColour(DrawStyle::OSDBACKGROUND);
170   clock.setPosition(osd.getWidth() - 100, 4);
171   clock.setSize(90, 30);
172   osd.add(&clock);
173
174   //osdChanNum.setBackgroundColour(DrawStyle::OSDBACKGROUND);
175   osdChanNum.setPosition(60+channellogomove, 4);
176   osdChanNum.setSize((numberWidth*10) + 22, getFontHeight()+5); // 10 px = width of number chars in font
177   osd.add(&osdChanNum);  
178
179   //osdChanName.setBackgroundColour(DrawStyle::OSDBACKGROUND);
180   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
181   osdChanName.setSize(300, 30);
182   osd.add(&osdChanName);
183   
184   boxRed.setBackgroundColour(DrawStyle::RED);
185   boxRed.setPosition(54+0*boxdiff, 104);
186   boxRed.setSize(18, 16);
187   osd.add(&boxRed);
188
189   boxGreen.setBackgroundColour(DrawStyle::GREEN);
190   boxGreen.setPosition(54+1*boxdiff, 104);
191   boxGreen.setSize(18, 16);
192   osd.add(&boxGreen);
193
194   boxYellow.setBackgroundColour(DrawStyle::YELLOW);
195   boxYellow.setPosition(54+2*boxdiff, 104);
196   boxYellow.setSize(18, 16);
197   osd.add(&boxYellow);
198
199   boxBlue.setBackgroundColour(DrawStyle::BLUE);
200   boxBlue.setPosition(54+3*boxdiff, 104);
201   boxBlue.setSize(18, 16);
202   osd.add(&boxBlue);  
203   
204   //textRed.setBackgroundColour(DrawStyle::OSDBACKGROUND);
205   textRed.setPosition(boxRed.getX2(), 98);
206   textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30);
207   textRed.setText(tr("Summary"));
208   osd.add(&textRed);  
209     
210   if (streamType == VDR::VIDEO)
211   {
212     //textGreen.setBackgroundColour(DrawStyle::OSDBACKGROUND);
213     textGreen.setPosition(boxGreen.getX2(), 98);
214     textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30);
215     textGreen.setText(tr("Audio"));
216     osd.add(&textGreen);  
217   }
218     
219   //textYellow.setBackgroundColour(DrawStyle::OSDBACKGROUND);
220   textYellow.setPosition(boxYellow.getX2(), 98);
221   textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30);
222   textYellow.setText(tr("Teletext"));
223   osd.add(&textYellow);  
224     
225   //textBlue.setBackgroundColour(DrawStyle::OSDBACKGROUND);
226   textBlue.setPosition(boxBlue.getX2(), 98);
227   textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30);
228   textBlue.setText(tr("EPG"));
229   osd.add(&textBlue);  
230     
231   //sl.setBackgroundColour(DrawStyle::OSDBACKGROUND);
232   sl.setPosition(90, 36);
233   sl.setSize(480, 58);
234   osd.add(&sl);
235   
236   
237
238   bufferBar.setPosition(osd.getWidth() - 90, 70);
239   bufferBar.setSize(40, 20);
240   bufferBar.setVisible(true);
241   osd.add(&bufferBar);
242
243   if (!osdv) {
244           sAspectRatio.setPosition(osd.getWidth() - 90, 40);
245           sAspectRatio.nextColour = DrawStyle::LIVETVSYMBOLS;
246           sAspectRatio.setVisible(false);
247           osd.add(&sAspectRatio);
248   
249           sAudioChannels.setPosition(osd.getWidth() - 130, 40);
250           sAudioChannels.nextColour = DrawStyle::LIVETVSYMBOLS;
251           sAudioChannels.setVisible(false);
252           osd.add(&sAudioChannels);
253   }
254   
255   if (osdv) {
256           TVMediaInfo ninfo;
257           ninfo.setStaticArtwork(sa_txtoff);
258           txtlogo.setPosition(54+4*boxdiff,104);
259           txtlogo.setSize(22,20);
260           txtlogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
261           osd.add(&txtlogo);
262
263           ninfo.setStaticArtwork(sa_dolbyoff);
264           dolbylogo.setPosition(54+4*boxdiff+15,104);
265           dolbylogo.setSize(22,20);
266           dolbylogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
267           osd.add(&dolbylogo);
268
269           ninfo.setStaticArtwork(sa_sd576i);
270           reslogo.setPosition(54+4*boxdiff+30,104);
271           reslogo.setSize(22,20);
272           reslogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
273           osd.add(&reslogo);
274
275           reslogo.setVisible(false);
276           dolbylogo.setVisible(false);
277           txtlogo.setVisible(false);
278   }
279
280
281   //textUnavailable.setBackgroundColour(DrawStyle::OSDBACKGROUND);
282   textUnavailable.setPosition(60, 30);
283   textUnavailable.setSize(osd.getWidth() - 120, 30);
284   textUnavailable.setText(tr("Channel Unavailable"));
285   textUnavailable.setVisible(false);
286   add(&textUnavailable);
287   
288
289   vdisplay.mode=Fullscreen;
290   vdisplay.fallbackMode=Fullscreen;
291   vdisplay.x=0;
292   vdisplay.y=0;
293   vdisplay.width=0;
294   vdisplay.height=0;
295
296 }
297
298 void VVideoLiveTV::preDelete()
299 {
300   if (playing) stop();
301 }
302
303 VVideoLiveTV::~VVideoLiveTV()
304 {
305   delete player;
306   video->setDefaultAspect();
307   delData();
308   OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
309   if (osdv)
310   {
311           osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
312   }
313 }
314
315 void VVideoLiveTV::delData()
316 {
317   if (eventList)
318   {
319     int eventListSize = eventList->size();
320     for(int i = 0; i < eventListSize; i++)
321     {
322       delete (*eventList)[i];
323     }
324     eventList->clear();
325     delete eventList;
326
327   }
328 }
329
330 int VVideoLiveTV::handleCommand(int command)
331 {
332   switch(command)
333   {
334     case Remote::BACK:
335     {
336       if (osd.getVisible() && !textUnavailable.getVisible())
337       {
338         clearScreen();
339         return 2;
340       }
341       // else drop through to stop
342     }
343     case Remote::STOP:
344     {
345       stop();
346       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
347       return 4;
348     }
349     
350     // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there
351     case Remote::UP:
352     {
353         if (Remote::getInstance()->mayHaveFewButtons()) {
354               if (okTriggeredOSD) doUpDown(false);
355               else doChanUpDown(UP);
356         } else {
357                 doUpDown(false);
358         }
359       return 2;
360     }
361     case Remote::DOWN:
362     {
363       if (Remote::getInstance()->mayHaveFewButtons()) {
364           if (okTriggeredOSD) doUpDown(true);
365           else doChanUpDown(DOWN);
366       } else {
367           doUpDown(true);
368       }
369       return 2;
370     }
371     case Remote::LEFT:
372     {
373       doLeftRight(false);
374       return 2;
375     }
376     case Remote::RIGHT:
377     {
378       doLeftRight(true);
379       return 2;
380     }
381     // Continue new remote only...
382     case Remote::CHANNELUP:
383     {
384       doChanUpDown(UP);
385       return 2;
386     }
387     case Remote::CHANNELDOWN:
388     {
389       doChanUpDown(DOWN);
390       return 2;
391     }
392
393     // END NEW REMOTE ONLY, START OLD REMOTE ONLY
394     
395     // DF_LEFT and DF_RIGHT never get here because they are stolen
396     // by command as vol- and vol+
397     
398     // Old remote. Decide what to do based on whether
399     // OK was pressed - osd shown manually, use up/down for epg nav
400     // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch-
401     
402     case Remote::DF_UP:
403     {
404       // Old remote, decide what to do based on okTriggeredOSD
405       if (okTriggeredOSD) doUpDown(false);
406       else doChanUpDown(UP);
407       return 2;
408     }
409     case Remote::DF_DOWN:
410     {
411       // Old remote, decide what to do based on okTriggeredOSD
412       if (okTriggeredOSD) doUpDown(true);
413       else doChanUpDown(DOWN);
414       return 2;
415     }
416
417     // END NEW/OLD REMOTE STUFF
418
419     case Remote::PREVCHANNEL:
420     {
421       channelChange(PREVIOUS, 0);
422       osdChannelIndex = currentChannelIndex;
423       displayOSD(true);
424       return 2;
425     }
426     case Remote::OK:
427     {
428       doOK();
429       return 2;
430     }
431     case Remote::RED:
432     case Remote::MENU:
433     {
434       doSummary();
435       return 2;
436     }
437     case Remote::FULL:
438     case Remote::TV:
439     {
440       toggleChopSides();
441       return 2;
442     }
443
444     case Remote::ZERO:
445     case Remote::ONE:
446     case Remote::TWO:
447     case Remote::THREE:
448     case Remote::FOUR:
449     case Remote::FIVE:
450     case Remote::SIX:
451     case Remote::SEVEN:
452     case Remote::EIGHT:
453     case Remote::NINE:
454     {
455       // key in channel number
456       doKey(command);
457       return 2;
458     }
459
460     case Remote::GREEN:
461     {
462       if (streamType == VDR::VIDEO) doAudioSelector();
463       return 2;   
464     }
465     case Remote::YELLOW:
466     {
467       if (streamType ==VDR::VIDEO) 
468       {
469         doTeletext(); //TODO: Add a selector for subtitles or teletext
470       }
471       return 2;
472     }
473     case Remote::GUIDE:
474     case Remote::BLUE:
475     {
476       doEPG();
477       return 2;
478     }
479     case Remote::RECORD:
480       if (streamType == VDR::VIDEO)
481         (static_cast<PlayerLiveTV*>(player))->toggleSubtitles();
482       return 2;
483   }
484
485   return 1;
486 }
487
488 void VVideoLiveTV::go()
489 {
490   playing = true;
491   draw();
492   boxstack->update(this);
493
494   setClock();
495   displayOSD(true);
496   
497   player->go(currentChannelIndex);
498 }
499
500 void VVideoLiveTV::stop()
501 {
502   Timers::getInstance()->cancelTimer(this, 1);
503   Timers::getInstance()->cancelTimer(this, 2);
504   player->stop();
505   playing = false;
506 }
507
508 void VVideoLiveTV::doLeftRight(bool right)
509 {
510   if (osd.getVisible())
511   {
512     if (right) osdChannelIndex = upChannel(osdChannelIndex);
513     else       osdChannelIndex = downChannel(osdChannelIndex);
514   }
515   else
516   {
517     osdChannelIndex = currentChannelIndex;
518   }
519   displayOSD(true);
520 }
521
522 void VVideoLiveTV::doUpDown(bool down)
523 {
524   if (osd.getVisible())
525   {
526     sl.draw();
527     
528     displayOSD(false);
529   }
530   else
531   {
532     displayOSD(true);
533   }
534 }
535
536 void VVideoLiveTV::doChanUpDown(int which)
537 {
538   channelChange(OFFSET, which);
539   osdChannelIndex = currentChannelIndex;
540   displayOSD(true);
541 }
542
543 void VVideoLiveTV::doOK()
544 {
545   if (osd.getVisible())
546   {
547     if (keying)
548     {
549       UINT newChannel = 0;
550       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
551       
552       channelChange(NUMBER, newChannel);
553       osdChannelIndex = currentChannelIndex;
554       displayOSD(true);
555     }
556     else if (osdChannelIndex == currentChannelIndex)
557     {
558       clearScreen();
559     }
560     else
561     {
562       channelChange(INDEX, osdChannelIndex);
563       displayOSD(true);
564     }
565   }
566   else
567   {
568     osdChannelIndex = currentChannelIndex;
569     displayOSD(true);
570     okTriggeredOSD = true;
571   }
572 }
573
574 void VVideoLiveTV::doSummary()
575 {
576         displayOSD(false);
577         Channel* currentChannel = (*chanList)[osdChannelIndex];
578         eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
579         if (!eventList)
580         {
581                 return;
582         }
583         sort(eventList->begin(), eventList->end(), EventSorter());
584
585         if (eventList->size() < 1) return;
586         Event* event = (*eventList)[0];
587         event->loadinfos(currentChannel->number);
588
589         VEpgSummary* vr = new VEpgSummary(event,currentChannel);
590         vr->draw();
591         boxstack->add(vr);
592         boxstack->update(vr);
593
594 }
595
596 void VVideoLiveTV::doKey(int command)
597 {
598   if (!osd.getVisible()) // First key. prep the data
599   {
600     setNowNextData();
601     updatePosterBanner();
602     keying = 0;    
603   }
604
605   int i;
606   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
607   keyingInput[0] = command;
608   keying++;
609
610   char* keyingString = new char[numberWidth + 1];
611   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
612   keyingString[numberWidth] = '\0';
613
614   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
615   
616   if (keying == numberWidth)
617   {
618     UINT newChannel = 0;
619     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
620     
621     channelChange(NUMBER, newChannel);
622     osdChannelIndex = currentChannelIndex;
623     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
624     displayOSD(true); // this will put one back if required
625   }
626   else
627   {
628     osdChanNum.setText(keyingString);
629
630     if (!osd.getVisible())
631     {
632       osd.setVisible(true);
633       draw();
634     }
635     else
636     {
637       osdChanNum.fillColour(DrawStyle::VIEWBACKGROUND);
638       osdChanNum.draw();
639     }
640     boxstack->update(this, osd.getRegion());
641     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
642   }
643   delete[] keyingString;
644 }
645
646 void VVideoLiveTV::doTeletext(bool subtitlemode)
647 {
648   if (streamType !=VDR::VIDEO) return;
649   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
650   bool exists=true;
651
652   // Cancel keying
653   if (keying)
654   {
655     keying = 0;
656     // and reset the display - this is a copy from setNowNextData
657     char formatChanNum[20];
658     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
659     osdChanNum.setText(formatChanNum);
660     osdChanName.setText((*chanList)[osdChannelIndex]->name);
661   }
662   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
663   if (osd.getVisible()) clearScreen();
664   // Draw the teletxt
665   VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
666   if (vtxv==NULL) {
667        vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this, (PlayerLiveTV*)player);
668       ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv);
669       exists=false;
670   }
671   vtxv->setSubtitleMode(subtitlemode);
672   vtxv->draw();
673   draw();
674   
675   if (!exists) {
676       BoxStack::getInstance()->add(vtxv);
677   }
678   BoxStack::getInstance()->update(this);
679   BoxStack::getInstance()->update(vtxv); 
680 }
681
682 void VVideoLiveTV::doAudioSelector()
683 {
684   // If the osd is already visisble there might be a timer for it
685   Timers::getInstance()->cancelTimer(this, 1);
686   //This causes a deadlock with the timertrhread itself is locked
687
688
689   // Cancel keying
690   if (keying)
691   {
692     keying = 0;
693     // and reset the display - this is a copy from setNowNextData
694     char formatChanNum[20];
695     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
696     osdChanNum.setText(formatChanNum);
697     osdChanName.setText((*chanList)[osdChannelIndex]->name);
698   }
699   int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel();
700   int subtitleType=0x10;
701   if (!(static_cast<PlayerLiveTV*>(player))->isSubtitlesOn()) {
702       if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() &&
703           ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() 
704           ) {
705               subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage();
706               subtitleType=0x11;
707           
708       } else {
709           subtitleType=0xFF; //turnedOff
710           subtitleChannel=0;
711       }
712   }
713
714   // Draw the selector
715   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(),
716       subtitleType,subtitleChannel,NULL);
717
718   vas->setBackgroundColour(DrawStyle::OSDBACKGROUND);
719   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
720   vas->draw();
721
722   // make vas != null and displayOSD will not set a timer or do any boxstack update
723   if (osd.getVisible()) displayOSD(false);
724   else displayOSD(true);
725   draw();
726
727   BoxStack::getInstance()->add(vas);
728   BoxStack::getInstance()->update(this);        
729   BoxStack::getInstance()->update(vas);     
730 }      
731       
732 void VVideoLiveTV::doEPG()
733 {
734   if (osd.getVisible()) clearScreen();
735
736   if (!Command::getInstance()->advMenues()) {
737
738           VEpg* vepg = new VEpg(this, currentChannelIndex, chanList);
739           vepg->draw();
740           boxstack->add(vepg);
741           boxstack->update(vepg);
742   } else {
743           Channel* currentChannel = (*chanList)[osdChannelIndex];
744           VEpgListAdvanced  *vepg= new VEpgListAdvanced(this, chanList, currentChannel->number);
745           vepg->draw();
746           boxstack->add(vepg);
747           boxstack->update(vepg);
748   }
749 }
750
751 void VVideoLiveTV::setNowNextData()
752 {
753   delData();
754   
755   Channel* currentChannel = (*chanList)[osdChannelIndex];
756
757   char formatChanNum[20];
758   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
759   osdChanNum.setText(formatChanNum);
760   osdChanName.setText(currentChannel->name);
761
762   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
763
764   if (!eventList)
765   {
766     sl.setText(tr("No channel data available"));
767   }
768   else
769   {
770     sort(eventList->begin(), eventList->end(), EventSorter());
771
772     char tempString[300];
773     char tempString2[300];
774     struct tm btime;
775     Event* event;
776     int eventListSize = eventList->size();
777     std::stringstream string;
778     for(int i = 0; i < eventListSize; i++)
779     {
780       event = (*eventList)[i];
781
782       time_t etime = event->time;
783       LOCALTIME_R(&etime, &btime);
784 #ifndef _MSC_VER
785       strftime(tempString2, 299, "%0H:%0M ", &btime);
786 #else
787       strftime(tempString2, 299, "%H:%M ", &btime);
788 #endif
789       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
790       string << tempString << "\n";
791     }
792     sl.setText(string.str().c_str());
793   }
794 }
795
796
797
798 void VVideoLiveTV::displayOSD(bool newNowNextData)
799 {
800   PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
801   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(true);
802   osd.setVisible(true);
803   if (newNowNextData)
804   {
805     setNowNextData();
806     updatePosterBanner();
807     keying = 0;
808   }
809   osd.draw();
810   if (osdposterbanner.getVisible()) osdposterbanner.draw();
811   
812
813   Region  toupdate;
814
815   toupdate=*osd.getRegion();
816
817
818   if (osdposterbanner.getVisible()) {
819           boxstack->update(this);
820   } else {
821           boxstack->update(this, &toupdate);
822   }
823   
824   bool setTimer = true;
825   if (vas) setTimer = false;
826   if (textUnavailable.getVisible()) setTimer = false;
827
828   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
829 }
830
831 void VVideoLiveTV::clearScreen()
832 {  
833   Timers::getInstance()->cancelTimer(this, 1);
834
835   textUnavailable.setVisible(false);
836   osd.setVisible(false);
837   osdposterbanner.setVisible(false);
838
839   okTriggeredOSD = false;
840
841   draw();
842   boxstack->update(this);
843
844   PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
845   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
846 }
847
848 void VVideoLiveTV::showUnavailable()
849 {
850   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");  
851   textUnavailable.setVisible(true);
852   textUnavailable.draw();
853   
854   if (!osd.getVisible()) displayOSD(true);
855
856   boxstack->update(this, textUnavailable.getRegion());  
857 }
858
859 void VVideoLiveTV::setClock()
860 {
861   char timeString[20];
862   time_t t;
863   time(&t);
864   struct tm tms;
865   LOCALTIME_R(&t, &tms);
866   strftime(timeString, 19, "%H:%M", &tms);
867   clock.setText(timeString);
868
869   time_t dt = 60 - (t % 60);  // seconds to the next minute
870   if (dt == 0) dt = 60; // advance a whole minute if necessary
871   dt += t;  // get a time_t value for it rather than using duration
872   // (so it will occur at the actual second and not second and a half)
873
874   Timers::getInstance()->setTimerT(this, 2, dt);
875 }
876
877 void VVideoLiveTV::timercall(int ref)
878 {
879   if (ref == 1)
880   {
881     if (keying)
882     {
883          Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); 
884       UINT newChannel = 0;
885       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
886       
887       Message* m = new Message();
888       m->message = Message::CHANNEL_CHANGE;
889       m->to = this;
890           m->parameter.num = newChannel;
891       m->tag = 1; // signal to call displayOSD();
892       Command::getInstance()->postMessageFromOuterSpace(m);
893       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); 
894     }
895     else
896     {
897         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); 
898       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
899       if (preBuffering < 100)
900       {
901         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");  
902         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
903         return;
904       }
905       bool osdpbvisible=osdposterbanner.getVisible();
906       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); 
907       osd.setVisible(false);
908       osdposterbanner.setVisible(false);
909       okTriggeredOSD = false;
910       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); 
911       draw();
912       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); 
913       if (osdpbvisible) boxstack->update(this);
914       else  boxstack->update(this, osd.getRegion());
915
916       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); 
917           PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
918           if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
919       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); 
920     }
921   }
922   else if (ref == 2)
923   {
924       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2  start."); 
925     setClock();
926     if (osd.getVisible())
927     {
928       clock.draw();
929       boxstack->update(this, osd.getRegion());
930     }
931     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); 
932   }
933 }
934
935 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
936 {
937   UINT newChannel = 0;
938   if (streamType ==VDR::VIDEO) {
939           VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
940           if (vtxt ) {
941                   BoxStack::getInstance()->remove(vtxt);
942
943           }
944   }
945   if (changeType == INDEX)
946   {
947     newChannel = newData;
948   }
949   else if (changeType == NUMBER)
950   {
951     UINT i;
952     for(i = 0; i < chanList->size(); i++)
953     {
954       if ((*chanList)[i]->number == (UINT)newData)
955       {
956         newChannel = i;
957         break;
958       }
959     }
960
961     if (i == chanList->size())
962     {
963       // no such channel
964       return false;
965     }
966   }
967   else if (changeType == OFFSET)
968   {
969     if (newData == UP) newChannel = upChannel(currentChannelIndex);
970     else newChannel = downChannel(currentChannelIndex);
971   }
972   else if (changeType == PREVIOUS)
973   {
974     newChannel = previousChannelIndex;
975   }
976   else
977   {
978     return false; // bad input
979   }
980
981   if (newChannel == currentChannelIndex) return true;
982
983   previousChannelIndex = currentChannelIndex;
984   currentChannelIndex = newChannel;
985   
986   preBuffering = 0;
987   
988   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
989   player->setChannel(currentChannelIndex);
990   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
991
992   // Blank out the symbols
993   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
994
995   bufferBar.setPercent(0);
996   
997   if (!osdv) {
998           sAudioChannels.setVisible(false);
999           sAspectRatio.setVisible(false);
1000   } else {
1001           reslogo.setVisible(false);
1002           dolbylogo.setVisible(false);
1003           txtlogo.setVisible(false);
1004   }
1005
1006   // Remove other stuff
1007   if (textUnavailable.getVisible())
1008   {
1009     textUnavailable.setVisible(false);
1010     
1011   }
1012
1013   if (osdv) {
1014           TVMediaInfo info;
1015           Channel* currentChannel = (*chanList)[currentChannelIndex];
1016           if (currentChannel) {
1017                   info.setChannelLogo(currentChannel->number);
1018                   if (streamType == VDR::RADIO) {
1019                           radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1020                           radioChannelLogo.setVisible(true);
1021                   }
1022                   osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1023
1024           }
1025   }
1026
1027   draw();
1028   BoxStack::getInstance()->update(this);
1029   
1030   return true;
1031 }
1032
1033 void VVideoLiveTV::processMessage(Message* m)
1034 {
1035   if (m->message == Message::MOUSE_LBDOWN)
1036   {
1037     //check if press is outside this view! then simulate cancel
1038         int x = (m->parameter.num >> 16) - osd.getScreenX();
1039         int y = (m->parameter.num & 0xFFFF) - osd.getScreenY();
1040     if (osd.getVisible()) {
1041     
1042         if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
1043             (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
1044             BoxStack::getInstance()->handleCommand(Remote::RED);
1045         } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
1046             (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
1047             BoxStack::getInstance()->handleCommand(Remote::GREEN);
1048         } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
1049             (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
1050             BoxStack::getInstance()->handleCommand(Remote::YELLOW);
1051         } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
1052             (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
1053             BoxStack::getInstance()->handleCommand(Remote::BLUE);
1054         } else {
1055             BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1056         }
1057
1058     } else {
1059         BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1060     }
1061   }
1062   else if (m->message == Message::CHANNEL_CHANGE)
1063   {
1064         channelChange(NUMBER, m->parameter.num);
1065     osdChannelIndex = currentChannelIndex;
1066     if (m->tag == 1) displayOSD(true);
1067   }
1068   else if (m->message == Message::CHILD_CLOSE)
1069   {
1070     if (m->from == vas)
1071     {
1072       vas = NULL;
1073       displayOSD(false);
1074     }
1075   }
1076   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
1077   {
1078         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %x", m->parameter.num);
1079         player->setAudioChannel((m->parameter.num & 0xFFFF), (m->parameter.num & 0xFF0000) >> 16, (m->parameter.num & 0xFF000000) >> 24);
1080   } 
1081   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
1082   {
1083     if (streamType !=VDR::VIDEO) return;
1084           Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %x", m->parameter.num);
1085       int type=((m->parameter.num & 0xFF0000)>>16);
1086       switch (type) {
1087       case 0x10: { //dvbsubtitle
1088           if (streamType == VDR::VIDEO){
1089               player->setSubtitleChannel((m->parameter.num & 0xFFFF));
1090               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(true);
1091               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1092               if (vtxt && vtxt->isInSubtitleMode()) {
1093                   BoxStack::getInstance()->remove(vtxt);
1094               }
1095           }
1096                  } break;
1097       case 0xFF: { //nosubtitles
1098           if (streamType == VDR::VIDEO){
1099               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1100               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1101               if (vtxt && vtxt->isInSubtitleMode()) {
1102                   BoxStack::getInstance()->remove(vtxt);
1103               }  
1104           }
1105                  } break;
1106       case 0x11: { //videotext
1107           (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1108           doTeletext(true);
1109           ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter.num & 0xFFFF));
1110                  } break;
1111       };
1112       if (vas) {
1113         BoxStack::getInstance()->update(vas);
1114         //BoxStack::getInstance()->update(&osd); //eveil error
1115       }
1116       BoxStack::getInstance()->update(this, osd.getRegion());
1117
1118       
1119   }
1120   else if (m->message == Message::PLAYER_EVENT)
1121   {
1122         switch (m->parameter.num)
1123     {
1124       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
1125       {
1126         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
1127         Command::getInstance()->connectionLost();
1128         break;
1129       }
1130       
1131       case PlayerLiveTV::STREAM_END:
1132       {
1133         // Message comes from playerlivetv through master mutex, so can do anything here
1134         showUnavailable();        
1135         break;
1136       }
1137       
1138       case PlayerLiveTV::ASPECT43:
1139       {
1140 #ifdef PAL_WSS
1141         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1142         {
1143           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
1144           wss.setWide(false);
1145           wss.draw();
1146           BoxStack::getInstance()->update(this, &wssRegion);
1147         }
1148 #endif
1149         
1150         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1151         if (osdv)
1152         {
1153           osdv->updateBackgroundColor(DrawStyle::BLACK);
1154         }
1155
1156         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
1157         sAspectRatio.setVisible(true);
1158         
1159         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1160         {
1161           osd.draw();
1162           BoxStack::getInstance()->update(this, osd.getRegion());
1163         }
1164         
1165         break;
1166       }
1167       case PlayerLiveTV::ASPECT169:
1168       {
1169 #ifdef PAL_WSS
1170         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1171         {
1172           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
1173           wss.setWide(true);
1174           wss.draw();
1175           BoxStack::getInstance()->update(this, &wssRegion);
1176         }
1177 #endif
1178         
1179         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
1180         sAspectRatio.setVisible(true);
1181
1182         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1183         if (osdv)
1184         {
1185           osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
1186         }
1187
1188
1189         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1190         {
1191           osd.draw();
1192           BoxStack::getInstance()->update(this, osd.getRegion());
1193         }
1194                 
1195         break;
1196       }
1197       case PlayerLiveTV::PREBUFFERING:
1198       {
1199         preBuffering = m->tag;
1200         Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);
1201         bufferBar.setPercent(preBuffering);
1202
1203         if (osd.getVisible())
1204         {
1205           bufferBar.setVisible(true);
1206           bufferBar.draw();
1207           Region r;
1208           bufferBar.getRootBoxRegion(&r);                       ///////// FIXME !!!
1209           BoxStack::getInstance()->update(this, &r);
1210
1211           if (preBuffering == 100)
1212           {
1213             doAudioChannelSymbol();
1214           }
1215         }
1216       }
1217     }
1218   }
1219 #ifdef VOMP_PLATTFORM_MVP
1220 // See vepg.cc ~ line 480
1221   else if (m->message == Message::HACK_MVP_RETURN_FROM_QUARTER)
1222   {
1223     video->setMode(videoMode);
1224   }
1225 #endif
1226 }
1227
1228 void VVideoLiveTV::doAudioChannelSymbol()
1229 {
1230   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1231   // get the doobery
1232   Channel* currentChannel = (*chanList)[osdChannelIndex];
1233     
1234   bool multiAudio = false;
1235   if (Audio::getInstance()->supportsAc3()) {
1236           if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true;
1237   }
1238   if (currentChannel->numAPids > 1) multiAudio = true;
1239   
1240   // draw the doobery
1241   if (!osdv) {
1242           if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1243           else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1244           sAudioChannels.setVisible(true);
1245   }
1246   if (osdv)
1247   {
1248           dolbylogo.setVisible(true);
1249           txtlogo.setVisible(true);
1250           TVMediaInfo info;
1251           if (currentChannel->tpid!=0) info.setStaticArtwork(sa_txton);
1252           else info.setStaticArtwork(sa_txtoff);
1253           txtlogo.setTVMedia(info, WTVMedia::ZoomVertical);
1254
1255
1256           if (Audio::getInstance()->supportsAc3() && currentChannel->numDPids>1)
1257                   info.setStaticArtwork(sa_dolbyon);
1258           else info.setStaticArtwork(sa_dolbyoff);
1259           dolbylogo.setTVMedia(info, WTVMedia::ZoomVertical);
1260
1261          Demuxer* demux=Demuxer::getInstance();
1262          if (demux) {
1263                  int vertical_size=demux->getVerticalSize();
1264                  Log::getInstance()->log("VVideoRec", Log::DEBUG, "TVMedia vertical size %d", vertical_size);
1265                  reslogo.setVisible(true);
1266                  switch (vertical_size) {
1267                  case 576:
1268                          info.setStaticArtwork(sa_sd576i);
1269                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1270                  case 720:
1271                          info.setStaticArtwork(sa_hd720p);
1272                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1273                  case 1080:
1274                          info.setStaticArtwork(sa_hd1080i);
1275                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1276                  default: // we assume sd
1277                          reslogo.setVisible(false);
1278                  };
1279
1280          } else {
1281                  reslogo.setVisible(false);
1282          }
1283
1284
1285   }
1286   
1287   if (osd.getVisible())
1288   {
1289     sAudioChannels.draw();
1290     Region r;
1291     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!
1292     // Fix this n'all.
1293     r.w = 32;
1294     r.h = 16;
1295     BoxStack::getInstance()->update(this, &r);
1296   }
1297 }
1298
1299 UINT VVideoLiveTV::upChannel(UINT index)
1300 {
1301   if (index == (chanList->size() - 1)) // at the end
1302     return 0; // so go to start
1303   else
1304     return index + 1;
1305 }
1306
1307 UINT VVideoLiveTV::downChannel(UINT index)
1308 {
1309   if (index == 0) // at the start
1310     return chanList->size() - 1; // so go to end
1311   else
1312     return index - 1;
1313 }
1314
1315 void VVideoLiveTV::toggleChopSides()
1316 {
1317   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1318
1319   if (videoMode == Video::NORMAL)
1320   {
1321     videoMode = Video::LETTERBOX;
1322     video->setMode(Video::LETTERBOX);
1323   }
1324   else
1325   {
1326     videoMode = Video::NORMAL;
1327     video->setMode(Video::NORMAL);
1328   }
1329 }
1330
1331 void VVideoLiveTV::updatePosterBanner()
1332 {
1333         OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1334         if (!osdv) {
1335                 osdposterbanner.setVisible(false);
1336                 return;
1337         }
1338         Event* toShow = NULL;
1339         if (eventList && eventList->size()) {
1340                 toShow = (*eventList)[0];
1341                 Channel* currentChannel = (*chanList)[osdChannelIndex];
1342                 toShow->loadinfos(currentChannel->number);
1343         }
1344
1345         if (toShow)
1346         {
1347                 TVMedia poster;
1348                 poster.height=0;
1349                 bool posterscale=true;
1350                 if (toShow->movieInfo) {
1351                         poster=toShow->movieInfo->poster;
1352                 }
1353                 if (toShow->seriesInfo) {
1354                         if (toShow->seriesInfo->banners.size()) {
1355                                 poster=toShow->seriesInfo->banners[0];
1356                                 posterscale=false;
1357                         } else if (toShow->seriesInfo->seasonposter.height) {
1358                                 poster=toShow->seriesInfo->seasonposter;
1359                         } else if (toShow->seriesInfo->posters.size()) {
1360                                 poster=toShow->seriesInfo->posters[0];
1361                         }
1362                 }
1363                 if (poster.height) {
1364                         osdposterbanner.setTVMedia(poster.info, posterscale ? WTVMedia::ZoomVertical : WTVMedia::ZoomHorizontal);
1365                         osdposterbanner.setVisible(true);
1366                 } else {
1367                         osdposterbanner.setVisible(false);
1368                 }
1369         } else {
1370                 osdposterbanner.setVisible(false);
1371         }
1372
1373 }
1374
1375
1376 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1377 {
1378   drawBitmap(posX, posY, bm,region);
1379   Region r;
1380   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1381   boxstack->update(this, &r);
1382 }
1383
1384 void VVideoLiveTV::clearOSD()
1385 {
1386   rectangle(area, DrawStyle(0,0,0,0));
1387   boxstack->update(this, &area);
1388 }
1389
1390 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1391 {
1392   Region r;
1393   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1394   //now convert to our display
1395   float scalex=720.f/((float) (region.framewidth+1));
1396   float scaley=576.f/((float) (region.frameheight+1));
1397   r.x = (UINT)floor(scalex*((float)r.x));
1398   r.y = (UINT)floor(scaley*((float)r.y));
1399   r.w = (UINT)(ceil(scalex*((float)r.w))+1.f);
1400   r.h = (UINT)(ceil(scaley*((float)r.h))+1.f);
1401   rectangle(r, DrawStyle(0,0,0,0));
1402   boxstack->update(this, &r);
1403 }