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