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