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