]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Buffering progress bar
[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
42 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
43 {
44   vdr = VDR::getInstance();
45   boxstack = BoxStack::getInstance();
46   video = Video::getInstance();
47   
48   vas = NULL;
49
50   chanList = tchanList;
51   vchannelList = tvchannelList;
52   numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
53
54   currentChannelIndex = 0;
55   previousChannelIndex = 0;
56   osdChannelIndex = 0;
57   keying = 0;
58
59   playing = false;
60
61   // Convert channel number to index
62   UINT i;
63   for(i = 0; i < chanList->size(); i++)
64   {
65     if ((*chanList)[i]->number == (UINT)initialChannelNumber)
66     {
67       currentChannelIndex = i;
68       osdChannelIndex = i;
69       break;
70     }
71   }
72
73   eventList = NULL;
74
75   videoMode = video->getMode();
76   
77   if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
78   {
79     streamType = VDR::VIDEO;
80     player = new PlayerLiveTV(Command::getInstance(), this, chanList);
81   }
82   else
83   {
84     streamType = VDR::RADIO;
85     player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
86   }
87   player->init();
88
89   setSize(video->getScreenWidth(), video->getScreenHeight());
90   createBuffer();
91   Colour transparent(0, 0, 0, 0);
92   setBackgroundColour(transparent);
93
94   dowss = false;
95   char* optionWSS = vdr->configLoad("General", "WSS");
96   if (optionWSS)
97   {
98     if (strstr(optionWSS, "Yes")) dowss = true;
99     delete[] optionWSS;
100   }
101   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
102
103   if (dowss)
104   {
105     wss.setFormat(video->getFormat());
106     wss.setWide(true);
107     add(&wss);
108     
109     wssRegion.x = 0;
110     wssRegion.y = 6;
111     wssRegion.w = video->getScreenWidth();
112     wssRegion.h = 2;
113   }
114   
115   Colour osdBack = Colour(0, 0, 0, 128);
116   
117   osd.setBackgroundColour(osdBack);
118   osd.setPosition(0, video->getScreenHeight() - 150);
119   osd.setSize(video->getScreenWidth(), 150);
120   osd.setVisible(false);
121   add(&osd);
122   
123   clock.setBackgroundColour(osdBack);
124   clock.setPosition(osd.getWidth() - 100, 4);
125   clock.setSize(90, 30);
126   osd.add(&clock);
127
128   osdChanNum.setBackgroundColour(osdBack);
129   osdChanNum.setPosition(40, 4);
130   osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
131   osd.add(&osdChanNum);  
132
133   osdChanName.setBackgroundColour(osdBack);
134   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
135   osdChanName.setSize(300, 30);
136   osd.add(&osdChanName);
137   
138   boxRed.setBackgroundColour(Colour::RED);
139   boxRed.setPosition(50, 104);
140   boxRed.setSize(18, 16);
141   osd.add(&boxRed);
142
143   boxGreen.setBackgroundColour(Colour::GREEN);
144   boxGreen.setPosition(220, 104);
145   boxGreen.setSize(18, 16);
146   osd.add(&boxGreen);
147
148   boxYellow.setBackgroundColour(Colour::YELLOW);
149   boxYellow.setPosition(390, 104);
150   boxYellow.setSize(18, 16);
151   osd.add(&boxYellow);
152
153   boxBlue.setBackgroundColour(Colour::BLUE);
154   boxBlue.setPosition(560, 104);
155   boxBlue.setSize(18, 16);
156   osd.add(&boxBlue);  
157   
158   textRed.setBackgroundColour(osdBack);
159   textRed.setPosition(boxRed.getX()+18, 98);
160   textRed.setSize(120, 30);
161   textRed.setText("Summary");
162   osd.add(&textRed);  
163     
164   if (streamType == VDR::VIDEO)
165   {
166     textGreen.setBackgroundColour(osdBack);
167     textGreen.setPosition(boxGreen.getX()+18, 98);
168     textGreen.setSize(120, 30);
169     textGreen.setText("Audio");
170     osd.add(&textGreen);  
171   }
172     
173   textYellow.setBackgroundColour(osdBack);
174   textYellow.setPosition(boxYellow.getX()+18, 98);
175   textYellow.setSize(120, 30);
176   textYellow.setText("");
177   osd.add(&textYellow);  
178     
179   textBlue.setBackgroundColour(osdBack);
180   textBlue.setPosition(boxBlue.getX()+18, 98);
181   textBlue.setSize(90, 30);
182   textBlue.setText("EPG");
183   osd.add(&textBlue);  
184     
185   sl.setBackgroundColour(osdBack);
186   sl.setPosition(70, 36);
187   sl.setSize(500, 58);
188   sl.setNoLoop();
189   osd.add(&sl);
190   
191   // Summary Box
192   summary.setBackgroundColour(osdBack);
193   summary.setPosition(0, video->getScreenHeight() - 300);
194   summary.setSize(video->getScreenWidth(), 150);
195   summary.setVisible(false);
196   add(&summary);  
197   
198   textSummary.setBackgroundColour(osdBack);
199   textSummary.setPosition(40, 10);
200   textSummary.setSize(video->getScreenWidth() - 80, 130);
201   textSummary.setParaMode(true);
202   summary.add(&textSummary);
203   
204   summaryBlackLine.setBackgroundColour(Colour::BLACK);
205   summaryBlackLine.setPosition(0, summary.getHeight() - 4);
206   summaryBlackLine.setSize(summary.getWidth(), 4);
207   summary.add(&summaryBlackLine);
208   
209   sAspectRatio.setPosition(osd.getWidth() - 90, clock.getY2()+4);
210   sAspectRatio.nextColour = Colour::YELLOW;
211   sAspectRatio.setVisible(false);
212   osd.add(&sAspectRatio);
213   
214   bufferBar.setPosition(osd.getWidth() - 94, 70);
215   bufferBar.setSize(50, 20);
216   bufferBar.setVisible(true);
217   osd.add(&bufferBar);
218   
219   // FIXME painful
220   Region r1 = summary.getRegionR();
221   Region r2 = osd.getRegionR();
222   osdSummaryRegion = r1 + r2;
223 }
224
225 VVideoLiveTV::~VVideoLiveTV()
226 {
227   if (playing) stop();
228
229   delete player;
230   video->setDefaultAspect();
231   delData();
232 }
233
234 void VVideoLiveTV::delData()
235 {
236   if (eventList)
237   {
238     int eventListSize = eventList->size();
239     for(int i = 0; i < eventListSize; i++)
240     {
241       delete (*eventList)[i];
242     }
243     eventList->clear();
244     delete eventList;
245
246   }
247   sl.clear();
248 }
249
250 int VVideoLiveTV::handleCommand(int command)
251 {
252   switch(command)
253   {
254     case Remote::BACK:
255     {
256       if (osd.getVisible())
257       {
258         clearScreen();
259         return 2;
260       }
261       // else drop through to stop
262     }
263     case Remote::STOP:
264     {
265       stop();
266       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
267       return 4;
268     }
269     case Remote::UP:
270     {
271       // New remote only
272       // epg data up
273       doUpDown(false);
274       return 2;
275     }
276     case Remote::DOWN:
277     {
278       // New remote only
279       // epg data down
280       doUpDown(true);
281       return 2;
282     }
283     case Remote::LEFT:
284     {
285       // New remote only
286       // epg data ch down
287       doLeftRight(false);
288       return 2;
289     }
290     case Remote::RIGHT:
291     {
292       // New remote only
293       // epg data ch up
294       doLeftRight(true);
295       return 2;
296     }
297     case Remote::DF_UP:
298     case Remote::CHANNELUP:
299     {
300       doChanUpDown(UP);
301       return 2;
302     }
303     case Remote::DF_DOWN:
304     case Remote::CHANNELDOWN:
305     {
306       doChanUpDown(DOWN);
307       return 2;
308     }
309     case Remote::PREVCHANNEL:
310     {
311       channelChange(PREVIOUS, 0);
312       return 2;
313     }
314     case Remote::OK:
315     {
316       doOK();
317       return 2;
318     }
319     case Remote::RED:
320     case Remote::MENU:
321     {
322       doSummary();
323       return 2;
324     }
325     case Remote::FULL:
326     case Remote::TV:
327     {
328       toggleChopSides();
329       return 2;
330     }
331
332     case Remote::ZERO:
333     case Remote::ONE:
334     case Remote::TWO:
335     case Remote::THREE:
336     case Remote::FOUR:
337     case Remote::FIVE:
338     case Remote::SIX:
339     case Remote::SEVEN:
340     case Remote::EIGHT:
341     case Remote::NINE:
342     {
343       // key in channel number
344       doKey(command);
345       return 2;
346     }
347
348     case Remote::GREEN:
349     {
350       if (streamType == VDR::VIDEO) doAudioSelector();
351       return 2;   
352     }
353     case Remote::YELLOW:
354     {
355       return 2;
356     }
357     case Remote::GUIDE:
358     case Remote::BLUE:
359     {
360       doEPG();
361       return 2;
362     }
363   }
364
365   return 1;
366 }
367
368 void VVideoLiveTV::go()
369 {
370   playing = true;
371   draw();
372   boxstack->update(this);
373
374   setClock();
375   displayOSD(true);
376   
377   player->go(currentChannelIndex);
378 }
379
380 void VVideoLiveTV::stop()
381 {
382   Timers::getInstance()->cancelTimer(this, 1);
383   Timers::getInstance()->cancelTimer(this, 2);
384   player->stop();
385   playing = false;
386 }
387
388 void VVideoLiveTV::doLeftRight(bool right)
389 {
390   if (osd.getVisible())
391   {
392     if (right) osdChannelIndex = upChannel(osdChannelIndex);
393     else       osdChannelIndex = downChannel(osdChannelIndex);
394   }
395   else
396   {
397     osdChannelIndex = currentChannelIndex;
398   }
399   displayOSD(true);
400 }
401
402 void VVideoLiveTV::doUpDown(bool down)
403 {
404   if (osd.getVisible())
405   {
406     if (down) sl.down();
407     else      sl.up();
408     sl.draw();
409     
410     displayOSD(false);
411   }
412   else
413   {
414     displayOSD(true);
415   }
416 }
417
418 void VVideoLiveTV::doChanUpDown(int which)
419 {
420   channelChange(OFFSET, which);
421   osdChannelIndex = currentChannelIndex;
422   displayOSD(true);
423 }
424
425 void VVideoLiveTV::doOK()
426 {
427   if (osd.getVisible())
428   {
429     if (keying)
430     {
431       UINT newChannel = 0;
432       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
433       
434       channelChange(NUMBER, newChannel);
435       osdChannelIndex = currentChannelIndex;
436       displayOSD(true);
437     }
438     else if (osdChannelIndex == currentChannelIndex)
439     {
440       clearScreen();
441     }
442     else
443     {
444       channelChange(INDEX, osdChannelIndex);
445       displayOSD(true);
446     }
447   }
448   else
449   {
450     osdChannelIndex = currentChannelIndex;
451     displayOSD(true);
452   }
453 }
454
455 void VVideoLiveTV::doSummary()
456 {
457   if (summary.getVisible())
458   {
459     summary.setVisible(false);
460     draw();
461     boxstack->update(this, summary.getRegion());
462     Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
463     return;
464   }
465
466   summary.setVisible(true);
467
468   if (osd.getVisible())
469   {
470     Timers::getInstance()->cancelTimer(this, 1);
471     displayOSD(false);
472   }
473   else
474   {
475     displayOSD(true);
476   }
477 }
478
479 void VVideoLiveTV::doKey(int command)
480 {
481   if (!osd.getVisible()) // First key. prep the data
482   {
483     setNowNextData();
484     keying = 0;    
485   }
486
487   int i;
488   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
489   keyingInput[0] = command;
490   keying++;
491
492   char* keyingString = new char[numberWidth + 1];
493   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
494   keyingString[numberWidth] = '\0';
495
496   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
497   
498   if (keying == numberWidth)
499   {
500     UINT newChannel = 0;
501     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
502     
503     channelChange(NUMBER, newChannel);
504     osdChannelIndex = currentChannelIndex;
505     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
506     displayOSD(true); // this will put one back if required
507   }
508   else
509   {
510     osdChanNum.setText(keyingString);
511
512     if (!osd.getVisible())
513     {
514       osd.setVisible(true);
515       draw();
516     }
517     else
518     {
519       osdChanNum.draw();
520     }
521     boxstack->update(this, osd.getRegion());
522     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
523   }
524   delete[] keyingString;
525 }
526
527 void VVideoLiveTV::doAudioSelector()
528 {
529   // If the osd is already visisble there might be a timer for it
530   Timers::getInstance()->cancelTimer(this, 1);
531
532   // Cancel keying
533   if (keying)
534   {
535     keying = 0;
536     // and reset the display - this is a copy from setNowNextData
537     char formatChanNum[20];
538     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
539     osdChanNum.setText(formatChanNum);
540     osdChanName.setText((*chanList)[osdChannelIndex]->name);
541   }
542
543   // Draw the selector
544   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel());
545   Colour osdBack = Colour(0, 0, 0, 128);
546   vas->setBackgroundColour(osdBack);
547   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
548   vas->draw();
549
550   // make vas != null and displayOSD will not set a timer or do any boxstack update
551   summary.setVisible(false);
552   if (osd.getVisible()) displayOSD(false);
553   else displayOSD(true);
554   draw();
555
556   BoxStack::getInstance()->add(vas);
557   BoxStack::getInstance()->update(this);        
558   BoxStack::getInstance()->update(vas);     
559 }      
560       
561 void VVideoLiveTV::doEPG()
562 {
563   if (osd.getVisible()) clearScreen();
564
565   video->setMode(Video::QUARTER);
566   video->setPosition(170, 5); //TODO need to deal with 4:3 switching
567
568   VEpg* vepg = new VEpg(this, currentChannelIndex, VDR::VIDEO);
569   vepg->draw();
570   boxstack->add(vepg);
571   boxstack->update(vepg);
572 }
573
574 void VVideoLiveTV::setNowNextData()
575 {
576   delData();
577   
578   Channel* currentChannel = (*chanList)[osdChannelIndex];
579
580   char formatChanNum[20];
581   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
582   osdChanNum.setText(formatChanNum);
583   osdChanName.setText(currentChannel->name);
584
585   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
586
587   if (!eventList)
588   {
589     sl.addOption(tr("No channel data available"), 0, 1);
590   }
591   else
592   {
593     sort(eventList->begin(), eventList->end(), EventSorter());
594
595     char tempString[300];
596     char tempString2[300];
597     struct tm* btime;
598     Event* event;
599     int eventListSize = eventList->size();
600     for(int i = 0; i < eventListSize; i++)
601     {
602       event = (*eventList)[i];
603
604       //btime = localtime((time_t*)&event->time);
605       time_t etime = event->time;
606       btime = localtime(&etime);
607 #ifndef _MSC_VER
608       strftime(tempString2, 299, "%0H:%0M ", btime);
609 #else
610       strftime(tempString2, 299, "%H:%M ", btime);
611 #endif
612       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
613       
614       sl.addOption(tempString, (ULONG)event, (i==0));
615     }
616   }
617 }
618
619 void VVideoLiveTV::setSummaryData()
620 {
621   // If osd is not being displayed, sl will be filled with now, current channel
622   // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
623   Event* selectedEvent = (Event*)sl.getCurrentOptionData();
624   
625   if (!selectedEvent)
626   {
627     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");  
628     textSummary.setText(tr("No summary available"));
629   }
630   else
631   {
632     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);  
633     textSummary.setText(selectedEvent->description);
634   }
635 }
636
637 void VVideoLiveTV::displayOSD(bool newNowNextData)
638 {
639   osd.setVisible(true);
640   if (newNowNextData)
641   {
642     setNowNextData();
643     keying = 0;
644   }
645   osd.draw();
646   
647   if (summary.getVisible())
648   {
649     setSummaryData();
650     summary.draw();
651     boxstack->update(this, &osdSummaryRegion);
652   }
653   else if (!vas)
654   {
655     boxstack->update(this, osd.getRegion());
656     Timers::getInstance()->setTimerD(this, 1, 4);
657   }
658 }
659
660 void VVideoLiveTV::clearScreen()
661 {  
662   if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
663
664   osd.setVisible(false);
665
666   if (summary.getVisible())
667   {
668     summary.setVisible(false);
669     draw();
670     boxstack->update(this, &osdSummaryRegion);
671   }
672   else
673   {
674     draw();
675     boxstack->update(this, osd.getRegion());
676   }
677 }
678
679 void VVideoLiveTV::setClock()
680 {
681   char timeString[20];
682   time_t t;
683   time(&t);
684   struct tm* tms = localtime(&t);
685   strftime(timeString, 19, "%H:%M", tms);
686   clock.setText(timeString);
687
688   time_t dt = 60 - (t % 60);  // seconds to the next minute
689   if (dt == 0) dt = 60; // advance a whole minute if necessary
690   dt += t;  // get a time_t value for it rather than using duration
691   // (so it will occur at the actual second and not second and a half)
692
693   Timers::getInstance()->setTimerT(this, 2, dt);
694 }
695
696 void VVideoLiveTV::timercall(int ref)
697 {
698   if (ref == 1)
699   {
700     if (keying)
701     {
702       // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
703       
704       UINT newChannel = 0;
705       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
706       
707       Message* m = new Message();
708       m->message = Message::CHANNEL_CHANGE;
709       m->to = this;
710       m->parameter = newChannel;
711       m->tag = 1; // signal to call displayOSD();
712       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
713     }
714     else
715     {
716       osd.setVisible(false);
717       draw();
718       Message* m = new Message();
719       m->message = Message::REDRAW;
720       m->to = BoxStack::getInstance();
721       m->from = this;
722       m->parameter = (ULONG)osd.getRegion();
723       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
724     }
725   }
726   else if (ref == 2)
727   {
728     setClock();
729     if (osd.getVisible())
730     {
731       clock.draw();
732       Message* m = new Message();
733       m->message = Message::REDRAW;
734       m->to = BoxStack::getInstance();
735       m->from = this;
736       m->parameter = (ULONG)osd.getRegion();
737       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.    
738     }
739   }
740 }
741
742 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
743 {
744   UINT newChannel = 0;
745
746   if (changeType == INDEX)
747   {
748     newChannel = newData;
749   }
750   else if (changeType == NUMBER)
751   {
752     UINT i;
753     for(i = 0; i < chanList->size(); i++)
754     {
755       if ((*chanList)[i]->number == (UINT)newData)
756       {
757         newChannel = i;
758         break;
759       }
760     }
761
762     if (i == chanList->size())
763     {
764       // no such channel
765       return false;
766     }
767   }
768   else if (changeType == OFFSET)
769   {
770     if (newData == UP) newChannel = upChannel(currentChannelIndex);
771     else newChannel = downChannel(currentChannelIndex);
772   }
773   else if (changeType == PREVIOUS)
774   {
775     newChannel = previousChannelIndex;
776   }
777   else
778   {
779     return false; // bad input
780   }
781
782   if (newChannel == currentChannelIndex) return true;
783
784   previousChannelIndex = currentChannelIndex;
785   currentChannelIndex = newChannel;
786   
787   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
788   player->setChannel(currentChannelIndex);
789   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
790
791   // Blank out the symbols
792   sAspectRatio.setVisible(false);
793   bufferBar.setPercent(0);
794   
795   return true;
796 }
797
798 void VVideoLiveTV::processMessage(Message* m)
799 {
800   if (m->message == Message::MOUSE_LBDOWN)
801   {
802     //check if press is outside this view! then simulate cancel
803     int x=(m->parameter>>16)-osd.getScreenX();
804     int y=(m->parameter&0xFFFF)-osd.getScreenY();
805     if (osd.getVisible()) {
806     
807         if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
808             (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
809             BoxStack::getInstance()->handleCommand(Remote::RED);
810         } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
811             (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
812             BoxStack::getInstance()->handleCommand(Remote::GREEN);
813         } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
814             (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
815             BoxStack::getInstance()->handleCommand(Remote::YELLOW);
816         } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
817             (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
818             BoxStack::getInstance()->handleCommand(Remote::BLUE);
819         } else {
820             BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
821         }
822
823     } else {
824         BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
825     }
826   }
827   else if (m->message == Message::CHANNEL_CHANGE)
828   {
829     channelChange(NUMBER, m->parameter);
830     osdChannelIndex = currentChannelIndex;
831     if (m->tag == 1) displayOSD(true);
832   }
833   else if (m->message == Message::EPG_CLOSE)
834   {
835     video->setMode(videoMode);
836   }
837   else if (m->message == Message::CHILD_CLOSE)
838   {
839     if (m->from == vas)
840     {
841       vas = NULL;
842       displayOSD(false);
843     }
844   }
845   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
846   {
847     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
848     player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16);
849   }
850   else if (m->message == Message::PLAYER_EVENT)
851   {
852     switch(m->parameter)
853     {
854       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
855       {
856         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
857         // I can't handle this, send it to command
858         Message* m2 = new Message();
859         m2->to = Command::getInstance();
860         m2->message = Message::CONNECTION_LOST;
861         Command::getInstance()->postMessageNoLock(m2);
862         break;
863       }
864       
865       /*
866       case PlayerLiveTV::STREAM_END:
867       {
868         // I can't handle this, send it to command - improve this
869         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
870         m2->to = Command::getInstance();
871         m2->message = Message::STREAM_END;
872         Command::getInstance()->postMessageNoLock(m2);
873         break;
874       }
875       */
876       
877       case PlayerLiveTV::ASPECT43:
878       {
879         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
880         {
881           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
882           wss.setWide(false);
883           wss.draw();
884           BoxStack::getInstance()->update(this, &wssRegion);
885         }
886         
887         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
888         sAspectRatio.setVisible(true);
889         
890         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
891         {
892           osd.draw();
893           BoxStack::getInstance()->update(this, osd.getRegion());
894         }
895         
896         break;
897       }
898       case PlayerLiveTV::ASPECT169:
899       {
900         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
901         {
902           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
903           wss.setWide(true);
904           wss.draw();
905           BoxStack::getInstance()->update(this, &wssRegion);
906         }
907         
908         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
909         sAspectRatio.setVisible(true);
910
911         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
912         {
913           osd.draw();
914           BoxStack::getInstance()->update(this, osd.getRegion());
915         }
916                 
917         break;
918       }
919       case PlayerLiveTV::PREBUFFERING:
920       {
921         Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", m->tag);
922         bufferBar.setVisible(true);
923         bufferBar.setPercent(m->tag);
924         bufferBar.draw();
925
926         if (osd.getVisible())
927         {
928           Region r;
929           bufferBar.getRootBoxRegion(&r);
930           BoxStack::getInstance()->update(this, &r);
931         }
932       }
933     }
934   }
935 }
936
937 UINT VVideoLiveTV::upChannel(UINT index)
938 {
939   if (index == (chanList->size() - 1)) // at the end
940     return 0; // so go to start
941   else
942     return index + 1;
943 }
944
945 UINT VVideoLiveTV::downChannel(UINT index)
946 {
947   if (index == 0) // at the start
948     return chanList->size() - 1; // so go to end
949   else
950     return index - 1;
951 }
952
953 void VVideoLiveTV::toggleChopSides()
954 {
955   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
956
957   if (videoMode == Video::NORMAL)
958   {
959     videoMode = Video::LETTERBOX;
960     video->setMode(Video::LETTERBOX);
961   }
962   else
963   {
964     videoMode = Video::NORMAL;
965     video->setMode(Video::NORMAL);
966   }
967 }
968