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