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