]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Add some BoxStack constants for readability
[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
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 BoxStack::COMMAND_HANDLED;
319       }
320       // else drop through to stop
321     }
322     FALLTHROUGH
323     case Input::STOP:
324     {
325       stop();
326       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
327       return BoxStack::DELETE_ME;
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 BoxStack::COMMAND_HANDLED;
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 BoxStack::COMMAND_HANDLED;
356     }
357     case Input::LEFT:
358     {
359       doLeftRight(false);
360       return BoxStack::COMMAND_HANDLED;
361     }
362     case Input::RIGHT:
363     {
364       doLeftRight(true);
365       return BoxStack::COMMAND_HANDLED;
366     }
367     case Input::CHANNELUP:
368     {
369       doChanUpDown(UP);
370       return BoxStack::COMMAND_HANDLED;
371     }
372     case Input::CHANNELDOWN:
373     {
374       doChanUpDown(DOWN);
375       return BoxStack::COMMAND_HANDLED;
376     }
377     case Input::PREVCHANNEL:
378     {
379       channelChange(PREVIOUS, 0);
380       osdChannelIndex = currentChannelIndex;
381       displayOSD(true);
382       return BoxStack::COMMAND_HANDLED;
383     }
384     case Input::OK:
385     {
386       doOK();
387       return BoxStack::COMMAND_HANDLED;
388     }
389     case Input::RED:
390     case Input::MENU:
391     {
392       doSummary();
393       return BoxStack::COMMAND_HANDLED;
394     }
395     case Input::FULL:
396     case Input::TV:
397     {
398       toggleChopSides();
399       return BoxStack::COMMAND_HANDLED;
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 BoxStack::COMMAND_HANDLED;
416     }
417
418     case Input::GREEN:
419     {
420       if (streamType == VDR::VIDEO) doAudioSelector();
421       return BoxStack::COMMAND_HANDLED;
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 BoxStack::COMMAND_HANDLED;
430     }
431     case Input::GUIDE:
432     case Input::BLUE:
433     {
434       doEPG();
435       return BoxStack::COMMAND_HANDLED;
436     }
437     case Input::RECORD:
438       if (streamType == VDR::VIDEO)
439         (static_cast<PlayerVideoLive*>(player))->toggleSubtitles();
440       return BoxStack::COMMAND_HANDLED;
441   }
442
443   return BoxStack::ABANDON_COMMAND;
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] * static_cast<int>(pow(10.f, 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] = static_cast<char>(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] * static_cast<int>(pow(10.f, 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.fillColour(DrawStyle::VIEWBACKGROUND);
595       osdChanNum.draw();
596     }
597     boxstack->update(this, osd.getRegion());
598     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
599   }
600   delete[] keyingString;
601 }
602
603 void VVideoLiveTV::doTeletext(bool subtitlemode)
604 {
605   if (streamType != VDR::VIDEO) return;
606
607   PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
608
609   playervl->tellSubtitlesOSDVisible(true);
610   bool exists = true;
611
612   // Cancel keying
613   if (keying)
614   {
615     keying = 0;
616     // and reset the display - this is a copy from setNowNextData
617     char formatChanNum[20];
618     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
619     osdChanNum.setText(formatChanNum);
620     osdChanName.setText((*chanList)[osdChannelIndex]->name);
621   }
622   playervl->tellSubtitlesOSDVisible(true);
623   if (osd.getVisible()) clearScreen();
624   // Draw the teletxt
625   VTeletextView* vtxv = playervl->getTeletextDecoder()->getTeletxtView();
626   if (vtxv == NULL)
627   {
628     vtxv = new VTeletextView(playervl->getTeletextDecoder(), this, playervl);
629     playervl->getTeletextDecoder()->registerTeletextView(vtxv);
630     exists=false;
631   }
632
633   vtxv->setSubtitleMode(subtitlemode);
634   vtxv->draw();
635   draw();
636   
637   if (!exists)
638   {
639     BoxStack::getInstance()->add(vtxv);
640   }
641
642   BoxStack::getInstance()->update(this);
643   BoxStack::getInstance()->update(vtxv); 
644 }
645
646 void VVideoLiveTV::doAudioSelector()
647 {
648   // Only called if streamtype == VDR::VIDEO
649   PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
650
651   // If the osd is already visisble there might be a timer for it
652   Timers::getInstance()->cancelTimer(this, 1);
653   //This causes a deadlock with the timertrhread itself is locked
654
655   // Cancel keying
656   if (keying)
657   {
658     keying = 0;
659     // and reset the display - this is a copy from setNowNextData
660     char formatChanNum[20];
661     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
662     osdChanNum.setText(formatChanNum);
663     osdChanName.setText((*chanList)[osdChannelIndex]->name);
664   }
665   int subtitleChannel = playervl->getCurrentSubtitleChannel();
666   int subtitleType = 0x10;
667   if (!playervl->isSubtitlesOn())
668   {
669     if (playervl->getTeletextDecoder()->getTeletxtView() &&
670         playervl->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode())
671     {
672       subtitleChannel = playervl->getTeletextDecoder()->getPage();
673       subtitleType = 0x11;
674     }
675     else
676     {
677       subtitleType = 0xFF; //turnedOff
678       subtitleChannel = 0;
679     }
680   }
681
682   // Draw the selector
683   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], playervl->getCurrentAudioChannel(),
684       subtitleType,subtitleChannel,NULL);
685
686   vas->setBackgroundColour(DrawStyle::OSDBACKGROUND);
687   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
688   vas->draw();
689
690   // make vas != null and displayOSD will not set a timer or do any boxstack update
691   if (osd.getVisible()) displayOSD(false);
692   else displayOSD(true);
693   draw();
694
695   BoxStack::getInstance()->add(vas);
696   BoxStack::getInstance()->update(this);        
697   BoxStack::getInstance()->update(vas);     
698 }      
699       
700 void VVideoLiveTV::doEPG()
701 {
702   if (osd.getVisible()) clearScreen();
703
704   if (!Control::getInstance()->isAdvMenus())
705   {
706     VEpg* vepg = new VEpg(this, currentChannelIndex, chanList);
707     vepg->draw();
708     boxstack->add(vepg);
709     boxstack->update(vepg);
710   }
711   else
712   {
713     Channel* currentChannel = (*chanList)[osdChannelIndex];
714     VEpgListAdvanced  *vepg= new VEpgListAdvanced(this, chanList, currentChannel->number);
715     vepg->draw();
716     boxstack->add(vepg);
717     boxstack->update(vepg);
718   }
719 }
720
721 void VVideoLiveTV::setNowNextData()
722 {
723   delData();
724   
725   Channel* currentChannel = (*chanList)[osdChannelIndex];
726
727   char formatChanNum[20];
728   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
729   osdChanNum.setText(formatChanNum);
730   osdChanName.setText(currentChannel->name);
731
732   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
733
734   if (!eventList)
735   {
736     sl.setText(tr("No channel data available"));
737   }
738   else
739   {
740     sort(eventList->begin(), eventList->end(), EventSorter());
741
742     char tempString[300];
743     struct tm btime;
744     Event* event;
745     int eventListSize = eventList->size();
746     std::stringstream ss;
747     for(int i = 0; i < eventListSize; i++)
748     {
749       event = (*eventList)[i];
750
751       time_t etime = event->time;
752       LOCALTIME_R(&etime, &btime);
753       // FIXME convert following to std::strftime
754 #ifndef _MSC_VER
755       strftime(tempString, 299, "%0H:%0M ", &btime);
756 #else
757       strftime(tempString, 299, "%H:%M ", &btime);
758 #endif
759       ss << tempString << " " << event->title << "\n";
760     }
761     sl.setText(ss.str().c_str());
762   }
763 }
764
765
766
767 void VVideoLiveTV::displayOSD(bool newNowNextData)
768 {
769   PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
770   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(true);
771   osd.setVisible(true);
772   if (newNowNextData)
773   {
774     setNowNextData();
775     updatePosterBanner();
776     keying = 0;
777   }
778   osd.draw();
779   if (osdposterbanner.getVisible()) osdposterbanner.draw();
780   
781
782   Region  toupdate;
783
784   toupdate=*osd.getRegion();
785
786
787   if (osdposterbanner.getVisible())
788   {
789     boxstack->update(this);
790   }
791   else
792   {
793     boxstack->update(this, &toupdate);
794   }
795   
796   bool setTimer = true;
797   if (vas) setTimer = false;
798   if (textUnavailable.getVisible()) setTimer = false;
799
800   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
801 }
802
803 void VVideoLiveTV::clearScreen()
804 {  
805   Timers::getInstance()->cancelTimer(this, 1);
806
807   textUnavailable.setVisible(false);
808   osd.setVisible(false);
809   osdposterbanner.setVisible(false);
810
811   okTriggeredOSD = false;
812
813   draw();
814   boxstack->update(this);
815
816   PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
817   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
818 }
819
820 void VVideoLiveTV::showUnavailable()
821 {
822   LogNT::getInstance()->debug(TAG, "Show unavailable called");
823   textUnavailable.setVisible(true);
824   textUnavailable.draw();
825   
826   if (!osd.getVisible()) displayOSD(true);
827
828   boxstack->update(this, textUnavailable.getRegion());  
829 }
830
831 void VVideoLiveTV::setClock()
832 {
833   char timeString[20];
834   time_t t;
835   time(&t);
836   struct tm tms;
837   LOCALTIME_R(&t, &tms);
838   strftime(timeString, 19, "%H:%M", &tms);
839   clock.setText(timeString);
840
841   time_t dt = 60 - (t % 60);  // seconds to the next minute
842   if (dt == 0) dt = 60; // advance a whole minute if necessary
843   dt += t;  // get a time_t value for it rather than using duration
844   // (so it will occur at the actual second and not second and a half)
845
846   Timers::getInstance()->setTimerT(this, 2, dt);
847 }
848
849 void VVideoLiveTV::timercall(int ref)
850 {
851   if (ref == 1)
852   {
853     if (keying)
854     {
855       LogNT::getInstance()->debug(TAG, "Timer Call 1 key start.");
856       UINT newChannel = 0;
857       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * static_cast<int>(pow(10.f, i));
858       
859       Message* m = new Message();
860       m->message = Message::CHANNEL_CHANGE;
861       m->to = this;
862       m->parameter = newChannel;
863       m->tag = 1; // signal to call displayOSD();
864       MessageQueue::getInstance()->postMessage(m);
865       LogNT::getInstance()->debug(TAG, "Timer Call 1 key end.");
866     }
867     else
868     {
869       LogNT::getInstance()->debug(TAG, "Timer Call 1 not key start.");
870       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
871       if (preBuffering < 100)
872       {
873         LogNT::getInstance()->debug(TAG, "Still prebuffering, not removing osd...");
874         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
875         return;
876       }
877       bool osdpbvisible=osdposterbanner.getVisible();
878       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 1.");
879       osd.setVisible(false);
880       osdposterbanner.setVisible(false);
881       okTriggeredOSD = false;
882       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 2.");
883       draw();
884       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 4.");
885       if (osdpbvisible) boxstack->update(this);
886       else  boxstack->update(this, osd.getRegion());
887
888       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 3.");
889       PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
890       if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
891       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey end.");
892     }
893   }
894   else if (ref == 2)
895   {
896     LogNT::getInstance()->debug(TAG, "Timer Call 2  start.");
897     setClock();
898     if (osd.getVisible())
899     {
900       clock.draw();
901       boxstack->update(this, osd.getRegion());
902     }
903     LogNT::getInstance()->debug(TAG, "Timer Call 2 end.");
904   }
905 }
906
907 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
908 {
909   UINT newChannel = 0;
910   if (streamType == VDR::VIDEO)
911   {
912     VTeletextView* vtxt = static_cast<PlayerVideoLive*>(player)->getTeletextDecoder()->getTeletxtView();
913     if (vtxt)
914     {
915       BoxStack::getInstance()->remove(vtxt);
916     }
917   }
918   if (changeType == INDEX)
919   {
920     newChannel = newData;
921   }
922   else if (changeType == NUMBER)
923   {
924     UINT i;
925     for(i = 0; i < chanList->size(); i++)
926     {
927       if ((*chanList)[i]->number == newData)
928       {
929         newChannel = i;
930         break;
931       }
932     }
933
934     if (i == chanList->size())
935     {
936       // no such channel
937       return false;
938     }
939   }
940   else if (changeType == OFFSET)
941   {
942     if (newData == UP) newChannel = upChannel(currentChannelIndex);
943     else newChannel = downChannel(currentChannelIndex);
944   }
945   else if (changeType == PREVIOUS)
946   {
947     newChannel = previousChannelIndex;
948   }
949   else
950   {
951     return false; // bad input
952   }
953
954   if (newChannel == currentChannelIndex) return true;
955
956   previousChannelIndex = currentChannelIndex;
957   currentChannelIndex = newChannel;
958   
959   preBuffering = 0;
960   
961   LogNT::getInstance()->debug(TAG, "Set player to channel {}", currentChannelIndex);
962   player->setChannel(currentChannelIndex);
963   LogNT::getInstance()->debug(TAG, "Done Set player to channel {}", currentChannelIndex);
964
965   // Blank out the symbols
966   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
967
968   bufferBar.setPercent(0);
969   
970   if (!osdv)
971   {
972     sAudioChannels.setVisible(false);
973     sAspectRatio.setVisible(false);
974   }
975   else
976   {
977     reslogo.setVisible(false);
978     dolbylogo.setVisible(false);
979     txtlogo.setVisible(false);
980   }
981
982   // Remove other stuff
983   if (textUnavailable.getVisible())
984   {
985     textUnavailable.setVisible(false);
986   }
987
988   if (osdv)
989   {
990     TVMediaInfo info;
991     Channel* currentChannel = (*chanList)[currentChannelIndex];
992     if (currentChannel)
993     {
994       info.setChannelLogo(currentChannel->number);
995       if (streamType == VDR::RADIO)
996       {
997         radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
998         radioChannelLogo.setVisible(true);
999       }
1000       osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1001     }
1002   }
1003
1004   draw();
1005   BoxStack::getInstance()->update(this);
1006   
1007   return true;
1008 }
1009
1010 void VVideoLiveTV::processMessage(Message* m)
1011 {
1012   if (m->message == Message::MOUSE_LBDOWN)
1013   {
1014     //check if press is outside this view! then simulate cancel
1015     int x = m->parameter - osd.getScreenX();
1016     int y = m->tag - osd.getScreenY();
1017     if (osd.getVisible())
1018     {
1019       if ((boxRed.getX() <= x) && (boxRed.getX() + static_cast<int>(boxRed.getWidth()) >= x) &&
1020           (boxRed.getY() <= y) && (boxRed.getY() + static_cast<int>(boxRed.getHeight()) >= y))
1021       {
1022         BoxStack::getInstance()->handleCommand(Input::RED);
1023       }
1024       else if ((boxGreen.getX() <= x) && (boxGreen.getX() + static_cast<int>(boxGreen.getWidth()) >= x) &&
1025                (boxGreen.getY() <= y) && (boxGreen.getY() + static_cast<int>(boxGreen.getHeight()) >= y))
1026       {
1027         BoxStack::getInstance()->handleCommand(Input::GREEN);
1028       }
1029       else if ((boxYellow.getX() <= x) && (boxYellow.getX() + static_cast<int>(boxYellow.getWidth()) >= x) &&
1030                (boxYellow.getY() <= y) && (boxYellow.getY() + static_cast<int>(boxYellow.getHeight()) >= y))
1031       {
1032         BoxStack::getInstance()->handleCommand(Input::YELLOW);
1033       }
1034       else if ((boxBlue.getX() <= x) && (boxBlue.getX() + static_cast<int>(boxBlue.getWidth()) >= x) &&
1035                (boxBlue.getY() <= y) && (boxBlue.getY() + static_cast<int>(boxBlue.getHeight()) >= y))
1036       {
1037         BoxStack::getInstance()->handleCommand(Input::BLUE);
1038       }
1039       else
1040       {
1041         BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
1042       }
1043     }
1044     else
1045     {
1046       BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
1047     }
1048   }
1049   else if (m->message == Message::CHANNEL_CHANGE)
1050   {
1051     channelChange(NUMBER, m->parameter);
1052     osdChannelIndex = currentChannelIndex;
1053     if (m->tag == 1) displayOSD(true);
1054   }
1055   else if (m->message == Message::CHILD_CLOSE)
1056   {
1057     if (m->from == vas)
1058     {
1059       vas = NULL;
1060       displayOSD(false);
1061     }
1062   }
1063   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
1064   {
1065     LogNT::getInstance()->debug(TAG, "Received change audio channel to {:#x}", m->parameter);
1066     player->setAudioChannel((m->parameter & 0xFFFF), (m->parameter & 0xFF0000) >> 16, (m->parameter & 0xFF000000) >> 24);
1067   } 
1068   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
1069   {
1070     if (streamType != VDR::VIDEO) return;
1071
1072     PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
1073
1074     LogNT::getInstance()->debug(TAG, "Received change subtitle channel to {:#x}", m->parameter);
1075     int type = (m->parameter & 0xFF0000) >> 16;
1076     switch (type)
1077     {
1078       case 0x10:
1079       {
1080         //dvbsubtitle
1081         playervl->setSubtitleChannel((m->parameter & 0xFFFF));
1082         playervl->turnSubtitlesOn(true);
1083         VTeletextView* vtxt = playervl->getTeletextDecoder()->getTeletxtView();
1084         if (vtxt && vtxt->isInSubtitleMode())
1085         {
1086           BoxStack::getInstance()->remove(vtxt);
1087         }
1088         break;
1089       }
1090       case 0xFF:
1091       {
1092         //nosubtitles
1093         playervl->turnSubtitlesOn(false);
1094         VTeletextView* vtxt = playervl->getTeletextDecoder()->getTeletxtView();
1095         if (vtxt && vtxt->isInSubtitleMode())
1096         {
1097           BoxStack::getInstance()->remove(vtxt);
1098         }
1099         break;
1100       }
1101       case 0x11:
1102       {
1103         //videotext
1104         playervl->turnSubtitlesOn(false);
1105         doTeletext(true);
1106         playervl->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
1107         break;
1108       }
1109     }
1110
1111     if (vas)
1112     {
1113       BoxStack::getInstance()->update(vas);
1114       //BoxStack::getInstance()->update(&osd); //eveil error
1115     }
1116     BoxStack::getInstance()->update(this, osd.getRegion());
1117   }
1118   else if (m->message == Message::PLAYER_EVENT)
1119   {
1120     switch (m->parameter)
1121     {
1122       case PlayerVideoLive::CONNECTION_LOST: // connection lost detected
1123       {
1124         LogNT::getInstance()->debug(TAG, "Received connection lost from player");
1125         Control::getInstance()->connectionLost();
1126         break;
1127       }
1128       
1129       case PlayerVideoLive::STREAM_END:
1130       {
1131         // Message comes from playerlivetv through master mutex, so can do anything here
1132         showUnavailable();        
1133         break;
1134       }
1135       
1136       case PlayerVideoLive::ASPECT43:
1137       {
1138         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1139         if (osdv)
1140         {
1141           osdv->updateBackgroundColor(DrawStyle::BLACK);
1142         }
1143
1144         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
1145         sAspectRatio.setVisible(true);
1146         
1147         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1148         {
1149           osd.draw();
1150           BoxStack::getInstance()->update(this, osd.getRegion());
1151         }
1152         
1153         break;
1154       }
1155       case PlayerVideoLive::ASPECT169:
1156       {
1157         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
1158         sAspectRatio.setVisible(true);
1159
1160         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1161         if (osdv)
1162         {
1163           osdv->updateBackgroundColor(DrawStyle::BLACK);
1164         }
1165
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 PlayerVideoLive::PREBUFFERING:
1176       {
1177         preBuffering = m->tag;
1178         LogNT::getInstance()->debug(TAG, "Prebuffering - {}", 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                  LogNT::getInstance()->debug(TAG, "TVMedia vertical size {}", 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::TRANSPARENT);
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 / static_cast<float>(region.framewidth + 1);
1367   float scaley = 576.f / static_cast<float>(region.frameheight + 1);
1368   r.x = static_cast<UINT>(floor(scalex * static_cast<float>(r.x)));
1369   r.y = static_cast<UINT>(floor(scaley * static_cast<float>(r.y)));
1370   r.w = static_cast<UINT>(ceil(scalex * static_cast<float>(r.w)) + 1.f);
1371   r.h = static_cast<UINT>(ceil(scaley * static_cast<float>(r.h)) + 1.f);
1372   rectangle(r, DrawStyle::TRANSPARENT);
1373   boxstack->update(this, &r);
1374 }