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