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