]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Restructure messages to go direct to target rather than through BoxStack
[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     setClock();
901     if (osd.getVisible())
902     {
903       clock.draw();
904       boxstack->update(this, osd.getRegion());
905     }
906     LogNT::getInstance()->debug(TAG, "Timer Call 2 end.");
907   }
908 }
909
910 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
911 {
912   UINT newChannel = 0;
913   if (streamType == VDR::VIDEO)
914   {
915     VTeletextView* vtxt = static_cast<PlayerVideoLive*>(player)->getTeletextDecoder()->getTeletxtView();
916     if (vtxt)
917     {
918       BoxStack::getInstance()->remove(vtxt);
919     }
920   }
921   if (changeType == INDEX)
922   {
923     newChannel = newData;
924   }
925   else if (changeType == NUMBER)
926   {
927     UINT i;
928     for(i = 0; i < chanList->size(); i++)
929     {
930       if ((*chanList)[i]->number == newData)
931       {
932         newChannel = i;
933         break;
934       }
935     }
936
937     if (i == chanList->size())
938     {
939       // no such channel
940       return false;
941     }
942   }
943   else if (changeType == OFFSET)
944   {
945     if (newData == UP) newChannel = upChannel(currentChannelIndex);
946     else newChannel = downChannel(currentChannelIndex);
947   }
948   else if (changeType == PREVIOUS)
949   {
950     newChannel = previousChannelIndex;
951   }
952   else
953   {
954     return false; // bad input
955   }
956
957   if (newChannel == currentChannelIndex) return true;
958
959   previousChannelIndex = currentChannelIndex;
960   currentChannelIndex = newChannel;
961   
962   preBuffering = 0;
963   
964   LogNT::getInstance()->debug(TAG, "Set player to channel {}", currentChannelIndex);
965   player->setChannel(currentChannelIndex);
966   LogNT::getInstance()->debug(TAG, "Done Set player to channel {}", currentChannelIndex);
967
968   // Blank out the symbols
969   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
970
971   bufferBar.setPercent(0);
972   
973   if (!osdv)
974   {
975     sAudioChannels.setVisible(false);
976     sAspectRatio.setVisible(false);
977   }
978   else
979   {
980     reslogo.setVisible(false);
981     dolbylogo.setVisible(false);
982     txtlogo.setVisible(false);
983   }
984
985   // Remove other stuff
986   if (textUnavailable.getVisible())
987   {
988     textUnavailable.setVisible(false);
989   }
990
991   if (osdv)
992   {
993     TVMediaInfo info;
994     Channel* currentChannel = (*chanList)[currentChannelIndex];
995     if (currentChannel)
996     {
997       info.setChannelLogo(currentChannel->number);
998       if (streamType == VDR::RADIO)
999       {
1000         radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1001         radioChannelLogo.setVisible(true);
1002       }
1003       osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1004     }
1005   }
1006
1007   draw();
1008   BoxStack::getInstance()->update(this);
1009   
1010   return true;
1011 }
1012
1013 void VVideoLiveTV::processMessage(Message* m)
1014 {
1015   if (m->message == Message::MOUSE_LBDOWN)
1016   {
1017     //check if press is outside this view! then simulate cancel
1018     int x = m->parameter - osd.getScreenX();
1019     int y = m->tag - osd.getScreenY();
1020     if (osd.getVisible())
1021     {
1022       if ((boxRed.getX() <= x) && (boxRed.getX() + static_cast<int>(boxRed.getWidth()) >= x) &&
1023           (boxRed.getY() <= y) && (boxRed.getY() + static_cast<int>(boxRed.getHeight()) >= y))
1024       {
1025         BoxStack::getInstance()->handleCommand(Input::RED);
1026       }
1027       else if ((boxGreen.getX() <= x) && (boxGreen.getX() + static_cast<int>(boxGreen.getWidth()) >= x) &&
1028                (boxGreen.getY() <= y) && (boxGreen.getY() + static_cast<int>(boxGreen.getHeight()) >= y))
1029       {
1030         BoxStack::getInstance()->handleCommand(Input::GREEN);
1031       }
1032       else if ((boxYellow.getX() <= x) && (boxYellow.getX() + static_cast<int>(boxYellow.getWidth()) >= x) &&
1033                (boxYellow.getY() <= y) && (boxYellow.getY() + static_cast<int>(boxYellow.getHeight()) >= y))
1034       {
1035         BoxStack::getInstance()->handleCommand(Input::YELLOW);
1036       }
1037       else if ((boxBlue.getX() <= x) && (boxBlue.getX() + static_cast<int>(boxBlue.getWidth()) >= x) &&
1038                (boxBlue.getY() <= y) && (boxBlue.getY() + static_cast<int>(boxBlue.getHeight()) >= y))
1039       {
1040         BoxStack::getInstance()->handleCommand(Input::BLUE);
1041       }
1042       else
1043       {
1044         BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
1045       }
1046     }
1047     else
1048     {
1049       BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
1050     }
1051   }
1052   else if (m->message == Message::CHANNEL_CHANGE)
1053   {
1054     channelChange(NUMBER, m->parameter);
1055     osdChannelIndex = currentChannelIndex;
1056     if (m->tag == 1) displayOSD(true);
1057   }
1058   else if (m->message == Message::CHILD_CLOSE)
1059   {
1060     if (m->from == vas)
1061     {
1062       vas = NULL;
1063       displayOSD(false);
1064     }
1065   }
1066   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
1067   {
1068     LogNT::getInstance()->debug(TAG, "Received change audio channel to {:#x}", m->parameter);
1069     player->setAudioChannel((m->parameter & 0xFFFF), (m->parameter & 0xFF0000) >> 16, (m->parameter & 0xFF000000) >> 24);
1070   } 
1071   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
1072   {
1073     if (streamType != VDR::VIDEO) return;
1074
1075     PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
1076
1077     LogNT::getInstance()->debug(TAG, "Received change subtitle channel to {:#x}", m->parameter);
1078     int type = (m->parameter & 0xFF0000) >> 16;
1079     switch (type)
1080     {
1081       case 0x10:
1082       {
1083         //dvbsubtitle
1084         playervl->setSubtitleChannel((m->parameter & 0xFFFF));
1085         playervl->turnSubtitlesOn(true);
1086         VTeletextView* vtxt = playervl->getTeletextDecoder()->getTeletxtView();
1087         if (vtxt && vtxt->isInSubtitleMode())
1088         {
1089           BoxStack::getInstance()->remove(vtxt);
1090         }
1091         break;
1092       }
1093       case 0xFF:
1094       {
1095         //nosubtitles
1096         playervl->turnSubtitlesOn(false);
1097         VTeletextView* vtxt = playervl->getTeletextDecoder()->getTeletxtView();
1098         if (vtxt && vtxt->isInSubtitleMode())
1099         {
1100           BoxStack::getInstance()->remove(vtxt);
1101         }
1102         break;
1103       }
1104       case 0x11:
1105       {
1106         //videotext
1107         playervl->turnSubtitlesOn(false);
1108         doTeletext(true);
1109         playervl->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
1110         break;
1111       }
1112     }
1113
1114     if (vas)
1115     {
1116       BoxStack::getInstance()->update(vas);
1117       //BoxStack::getInstance()->update(&osd); //eveil error
1118     }
1119     BoxStack::getInstance()->update(this, osd.getRegion());
1120   }
1121   else if (m->message == Message::PLAYER_EVENT)
1122   {
1123     switch (m->parameter)
1124     {
1125       case PlayerVideoLive::CONNECTION_LOST: // connection lost detected
1126       {
1127         LogNT::getInstance()->debug(TAG, "Received connection lost from player");
1128         Control::getInstance()->connectionLost();
1129         break;
1130       }
1131       
1132       case PlayerVideoLive::STREAM_END:
1133       {
1134         // Message comes from playerlivetv through master mutex, so can do anything here
1135         showUnavailable();        
1136         break;
1137       }
1138       
1139       case PlayerVideoLive::ASPECT43:
1140       {
1141         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1142         if (osdv)
1143         {
1144           osdv->updateBackgroundColor(DrawStyle::BLACK);
1145         }
1146
1147         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
1148         sAspectRatio.setVisible(true);
1149         
1150         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1151         {
1152           osd.draw();
1153           BoxStack::getInstance()->update(this, osd.getRegion());
1154         }
1155         
1156         break;
1157       }
1158       case PlayerVideoLive::ASPECT169:
1159       {
1160         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
1161         sAspectRatio.setVisible(true);
1162
1163         OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1164         if (osdv)
1165         {
1166           osdv->updateBackgroundColor(DrawStyle::BLACK);
1167         }
1168
1169
1170         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1171         {
1172           osd.draw();
1173           BoxStack::getInstance()->update(this, osd.getRegion());
1174         }
1175                 
1176         break;
1177       }
1178       case PlayerVideoLive::PREBUFFERING:
1179       {
1180         preBuffering = m->tag;
1181         LogNT::getInstance()->debug(TAG, "Prebuffering - {}", preBuffering);
1182         bufferBar.setPercent(preBuffering);
1183
1184         if (osd.getVisible())
1185         {
1186           bufferBar.setVisible(true);
1187           bufferBar.draw();
1188           Region r;
1189           bufferBar.getRootBoxRegion(&r);                       ///////// FIXME !!!
1190           BoxStack::getInstance()->update(this, &r);
1191
1192           if (preBuffering == 100)
1193           {
1194             doAudioChannelSymbol();
1195           }
1196         }
1197       }
1198     }
1199   }
1200 }
1201
1202 void VVideoLiveTV::doAudioChannelSymbol()
1203 {
1204   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1205   // get the doobery
1206   Channel* currentChannel = (*chanList)[osdChannelIndex];
1207     
1208   bool multiAudio = false;
1209   if (Audio::getInstance()->supportsAc3()) {
1210           if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true;
1211   }
1212   if (currentChannel->numAPids > 1) multiAudio = true;
1213   
1214   // draw the doobery
1215   if (!osdv) {
1216           if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1217           else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1218           sAudioChannels.setVisible(true);
1219   }
1220   if (osdv)
1221   {
1222           dolbylogo.setVisible(true);
1223           txtlogo.setVisible(true);
1224           TVMediaInfo info;
1225           if (currentChannel->tpid!=0) info.setStaticArtwork(sa_txton);
1226           else info.setStaticArtwork(sa_txtoff);
1227           txtlogo.setTVMedia(info, WTVMedia::ZoomVertical);
1228
1229
1230           if (Audio::getInstance()->supportsAc3() && currentChannel->numDPids>1)
1231                   info.setStaticArtwork(sa_dolbyon);
1232           else info.setStaticArtwork(sa_dolbyoff);
1233           dolbylogo.setTVMedia(info, WTVMedia::ZoomVertical);
1234
1235          Demuxer* demux=Demuxer::getInstance();
1236          if (demux) {
1237                  int vertical_size=demux->getVerticalSize();
1238                  LogNT::getInstance()->debug(TAG, "TVMedia vertical size {}", vertical_size);
1239                  reslogo.setVisible(true);
1240                  switch (vertical_size) {
1241                  case 576:
1242                          info.setStaticArtwork(sa_sd576i);
1243                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1244                  case 720:
1245                          info.setStaticArtwork(sa_hd720p);
1246                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1247                  case 1080:
1248                          info.setStaticArtwork(sa_hd1080i);
1249                          reslogo.setTVMedia(info, WTVMedia::ZoomVertical);break;
1250                  default: // we assume sd
1251                          reslogo.setVisible(false);
1252                  };
1253
1254          } else {
1255                  reslogo.setVisible(false);
1256          }
1257
1258
1259   }
1260   
1261   if (osd.getVisible())
1262   {
1263     sAudioChannels.draw();
1264     Region r;
1265     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!
1266     // Fix this n'all.
1267     r.w = 32;
1268     r.h = 16;
1269     BoxStack::getInstance()->update(this, &r);
1270   }
1271 }
1272
1273 UINT VVideoLiveTV::upChannel(UINT index)
1274 {
1275   if (index == (chanList->size() - 1)) // at the end
1276     return 0; // so go to start
1277   else
1278     return index + 1;
1279 }
1280
1281 UINT VVideoLiveTV::downChannel(UINT index)
1282 {
1283   if (index == 0) // at the start
1284     return chanList->size() - 1; // so go to end
1285   else
1286     return index - 1;
1287 }
1288
1289 void VVideoLiveTV::toggleChopSides()
1290 {
1291   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1292
1293   if (videoMode == Video::NORMAL)
1294   {
1295     videoMode = Video::LETTERBOX;
1296     video->setMode(Video::LETTERBOX);
1297   }
1298   else
1299   {
1300     videoMode = Video::NORMAL;
1301     video->setMode(Video::NORMAL);
1302   }
1303 }
1304
1305 void VVideoLiveTV::updatePosterBanner()
1306 {
1307         OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1308         if (!osdv) {
1309                 osdposterbanner.setVisible(false);
1310                 return;
1311         }
1312         Event* toShow = NULL;
1313         if (eventList && eventList->size()) {
1314                 toShow = (*eventList)[0];
1315                 Channel* currentChannel = (*chanList)[osdChannelIndex];
1316                 toShow->loadinfos(currentChannel->number);
1317         }
1318
1319         if (toShow)
1320         {
1321                 TVMedia poster;
1322                 poster.height=0;
1323                 bool posterscale=true;
1324                 if (toShow->movieInfo) {
1325                         poster=toShow->movieInfo->poster;
1326                 }
1327                 if (toShow->seriesInfo) {
1328                         if (toShow->seriesInfo->banners.size()) {
1329                                 poster=toShow->seriesInfo->banners[0];
1330                                 posterscale=false;
1331                         } else if (toShow->seriesInfo->seasonposter.height) {
1332                                 poster=toShow->seriesInfo->seasonposter;
1333                         } else if (toShow->seriesInfo->posters.size()) {
1334                                 poster=toShow->seriesInfo->posters[0];
1335                         }
1336                 }
1337                 if (poster.height) {
1338                         osdposterbanner.setTVMedia(poster.info, posterscale ? WTVMedia::ZoomVertical : WTVMedia::ZoomHorizontal);
1339                         osdposterbanner.setVisible(true);
1340                 } else {
1341                         osdposterbanner.setVisible(false);
1342                 }
1343         } else {
1344                 osdposterbanner.setVisible(false);
1345         }
1346
1347 }
1348
1349
1350 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1351 {
1352   drawBitmap(posX, posY, bm,region);
1353   Region r;
1354   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1355   boxstack->update(this, &r);
1356 }
1357
1358 void VVideoLiveTV::clearOSD()
1359 {
1360   rectangle(area, DrawStyle::TRANSPARENT);
1361   boxstack->update(this, &area);
1362 }
1363
1364 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1365 {
1366   Region r;
1367   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1368   //now convert to our display
1369   float scalex = 720.f / static_cast<float>(region.framewidth + 1);
1370   float scaley = 576.f / static_cast<float>(region.frameheight + 1);
1371   r.x = static_cast<UINT>(floor(scalex * static_cast<float>(r.x)));
1372   r.y = static_cast<UINT>(floor(scaley * static_cast<float>(r.y)));
1373   r.w = static_cast<UINT>(ceil(scalex * static_cast<float>(r.w)) + 1.f);
1374   r.h = static_cast<UINT>(ceil(scaley * static_cast<float>(r.h)) + 1.f);
1375   rectangle(r, DrawStyle::TRANSPARENT);
1376   boxstack->update(this, &r);
1377 }