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