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