]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Change ChannelList back to std::vector, use custom deleter in shared_ptr
[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(std::shared_ptr<ChannelList> tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
56 : chanList(tchanList), vchannelList(tvchannelList)
57 {
58   vdr = VDR::getInstance();
59   boxstack = BoxStack::getInstance();
60   video = Video::getInstance();
61   
62   vas = NULL;
63
64   numberWidth = VDR::getInstance()->getChannelNumberWidth();
65
66   currentChannelIndex = 0;
67   previousChannelIndex = 0;
68   osdChannelIndex = 0;
69   keying = 0;
70   preBuffering = 0;
71
72   playing = false;
73
74   // Convert channel number to index
75   for(UINT i = 0; i < chanList->size(); i++)
76   {
77     if ((*chanList)[i]->number == initialChannelNumber)
78     {
79       currentChannelIndex = i;
80       osdChannelIndex = i;
81       break;
82     }
83   }
84
85   eventList = NULL;
86
87   videoMode = video->getMode();
88   
89   if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
90   {
91     streamType = VDR::VIDEO;
92     player = new PlayerVideoLive(Control::getInstance(), this, this, chanList);
93   }
94   else
95   {
96     streamType = VDR::RADIO;
97     player = new PlayerRadioLive(Control::getInstance(), this, chanList);
98   }
99   player->init();
100
101   setSize(video->getScreenWidth(), video->getScreenHeight());
102   createBuffer();
103   setBackgroundColour(DrawStyle::TRANSPARENT);
104
105   OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
106   if (osdv)
107   {
108     osdv->updateBackgroundColor(DrawStyle::BLACK);
109   }
110
111   // This variable is set to true if the user pressed OK to bring the OSD on screen
112   // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling
113   okTriggeredOSD = false;
114
115   TVMediaInfo info;
116   info.setChannelLogo(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.setTVMedia(info, 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.setTVMedia(info, 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     TVMediaInfo ninfo;
239     ninfo.setStaticArtwork(sa_txtoff);
240     txtlogo.setPosition(54+4*boxdiff,104);
241     txtlogo.setSize(22,20);
242     txtlogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
243     osd.add(&txtlogo);
244
245     ninfo.setStaticArtwork(sa_dolbyoff);
246     dolbylogo.setPosition(54+4*boxdiff+15,104);
247     dolbylogo.setSize(22,20);
248     dolbylogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
249     osd.add(&dolbylogo);
250
251     ninfo.setStaticArtwork(sa_sd576i);
252     reslogo.setPosition(54+4*boxdiff+30,104);
253     reslogo.setSize(22,20);
254     reslogo.setTVMedia(ninfo, WTVMedia::ZoomVertical);
255     osd.add(&reslogo);
256
257     reslogo.setVisible(false);
258     dolbylogo.setVisible(false);
259     txtlogo.setVisible(false);
260   }
261
262   //textUnavailable.setBackgroundColour(DrawStyle::OSDBACKGROUND);
263   textUnavailable.setPosition(60, 30);
264   textUnavailable.setSize(osd.getWidth() - 120, 30);
265   textUnavailable.setText(tr("Channel Unavailable"));
266   textUnavailable.setVisible(false);
267   add(&textUnavailable);
268   
269   vdisplay.mode = Fullscreen;
270   vdisplay.fallbackMode = Fullscreen;
271   vdisplay.x = 0;
272   vdisplay.y = 0;
273   vdisplay.width = 0;
274   vdisplay.height = 0;
275
276   MessageQueue::getInstance()->addReceiver(this);
277 }
278
279 void VVideoLiveTV::preDelete()
280 {
281   if (playing) stop();
282 }
283
284 VVideoLiveTV::~VVideoLiveTV()
285 {
286   MessageQueue::getInstance()->removeReceiver(this);
287   delete player;
288   video->setDefaultAspect();
289   delData();
290   OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
291   if (osdv)
292   {
293     osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
294   }
295 }
296
297 void VVideoLiveTV::delData()
298 {
299   if (eventList)
300   {
301     int eventListSize = eventList->size();
302     for(int i = 0; i < eventListSize; i++)
303     {
304       delete (*eventList)[i];
305     }
306     eventList->clear();
307     delete eventList;
308   }
309 }
310
311 int VVideoLiveTV::handleCommand(int command)
312 {
313   switch(command)
314   {
315     case Input::BACK:
316     {
317       if (osd.getVisible() && !textUnavailable.getVisible())
318       {
319         clearScreen();
320         return BoxStack::COMMAND_HANDLED;
321       }
322       // else drop through to stop
323     }
324     FALLTHROUGH
325     case Input::STOP:
326     {
327       stop();
328       if (vchannelList) vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
329       return BoxStack::DELETE_ME;
330     }
331     
332     // navigate EPG, bring it onscreen if it's not there
333     case Input::UP:
334     {
335       if (InputMan::getInstance()->mayHaveFewButtons())
336       {
337         if (okTriggeredOSD) doUpDown(false);
338         else doChanUpDown(UP);
339       }
340       else
341       {
342         doUpDown(false);
343       }
344       return BoxStack::COMMAND_HANDLED;
345     }
346     case Input::DOWN:
347     {
348       if (InputMan::getInstance()->mayHaveFewButtons())
349       {
350         if (okTriggeredOSD) doUpDown(true);
351         else doChanUpDown(DOWN);
352       }
353       else
354       {
355         doUpDown(true);
356       }
357       return BoxStack::COMMAND_HANDLED;
358     }
359     case Input::LEFT:
360     {
361       doLeftRight(false);
362       return BoxStack::COMMAND_HANDLED;
363     }
364     case Input::RIGHT:
365     {
366       doLeftRight(true);
367       return BoxStack::COMMAND_HANDLED;
368     }
369     case Input::CHANNELUP:
370     {
371       doChanUpDown(UP);
372       return BoxStack::COMMAND_HANDLED;
373     }
374     case Input::CHANNELDOWN:
375     {
376       doChanUpDown(DOWN);
377       return BoxStack::COMMAND_HANDLED;
378     }
379     case Input::PREVCHANNEL:
380     {
381       channelChange(PREVIOUS, 0);
382       osdChannelIndex = currentChannelIndex;
383       displayOSD(true);
384       return BoxStack::COMMAND_HANDLED;
385     }
386     case Input::OK:
387     {
388       doOK();
389       return BoxStack::COMMAND_HANDLED;
390     }
391     case Input::RED:
392     case Input::MENU:
393     {
394       doSummary();
395       return BoxStack::COMMAND_HANDLED;
396     }
397     case Input::FULL:
398     case Input::TV:
399     {
400       toggleChopSides();
401       return BoxStack::COMMAND_HANDLED;
402     }
403
404     case Input::ZERO:
405     case Input::ONE:
406     case Input::TWO:
407     case Input::THREE:
408     case Input::FOUR:
409     case Input::FIVE:
410     case Input::SIX:
411     case Input::SEVEN:
412     case Input::EIGHT:
413     case Input::NINE:
414     {
415       // key in channel number
416       doKey(command);
417       return BoxStack::COMMAND_HANDLED;
418     }
419
420     case Input::GREEN:
421     {
422       if (streamType == VDR::VIDEO) doAudioSelector();
423       return BoxStack::COMMAND_HANDLED;
424     }
425     case Input::YELLOW:
426     {
427       if (streamType ==VDR::VIDEO) 
428       {
429         doTeletext(); //TODO: Add a selector for subtitles or teletext
430       }
431       return BoxStack::COMMAND_HANDLED;
432     }
433     case Input::GUIDE:
434     case Input::BLUE:
435     {
436       doEPG();
437       return BoxStack::COMMAND_HANDLED;
438     }
439     case Input::RECORD:
440       if (streamType == VDR::VIDEO)
441         (static_cast<PlayerVideoLive*>(player))->toggleSubtitles();
442       return BoxStack::COMMAND_HANDLED;
443   }
444
445   return BoxStack::ABANDON_COMMAND;
446 }
447
448 void VVideoLiveTV::go()
449 {
450   playing = true;
451   draw();
452   boxstack->update(this);
453
454   setClock();
455   displayOSD(true);
456   
457   player->go(currentChannelIndex);
458 }
459
460 void VVideoLiveTV::stop()
461 {
462   Timers::getInstance()->cancelTimer(this, 1);
463   Timers::getInstance()->cancelTimer(this, 2);
464   player->stop();
465   playing = false;
466 }
467
468 void VVideoLiveTV::doLeftRight(bool right)
469 {
470   if (osd.getVisible())
471   {
472     if (right) osdChannelIndex = upChannel(osdChannelIndex);
473     else       osdChannelIndex = downChannel(osdChannelIndex);
474   }
475   else
476   {
477     osdChannelIndex = currentChannelIndex;
478   }
479   displayOSD(true);
480 }
481
482 void VVideoLiveTV::doUpDown(bool /* down */)
483 {
484   if (osd.getVisible())
485   {
486     sl.draw();
487     
488     displayOSD(false);
489   }
490   else
491   {
492     displayOSD(true);
493   }
494 }
495
496 void VVideoLiveTV::doChanUpDown(int which)
497 {
498   channelChange(OFFSET, which);
499   osdChannelIndex = currentChannelIndex;
500   displayOSD(true);
501 }
502
503 void VVideoLiveTV::doOK()
504 {
505   if (osd.getVisible())
506   {
507     if (keying)
508     {
509       UINT newChannel = 0;
510       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * static_cast<int>(pow(10.f, i));
511       
512       channelChange(NUMBER, newChannel);
513       osdChannelIndex = currentChannelIndex;
514       displayOSD(true);
515     }
516     else if (osdChannelIndex == currentChannelIndex)
517     {
518       clearScreen();
519     }
520     else
521     {
522       channelChange(INDEX, osdChannelIndex);
523       displayOSD(true);
524     }
525   }
526   else
527   {
528     osdChannelIndex = currentChannelIndex;
529     displayOSD(true);
530     okTriggeredOSD = true;
531   }
532 }
533
534 void VVideoLiveTV::doSummary()
535 {
536   displayOSD(false);
537   Channel* currentChannel = (*chanList)[osdChannelIndex];
538   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
539   if (!eventList)
540   {
541       return;
542   }
543   sort(eventList->begin(), eventList->end(), EventSorter());
544
545   if (eventList->size() < 1) return;
546   Event* event = (*eventList)[0];
547   event->loadinfos(currentChannel->number);
548
549   VEpgSummary* vr = new VEpgSummary(event,currentChannel);
550   vr->draw();
551   boxstack->add(vr);
552   boxstack->update(vr);
553 }
554
555 void VVideoLiveTV::doKey(int command)
556 {
557   if (!osd.getVisible()) // First key. prep the data
558   {
559     setNowNextData();
560     updatePosterBanner();
561     keying = 0;    
562   }
563
564   int i;
565   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
566   keyingInput[0] = command;
567   keying++;
568
569   char* keyingString = new char[numberWidth + 1];
570   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
571   keyingString[numberWidth] = '\0';
572
573   for (i = 0; i < keying; i++) keyingString[i] = static_cast<char>(keyingInput[keying - 1 - i] + 48);
574   
575   if (keying == numberWidth)
576   {
577     UINT newChannel = 0;
578     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * static_cast<int>(pow(10.f, i));
579     
580     channelChange(NUMBER, newChannel);
581     osdChannelIndex = currentChannelIndex;
582     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
583     displayOSD(true); // this will put one back if required
584   }
585   else
586   {
587     osdChanNum.setText(keyingString);
588
589     if (!osd.getVisible())
590     {
591       osd.setVisible(true);
592       draw();
593     }
594     else
595     {
596       osdChanNum.fillColour(DrawStyle::VIEWBACKGROUND);
597       osdChanNum.draw();
598     }
599     boxstack->update(this, osd.getRegion());
600     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
601   }
602   delete[] keyingString;
603 }
604
605 void VVideoLiveTV::doTeletext(bool subtitlemode)
606 {
607   if (streamType != VDR::VIDEO) return;
608
609   PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
610
611   playervl->tellSubtitlesOSDVisible(true);
612   bool exists = true;
613
614   // Cancel keying
615   if (keying)
616   {
617     keying = 0;
618     // and reset the display - this is a copy from setNowNextData
619     char formatChanNum[20];
620     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
621     osdChanNum.setText(formatChanNum);
622     osdChanName.setText((*chanList)[osdChannelIndex]->name);
623   }
624   playervl->tellSubtitlesOSDVisible(true);
625   if (osd.getVisible()) clearScreen();
626   // Draw the teletxt
627   VTeletextView* vtxv = playervl->getTeletextDecoder()->getTeletxtView();
628   if (vtxv == NULL)
629   {
630     vtxv = new VTeletextView(playervl->getTeletextDecoder(), this, playervl);
631     playervl->getTeletextDecoder()->registerTeletextView(vtxv);
632     exists=false;
633   }
634
635   vtxv->setSubtitleMode(subtitlemode);
636   vtxv->draw();
637   draw();
638   
639   if (!exists)
640   {
641     BoxStack::getInstance()->add(vtxv);
642   }
643
644   BoxStack::getInstance()->update(this);
645   BoxStack::getInstance()->update(vtxv); 
646 }
647
648 void VVideoLiveTV::doAudioSelector()
649 {
650   // Only called if streamtype == VDR::VIDEO
651   PlayerVideoLive* playervl = static_cast<PlayerVideoLive*>(player);
652
653   // If the osd is already visisble there might be a timer for it
654   Timers::getInstance()->cancelTimer(this, 1);
655   //This causes a deadlock with the timertrhread itself is locked
656
657   // Cancel keying
658   if (keying)
659   {
660     keying = 0;
661     // and reset the display - this is a copy from setNowNextData
662     char formatChanNum[20];
663     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
664     osdChanNum.setText(formatChanNum);
665     osdChanName.setText((*chanList)[osdChannelIndex]->name);
666   }
667   int subtitleChannel = playervl->getCurrentSubtitleChannel();
668   int subtitleType = 0x10;
669   if (!playervl->isSubtitlesOn())
670   {
671     if (playervl->getTeletextDecoder()->getTeletxtView() &&
672         playervl->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode())
673     {
674       subtitleChannel = playervl->getTeletextDecoder()->getPage();
675       subtitleType = 0x11;
676     }
677     else
678     {
679       subtitleType = 0xFF; //turnedOff
680       subtitleChannel = 0;
681     }
682   }
683
684   // Draw the selector
685   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], playervl->getCurrentAudioChannel(),
686       subtitleType,subtitleChannel,NULL);
687
688   vas->setBackgroundColour(DrawStyle::OSDBACKGROUND);
689   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
690   vas->draw();
691
692   // make vas != null and displayOSD will not set a timer or do any boxstack update
693   if (osd.getVisible()) displayOSD(false);
694   else displayOSD(true);
695   draw();
696
697   BoxStack::getInstance()->add(vas);
698   BoxStack::getInstance()->update(this);        
699   BoxStack::getInstance()->update(vas);     
700 }      
701       
702 void VVideoLiveTV::doEPG()
703 {
704   if (osd.getVisible()) clearScreen();
705
706   if (!Control::getInstance()->isAdvMenus())
707   {
708     VEpg* vepg = new VEpg(this, currentChannelIndex, chanList);
709     vepg->draw();
710     boxstack->add(vepg);
711     boxstack->update(vepg);
712   }
713   else
714   {
715     Channel* currentChannel = (*chanList)[osdChannelIndex];
716     VEpgListAdvanced  *vepg= new VEpgListAdvanced(this, chanList, currentChannel->number);
717     vepg->draw();
718     boxstack->add(vepg);
719     boxstack->update(vepg);
720   }
721 }
722
723 void VVideoLiveTV::setNowNextData()
724 {
725   delData();
726   
727   Channel* currentChannel = (*chanList)[osdChannelIndex];
728
729   char formatChanNum[20];
730   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
731   osdChanNum.setText(formatChanNum);
732   osdChanName.setText(currentChannel->name);
733
734   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
735
736   if (!eventList)
737   {
738     sl.setText(tr("No channel data available"));
739   }
740   else
741   {
742     sort(eventList->begin(), eventList->end(), EventSorter());
743
744     char tempString[300];
745     struct tm btime;
746     Event* event;
747     int eventListSize = eventList->size();
748     std::stringstream ss;
749     for(int i = 0; i < eventListSize; i++)
750     {
751       event = (*eventList)[i];
752
753       time_t etime = event->time;
754       LOCALTIME_R(&etime, &btime);
755       // FIXME convert following to std::strftime
756 #ifndef _MSC_VER
757       strftime(tempString, 299, "%0H:%0M ", &btime);
758 #else
759       strftime(tempString, 299, "%H:%M ", &btime);
760 #endif
761       ss << tempString << " " << event->title << "\n";
762     }
763     sl.setText(ss.str().c_str());
764   }
765 }
766
767
768
769 void VVideoLiveTV::displayOSD(bool newNowNextData)
770 {
771   PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
772   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(true);
773   osd.setVisible(true);
774   if (newNowNextData)
775   {
776     setNowNextData();
777     updatePosterBanner();
778     keying = 0;
779   }
780   osd.draw();
781   if (osdposterbanner.getVisible()) osdposterbanner.draw();
782   
783
784   Region  toupdate;
785
786   toupdate=*osd.getRegion();
787
788
789   if (osdposterbanner.getVisible())
790   {
791     boxstack->update(this);
792   }
793   else
794   {
795     boxstack->update(this, &toupdate);
796   }
797   
798   bool setTimer = true;
799   if (vas) setTimer = false;
800   if (textUnavailable.getVisible()) setTimer = false;
801
802   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
803 }
804
805 void VVideoLiveTV::clearScreen()
806 {  
807   Timers::getInstance()->cancelTimer(this, 1);
808
809   textUnavailable.setVisible(false);
810   osd.setVisible(false);
811   osdposterbanner.setVisible(false);
812
813   okTriggeredOSD = false;
814
815   draw();
816   boxstack->update(this);
817
818   PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
819   if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
820 }
821
822 void VVideoLiveTV::showUnavailable()
823 {
824   LogNT::getInstance()->debug(TAG, "Show unavailable called");
825   textUnavailable.setVisible(true);
826   textUnavailable.draw();
827   
828   if (!osd.getVisible()) displayOSD(true);
829
830   boxstack->update(this, textUnavailable.getRegion());  
831 }
832
833 void VVideoLiveTV::setClock()
834 {
835   char timeString[20];
836   time_t t;
837   time(&t);
838   struct tm tms;
839   LOCALTIME_R(&t, &tms);
840   strftime(timeString, 19, "%H:%M", &tms);
841   clock.setText(timeString);
842
843   time_t dt = 60 - (t % 60);  // seconds to the next minute
844   if (dt == 0) dt = 60; // advance a whole minute if necessary
845   dt += t;  // get a time_t value for it rather than using duration
846   // (so it will occur at the actual second and not second and a half)
847
848   Timers::getInstance()->setTimerT(this, 2, dt);
849 }
850
851 void VVideoLiveTV::timercall(int ref)
852 {
853   if (ref == 1)
854   {
855     if (keying)
856     {
857       LogNT::getInstance()->debug(TAG, "Timer Call 1 key start.");
858       UINT newChannel = 0;
859       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * static_cast<int>(pow(10.f, i));
860       
861       Message* m = new Message();
862       m->message = Message::CHANNEL_CHANGE;
863       m->to = this;
864       m->parameter = newChannel;
865       m->tag = 1; // signal to call displayOSD();
866       MessageQueue::getInstance()->postMessage(m);
867       LogNT::getInstance()->debug(TAG, "Timer Call 1 key end.");
868     }
869     else
870     {
871       LogNT::getInstance()->debug(TAG, "Timer Call 1 not key start.");
872       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
873       if (preBuffering < 100)
874       {
875         LogNT::getInstance()->debug(TAG, "Still prebuffering, not removing osd...");
876         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
877         return;
878       }
879       bool osdpbvisible=osdposterbanner.getVisible();
880       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 1.");
881       osd.setVisible(false);
882       osdposterbanner.setVisible(false);
883       okTriggeredOSD = false;
884       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 2.");
885       draw();
886       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 4.");
887       if (osdpbvisible) boxstack->update(this);
888       else  boxstack->update(this, osd.getRegion());
889
890       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey 3.");
891       PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
892       if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
893       LogNT::getInstance()->debug(TAG, "Timer Call 1 notkey end.");
894     }
895   }
896   else if (ref == 2)
897   {
898     LogNT::getInstance()->debug(TAG, "Timer Call 2  start.");
899     setClock();
900     if (osd.getVisible())
901     {
902       clock.fillColour(DrawStyle::OSDBACKGROUND);
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 }