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