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