]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Fix subs running during pause - after a jump then quick pause
[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 #include "imageloader.h"
50
51 #include "vvideolivetv.h"
52
53 static const char* TAG = "VVideoLiveTV";
54
55
56 VVideoLiveTV::VVideoLiveTV(std::shared_ptr<ChannelList> tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
57 : chanList(tchanList), vchannelList(tvchannelList)
58 {
59   vdr = VDR::getInstance();
60   boxstack = BoxStack::getInstance();
61   video = Video::getInstance();
62   
63   vas = NULL;
64
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   Image icon = ImageLoader::getInstance()->createChannelLogo(initialChannelNumber);
117
118   if (osdv && streamType == VDR::RADIO) {
119           radioChannelLogo.setPosition(video->getScreenWidth()/4,video->getScreenHeight()/4);
120           radioChannelLogo.setSize(video->getScreenWidth()/2,video->getScreenHeight()/2);
121           radioChannelLogo.setVisible(true);
122           radioChannelLogo.setImage(icon, WTVMedia::ZoomVertical);
123           add(&radioChannelLogo);
124   }
125
126   osdposterbanner.setPosition(20,20);
127   //osdposterbanner.setBackgroundColour(DrawStyle::OSDBACKGROUND);
128   osdposterbanner.setSize(video->getScreenWidth()*4/10,video->getScreenHeight()*4/10);
129   osdposterbanner.setVisible(false);
130   add(&osdposterbanner);
131
132   osd.setBackgroundColour(DrawStyle::OSDBACKGROUND);
133   osd.setPosition(0, video->getScreenHeight() - 150);
134   osd.setSize(video->getScreenWidth(), 150);
135   osd.setVisible(false);
136   add(&osd);
137   
138   int channellogomove=0;
139   int boxdiff=166;
140   if (osdv)
141   {
142     //osdChannelLogo.setBackgroundColour(DrawStyle::OSDBACKGROUND);
143     osdChannelLogo.setPosition(30,5);
144     osdChannelLogo.setSize(60,60);
145     osdChannelLogo.setImage(icon, WTVMedia::ZoomVertical);
146     osd.add(&osdChannelLogo);
147     channellogomove = 30-5;
148     boxdiff = 145;
149   }
150
151   //clock.setBackgroundColour(DrawStyle::OSDBACKGROUND);
152   clock.setPosition(osd.getWidth() - 100, 4);
153   clock.setSize(90, 30);
154   osd.add(&clock);
155
156   //osdChanNum.setBackgroundColour(DrawStyle::OSDBACKGROUND);
157   osdChanNum.setPosition(60 + channellogomove, 4);
158   osdChanNum.setSize((numberWidth * 10) + 22, getFontHeight() + 5); // 10 px = width of number chars in font
159   osd.add(&osdChanNum);
160
161   //osdChanName.setBackgroundColour(DrawStyle::OSDBACKGROUND);
162   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
163   osdChanName.setSize(300, 30);
164   osd.add(&osdChanName);
165   
166   boxRed.setBackgroundColour(DrawStyle::RED);
167   boxRed.setPosition(54+0*boxdiff, 104);
168   boxRed.setSize(18, 16);
169   osd.add(&boxRed);
170
171   boxGreen.setBackgroundColour(DrawStyle::GREEN);
172   boxGreen.setPosition(54+1*boxdiff, 104);
173   boxGreen.setSize(18, 16);
174   osd.add(&boxGreen);
175
176   boxYellow.setBackgroundColour(DrawStyle::YELLOW);
177   boxYellow.setPosition(54+2*boxdiff, 104);
178   boxYellow.setSize(18, 16);
179   osd.add(&boxYellow);
180
181   boxBlue.setBackgroundColour(DrawStyle::BLUE);
182   boxBlue.setPosition(54+3*boxdiff, 104);
183   boxBlue.setSize(18, 16);
184   osd.add(&boxBlue);  
185   
186   //textRed.setBackgroundColour(DrawStyle::OSDBACKGROUND);
187   textRed.setPosition(boxRed.getX2(), 98);
188   textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30);
189   textRed.setText(tr("Summary"));
190   osd.add(&textRed);  
191     
192   if (streamType == VDR::VIDEO)
193   {
194     //textGreen.setBackgroundColour(DrawStyle::OSDBACKGROUND);
195     textGreen.setPosition(boxGreen.getX2(), 98);
196     textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30);
197     textGreen.setText(tr("Audio"));
198     osd.add(&textGreen);  
199   }
200     
201   //textYellow.setBackgroundColour(DrawStyle::OSDBACKGROUND);
202   textYellow.setPosition(boxYellow.getX2(), 98);
203   textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30);
204   textYellow.setText(tr("Teletext"));
205   osd.add(&textYellow);  
206     
207   //textBlue.setBackgroundColour(DrawStyle::OSDBACKGROUND);
208   textBlue.setPosition(boxBlue.getX2(), 98);
209   textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30);
210   textBlue.setText(tr("EPG"));
211   osd.add(&textBlue);  
212     
213   //sl.setBackgroundColour(DrawStyle::OSDBACKGROUND);
214   sl.setPosition(90, 36);
215   sl.setSize(480, 58);
216   osd.add(&sl);
217   
218   bufferBar.setPosition(osd.getWidth() - 90, 70);
219   bufferBar.setSize(40, 20);
220   bufferBar.setVisible(true);
221   osd.add(&bufferBar);
222
223   if (!osdv)
224   {
225     sAspectRatio.setPosition(osd.getWidth() - 90, 40);
226     sAspectRatio.nextColour = DrawStyle::LIVETVSYMBOLS;
227     sAspectRatio.setVisible(false);
228     osd.add(&sAspectRatio);
229
230     sAudioChannels.setPosition(osd.getWidth() - 130, 40);
231     sAudioChannels.nextColour = DrawStyle::LIVETVSYMBOLS;
232     sAudioChannels.setVisible(false);
233     osd.add(&sAudioChannels);
234   }
235   
236   if (osdv)
237   {
238     Image sIcon;
239
240     sIcon = ImageLoader::getInstance()->createStatic(sa_txtoff);
241     txtlogo.setPosition(54+4*boxdiff,104);
242     txtlogo.setSize(22,20);
243     txtlogo.setImage(sIcon, WTVMedia::ZoomVertical);
244     osd.add(&txtlogo);
245
246     sIcon = ImageLoader::getInstance()->createStatic(sa_dolbyoff);
247     dolbylogo.setPosition(54+4*boxdiff+15,104);
248     dolbylogo.setSize(22,20);
249     dolbylogo.setImage(sIcon, WTVMedia::ZoomVertical);
250     osd.add(&dolbylogo);
251
252     sIcon = ImageLoader::getInstance()->createStatic(sa_sd576i);
253     reslogo.setPosition(54+4*boxdiff+30,104);
254     reslogo.setSize(22,20);
255     reslogo.setImage(sIcon, 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       if (vchannelList) 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.fillColour(DrawStyle::OSDBACKGROUND);
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     Channel* currentChannel = (*chanList)[currentChannelIndex];
995     if (currentChannel)
996     {
997       Image icon = ImageLoader::getInstance()->createChannelLogo(currentChannel->number);
998       if (streamType == VDR::RADIO)
999       {
1000         radioChannelLogo.setImage(icon, WTVMedia::ZoomVertical);
1001         radioChannelLogo.setVisible(true);
1002       }
1003       osdChannelLogo.setImage(icon, 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   {
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   {
1218     if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1219     else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1220     sAudioChannels.setVisible(true);
1221   }
1222   if (osdv)
1223   {
1224     dolbylogo.setVisible(true);
1225     txtlogo.setVisible(true);
1226     Image icon;
1227     int sa_id;
1228
1229     if (currentChannel->tpid!=0) sa_id = sa_txton;
1230     else sa_id = sa_txtoff;
1231     icon = ImageLoader::getInstance()->createStatic(sa_id);
1232     txtlogo.setImage(icon, WTVMedia::ZoomVertical);
1233
1234     if (Audio::getInstance()->supportsAc3() && currentChannel->numDPids>1)
1235       sa_id = sa_dolbyon;
1236     else
1237       sa_id = sa_dolbyoff;
1238     icon = ImageLoader::getInstance()->createStatic(sa_id);
1239     dolbylogo.setImage(icon, WTVMedia::ZoomVertical);
1240
1241     Demuxer* demux=Demuxer::getInstance();
1242     if (demux)
1243     {
1244       int vertical_size = demux->getVerticalSize();
1245       LogNT::getInstance()->debug(TAG, "TVMedia vertical size {}", vertical_size);
1246
1247       reslogo.setVisible(true);
1248       switch (vertical_size)
1249       {
1250         case 576:
1251           icon = ImageLoader::getInstance()->createStatic(sa_sd576i);
1252           reslogo.setImage(icon, WTVMedia::ZoomVertical);
1253           break;
1254         case 720:
1255           icon = ImageLoader::getInstance()->createStatic(sa_hd720p);
1256           reslogo.setImage(icon, WTVMedia::ZoomVertical);
1257           break;
1258         case 1080:
1259           icon = ImageLoader::getInstance()->createStatic(sa_hd1080i);
1260           reslogo.setImage(icon, WTVMedia::ZoomVertical);
1261           break;
1262         default: // we assume sd
1263           reslogo.setVisible(false);
1264       }
1265
1266     } else {
1267         reslogo.setVisible(false);
1268     }
1269   }
1270   
1271   if (osd.getVisible())
1272   {
1273     sAudioChannels.draw();
1274     Region r;
1275     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!
1276     // Fix this n'all.
1277     r.w = 32;
1278     r.h = 16;
1279     BoxStack::getInstance()->update(this, &r);
1280   }
1281 }
1282
1283 UINT VVideoLiveTV::upChannel(UINT index)
1284 {
1285   if (index == (chanList->size() - 1)) // at the end
1286     return 0; // so go to start
1287   else
1288     return index + 1;
1289 }
1290
1291 UINT VVideoLiveTV::downChannel(UINT index)
1292 {
1293   if (index == 0) // at the start
1294     return chanList->size() - 1; // so go to end
1295   else
1296     return index - 1;
1297 }
1298
1299 void VVideoLiveTV::toggleChopSides()
1300 {
1301   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1302
1303   if (videoMode == Video::NORMAL)
1304   {
1305     videoMode = Video::LETTERBOX;
1306     video->setMode(Video::LETTERBOX);
1307   }
1308   else
1309   {
1310     videoMode = Video::NORMAL;
1311     video->setMode(Video::NORMAL);
1312   }
1313 }
1314
1315 void VVideoLiveTV::updatePosterBanner()
1316 {
1317         OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1318         if (!osdv) {
1319                 osdposterbanner.setVisible(false);
1320                 return;
1321         }
1322         Event* toShow = NULL;
1323         if (eventList && eventList->size()) {
1324                 toShow = (*eventList)[0];
1325                 Channel* currentChannel = (*chanList)[osdChannelIndex];
1326                 toShow->loadinfos(currentChannel->number);
1327         }
1328
1329         if (toShow)
1330         {
1331                 TVMediaStruct poster;
1332                 poster.height=0;
1333                 bool posterscale=true;
1334                 if (toShow->movieInfo) {
1335                         poster=toShow->movieInfo->poster;
1336                 }
1337                 if (toShow->seriesInfo) {
1338                         if (toShow->seriesInfo->banners.size()) {
1339                                 poster=toShow->seriesInfo->banners[0];
1340                                 posterscale=false;
1341                         } else if (toShow->seriesInfo->seasonposter.height) {
1342                                 poster=toShow->seriesInfo->seasonposter;
1343                         } else if (toShow->seriesInfo->posters.size()) {
1344                                 poster=toShow->seriesInfo->posters[0];
1345                         }
1346                 }
1347                 if (poster.height) {
1348                         osdposterbanner.setImage(poster.image, posterscale ? WTVMedia::ZoomVertical : WTVMedia::ZoomHorizontal);
1349                         osdposterbanner.setVisible(true);
1350                 } else {
1351                         osdposterbanner.setVisible(false);
1352                 }
1353         } else {
1354                 osdposterbanner.setVisible(false);
1355         }
1356
1357 }
1358
1359
1360 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1361 {
1362   drawBitmap(posX, posY, bm,region);
1363   Region r;
1364   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1365   boxstack->update(this, &r);
1366 }
1367
1368 void VVideoLiveTV::clearOSD()
1369 {
1370   rectangle(area, DrawStyle::TRANSPARENT);
1371   boxstack->update(this, &area);
1372 }
1373
1374 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1375 {
1376   Region r;
1377   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1378   //now convert to our display
1379   float scalex = 720.f / static_cast<float>(region.framewidth + 1);
1380   float scaley = 576.f / static_cast<float>(region.frameheight + 1);
1381   r.x = static_cast<UINT>(floor(scalex * static_cast<float>(r.x)));
1382   r.y = static_cast<UINT>(floor(scaley * static_cast<float>(r.y)));
1383   r.w = static_cast<UINT>(ceil(scalex * static_cast<float>(r.w)) + 1.f);
1384   r.h = static_cast<UINT>(ceil(scaley * static_cast<float>(r.h)) + 1.f);
1385   rectangle(r, DrawStyle::TRANSPARENT);
1386   boxstack->update(this, &r);
1387 }