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