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