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