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