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