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