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