]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Add more flexible video display options
[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, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "vvideolivetv.h"
22
23 #include "vchannellist.h"
24 #include "video.h"
25 #include "audio.h"
26 #include "playerlive.h"
27 #include "playerlivetv.h"
28 #include "playerliveradio.h"
29 #include "channel.h"
30 #include "boxstack.h"
31 #include "colour.h"
32 #include "osd.h"
33 #include "command.h"
34 #include "i18n.h"
35 #include "wtextbox.h"
36 #include "remote.h"
37 #include "vaudioselector.h"
38 #include "colour.h"
39 #include "event.h"
40 #include "timers.h"
41 #include "vepg.h"
42 #include "bitmap.h"
43 #include "log.h"
44 #include "vteletextview.h"
45 #include "vepgsummary.h"
46 #include "vepglistadvanced.h"
47
48 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
49 : osdBack(0, 0, 0, 128)
50 {
51   vdr = VDR::getInstance();
52   boxstack = BoxStack::getInstance();
53   video = Video::getInstance();
54   
55   vas = NULL;
56
57   chanList = tchanList;
58   vchannelList = tvchannelList;
59   numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
60
61   currentChannelIndex = 0;
62   previousChannelIndex = 0;
63   osdChannelIndex = 0;
64   keying = 0;
65   preBuffering = 0;
66
67   playing = false;
68
69   // Convert channel number to index
70   UINT i;
71   for(i = 0; i < chanList->size(); i++)
72   {
73     if ((*chanList)[i]->number == (UINT)initialChannelNumber)
74     {
75       currentChannelIndex = i;
76       osdChannelIndex = i;
77       break;
78     }
79   }
80
81   eventList = NULL;
82
83   videoMode = video->getMode();
84   
85   if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
86   {
87     streamType = VDR::VIDEO;
88     player = new PlayerLiveTV(Command::getInstance(), this, this, chanList);
89   }
90   else
91   {
92     streamType = VDR::RADIO;
93     player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
94   }
95   player->init();
96
97   setSize(video->getScreenWidth(), video->getScreenHeight());
98   createBuffer();
99   DrawStyle transparent(0, 0, 0, 0);
100   setBackgroundColour(transparent);
101 #ifdef PAL_WSS
102   dowss = false;
103   char* optionWSS = vdr->configLoad("General", "WSS");
104   if (optionWSS)
105   {
106     if (strstr(optionWSS, "Yes")) dowss = true;
107     delete[] optionWSS;
108   }
109   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
110
111   if (dowss)
112   {
113     wss.setFormat(video->getFormat());
114     wss.setWide(true);
115     add(&wss);
116     
117     wssRegion.x = 0;
118     wssRegion.y = 6;
119     wssRegion.w = video->getScreenWidth();
120     wssRegion.h = 2;
121   }
122 #endif
123   // This variable is set to true if the user pressed OK to bring the OSD on screen
124   // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling
125   okTriggeredOSD = false;
126
127   TVMediaInfo info;
128   info.setChannelLogo(initialChannelNumber);
129   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
130
131   if (osdv && streamType == VDR::RADIO) {
132           radioChannelLogo.setPosition(video->getScreenWidth()/4,video->getScreenHeight()/4);
133           radioChannelLogo.setSize(video->getScreenWidth()/2,video->getScreenHeight()/2);
134           radioChannelLogo.setVisible(true);
135           radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
136           add(&radioChannelLogo);
137   }
138
139   osdposterbanner.setPosition(20,20);
140   osdposterbanner.setBackgroundColour(osdBack);
141   osdposterbanner.setSize(video->getScreenWidth()*4/10,video->getScreenHeight()*4/10);
142   osdposterbanner.setVisible(false);
143   add(&osdposterbanner);
144
145
146
147   osd.setBackgroundColour(osdBack);
148   osd.setPosition(0, video->getScreenHeight() - 150);
149   osd.setSize(video->getScreenWidth(), 150);
150   osd.setVisible(false);
151   add(&osd);
152   
153   int channellogomove=0;
154   if (osdv) {
155           osdChannelLogo.setBackgroundColour(osdBack);
156           osdChannelLogo.setPosition(30,5);
157           osdChannelLogo.setSize(60,60);
158           osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
159           osd.add(&osdChannelLogo);
160           channellogomove=30-5;
161   }
162
163   clock.setBackgroundColour(osdBack);
164   clock.setPosition(osd.getWidth() - 100, 4);
165   clock.setSize(90, 30);
166   osd.add(&clock);
167
168   osdChanNum.setBackgroundColour(osdBack);
169   osdChanNum.setPosition(60+channellogomove, 4);
170   osdChanNum.setSize((numberWidth*10) + 22, getFontHeight()+5); // 10 px = width of number chars in font
171   osd.add(&osdChanNum);  
172
173   osdChanName.setBackgroundColour(osdBack);
174   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
175   osdChanName.setSize(300, 30);
176   osd.add(&osdChanName);
177   
178   boxRed.setBackgroundColour(DrawStyle::RED);
179   boxRed.setPosition(54, 104);
180   boxRed.setSize(18, 16);
181   osd.add(&boxRed);
182
183   boxGreen.setBackgroundColour(DrawStyle::GREEN);
184   boxGreen.setPosition(220, 104);
185   boxGreen.setSize(18, 16);
186   osd.add(&boxGreen);
187
188   boxYellow.setBackgroundColour(DrawStyle::YELLOW);
189   boxYellow.setPosition(390, 104);
190   boxYellow.setSize(18, 16);
191   osd.add(&boxYellow);
192
193   boxBlue.setBackgroundColour(DrawStyle::BLUE);
194   boxBlue.setPosition(560, 104);
195   boxBlue.setSize(18, 16);
196   osd.add(&boxBlue);  
197   
198   textRed.setBackgroundColour(osdBack);
199   textRed.setPosition(boxRed.getX2(), 98);
200   textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30);
201   textRed.setText(tr("Summary"));
202   osd.add(&textRed);  
203     
204   if (streamType == VDR::VIDEO)
205   {
206     textGreen.setBackgroundColour(osdBack);
207     textGreen.setPosition(boxGreen.getX2(), 98);
208     textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30);
209     textGreen.setText(tr("Audio"));
210     osd.add(&textGreen);  
211   }
212     
213   textYellow.setBackgroundColour(osdBack);
214   textYellow.setPosition(boxYellow.getX2(), 98);
215   textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30);
216   textYellow.setText(tr("Teletext"));
217   osd.add(&textYellow);  
218     
219   textBlue.setBackgroundColour(osdBack);
220   textBlue.setPosition(boxBlue.getX2(), 98);
221   textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30);
222   textBlue.setText(tr("EPG"));
223   osd.add(&textBlue);  
224     
225   sl.setBackgroundColour(osdBack);
226   sl.setPosition(90, 36);
227   sl.setSize(480, 58);
228   sl.setNoLoop();
229   osd.add(&sl);
230   
231   // Summary Box
232   summary.setBackgroundColour(osdBack);
233   summary.setPosition(0, video->getScreenHeight() - 300);
234   summary.setSize(video->getScreenWidth(), 150);
235   summary.setVisible(false);
236   add(&summary);  
237   
238   textSummary.setBackgroundColour(osdBack);
239   textSummary.setPosition(40, 10);
240   textSummary.setSize(video->getScreenWidth() - 80, 130);
241   textSummary.setParaMode(true);
242   summary.add(&textSummary);
243   
244   summaryBlackLine.setBackgroundColour(DrawStyle::BLACK);
245   summaryBlackLine.setPosition(0, summary.getHeight() - 4);
246   summaryBlackLine.setSize(summary.getWidth(), 4);
247   summary.add(&summaryBlackLine);
248   
249   sAspectRatio.setPosition(osd.getWidth() - 90, 40);
250   sAspectRatio.nextColour = DrawStyle::SELECTHIGHLIGHT;
251   sAspectRatio.setVisible(false);
252   osd.add(&sAspectRatio);
253   
254   bufferBar.setPosition(osd.getWidth() - 90, 70);
255   bufferBar.setSize(40, 20);
256   bufferBar.setVisible(true);
257   osd.add(&bufferBar);
258   
259   sAudioChannels.setPosition(osd.getWidth() - 130, 40);
260   sAudioChannels.nextColour = DrawStyle::SELECTHIGHLIGHT;
261   sAudioChannels.setVisible(false);
262   osd.add(&sAudioChannels);
263   
264   textUnavailable.setBackgroundColour(osdBack);
265   textUnavailable.setPosition(60, 30);
266   textUnavailable.setSize(osd.getWidth() - 120, 30);
267   textUnavailable.setText(tr("Channel Unavailable"));
268   textUnavailable.setVisible(false);
269   add(&textUnavailable);
270   
271   // FIXME painful
272   Region r1 = summary.getRegionR();
273   Region r2 = osd.getRegionR();
274   osdSummaryRegion = r1 + r2;
275
276   vdisplay.mode=Fullscreen;
277   vdisplay.fallbackMode=Fullscreen;
278   vdisplay.x=0;
279   vdisplay.y=0;
280   vdisplay.width=0;
281   vdisplay.height=0;
282 }
283
284 void VVideoLiveTV::preDelete()
285 {
286   if (playing) stop();
287 }
288
289 VVideoLiveTV::~VVideoLiveTV()
290 {
291   delete player;
292   video->setDefaultAspect();
293   delData();
294 }
295
296 void VVideoLiveTV::delData()
297 {
298   if (eventList)
299   {
300     int eventListSize = eventList->size();
301     for(int i = 0; i < eventListSize; i++)
302     {
303       delete (*eventList)[i];
304     }
305     eventList->clear();
306     delete eventList;
307
308   }
309   sl.clear();
310 }
311
312 int VVideoLiveTV::handleCommand(int command)
313 {
314   switch(command)
315   {
316     case Remote::BACK:
317     {
318       if (osd.getVisible() && !textUnavailable.getVisible())
319       {
320         clearScreen();
321         return 2;
322       }
323       // else drop through to stop
324     }
325     case Remote::STOP:
326     {
327       stop();
328       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
329       return 4;
330     }
331     
332     // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there
333     case Remote::UP:
334     {
335         if (Remote::getInstance()->mayHaveFewButtons()) {
336               if (okTriggeredOSD) doUpDown(false);
337               else doChanUpDown(UP);
338         } else {
339                 doUpDown(false);
340         }
341       return 2;
342     }
343     case Remote::DOWN:
344     {
345       if (Remote::getInstance()->mayHaveFewButtons()) {
346           if (okTriggeredOSD) doUpDown(true);
347           else doChanUpDown(DOWN);
348       } else {
349           doUpDown(true);
350       }
351       return 2;
352     }
353     case Remote::LEFT:
354     {
355       doLeftRight(false);
356       return 2;
357     }
358     case Remote::RIGHT:
359     {
360       doLeftRight(true);
361       return 2;
362     }
363     // Continue new remote only...
364     case Remote::CHANNELUP:
365     {
366       doChanUpDown(UP);
367       return 2;
368     }
369     case Remote::CHANNELDOWN:
370     {
371       doChanUpDown(DOWN);
372       return 2;
373     }
374
375     // END NEW REMOTE ONLY, START OLD REMOTE ONLY
376     
377     // DF_LEFT and DF_RIGHT never get here because they are stolen
378     // by command as vol- and vol+
379     
380     // Old remote. Decide what to do based on whether
381     // OK was pressed - osd shown manually, use up/down for epg nav
382     // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch-
383     
384     case Remote::DF_UP:
385     {
386       // Old remote, decide what to do based on okTriggeredOSD
387       if (okTriggeredOSD) doUpDown(false);
388       else doChanUpDown(UP);
389       return 2;
390     }
391     case Remote::DF_DOWN:
392     {
393       // Old remote, decide what to do based on okTriggeredOSD
394       if (okTriggeredOSD) doUpDown(true);
395       else doChanUpDown(DOWN);
396       return 2;
397     }
398
399     // END NEW/OLD REMOTE STUFF
400
401     case Remote::PREVCHANNEL:
402     {
403       channelChange(PREVIOUS, 0);
404       osdChannelIndex = currentChannelIndex;
405       displayOSD(true);
406       return 2;
407     }
408     case Remote::OK:
409     {
410       doOK();
411       return 2;
412     }
413     case Remote::RED:
414     case Remote::MENU:
415     {
416       doSummary();
417       return 2;
418     }
419     case Remote::FULL:
420     case Remote::TV:
421     {
422       toggleChopSides();
423       return 2;
424     }
425
426     case Remote::ZERO:
427     case Remote::ONE:
428     case Remote::TWO:
429     case Remote::THREE:
430     case Remote::FOUR:
431     case Remote::FIVE:
432     case Remote::SIX:
433     case Remote::SEVEN:
434     case Remote::EIGHT:
435     case Remote::NINE:
436     {
437       // key in channel number
438       doKey(command);
439       return 2;
440     }
441
442     case Remote::GREEN:
443     {
444       if (streamType == VDR::VIDEO) doAudioSelector();
445       return 2;   
446     }
447     case Remote::YELLOW:
448     {
449       if (streamType ==VDR::VIDEO) 
450       {
451         doTeletext(); //TODO: Add a selector for subtitles or teletext
452       }
453       return 2;
454     }
455     case Remote::GUIDE:
456     case Remote::BLUE:
457     {
458       doEPG();
459       return 2;
460     }
461     case Remote::RECORD:
462       if (streamType == VDR::VIDEO)
463         (static_cast<PlayerLiveTV*>(player))->toggleSubtitles();
464       return 2;
465   }
466
467   return 1;
468 }
469
470 void VVideoLiveTV::go()
471 {
472   playing = true;
473   draw();
474   boxstack->update(this);
475
476   setClock();
477   displayOSD(true);
478   
479   player->go(currentChannelIndex);
480 }
481
482 void VVideoLiveTV::stop()
483 {
484   Timers::getInstance()->cancelTimer(this, 1);
485   Timers::getInstance()->cancelTimer(this, 2);
486   player->stop();
487   playing = false;
488 }
489
490 void VVideoLiveTV::doLeftRight(bool right)
491 {
492   if (osd.getVisible())
493   {
494     if (right) osdChannelIndex = upChannel(osdChannelIndex);
495     else       osdChannelIndex = downChannel(osdChannelIndex);
496   }
497   else
498   {
499     osdChannelIndex = currentChannelIndex;
500   }
501   displayOSD(true);
502 }
503
504 void VVideoLiveTV::doUpDown(bool down)
505 {
506   if (osd.getVisible())
507   {
508     if (down) sl.down();
509     else      sl.up();
510     sl.draw();
511     
512     displayOSD(false);
513   }
514   else
515   {
516     displayOSD(true);
517   }
518 }
519
520 void VVideoLiveTV::doChanUpDown(int which)
521 {
522   channelChange(OFFSET, which);
523   osdChannelIndex = currentChannelIndex;
524   displayOSD(true);
525 }
526
527 void VVideoLiveTV::doOK()
528 {
529   if (osd.getVisible())
530   {
531     if (keying)
532     {
533       UINT newChannel = 0;
534       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
535       
536       channelChange(NUMBER, newChannel);
537       osdChannelIndex = currentChannelIndex;
538       displayOSD(true);
539     }
540     else if (osdChannelIndex == currentChannelIndex)
541     {
542       clearScreen();
543     }
544     else
545     {
546       channelChange(INDEX, osdChannelIndex);
547       displayOSD(true);
548     }
549   }
550   else
551   {
552     osdChannelIndex = currentChannelIndex;
553     displayOSD(true);
554     okTriggeredOSD = true;
555   }
556 }
557
558 void VVideoLiveTV::doSummary()
559 {
560         displayOSD(false);
561         Channel* currentChannel = (*chanList)[osdChannelIndex];
562         eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
563         if (!eventList)
564         {
565                 return;
566         }
567         sort(eventList->begin(), eventList->end(), EventSorter());
568
569         if (eventList->size() < 1) return;
570         Event* event = (*eventList)[0];
571         event->loadinfos(currentChannel->number);
572
573         VEpgSummary* vr = new VEpgSummary(event,currentChannel);
574         vr->draw();
575         boxstack->add(vr);
576         boxstack->update(vr);
577 /*
578   if (summary.getVisible())
579   {
580     summary.setVisible(false);
581     draw();
582     boxstack->update(this, summary.getRegion());
583     Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
584     return;
585   }
586
587   summary.setVisible(true);
588
589   if (osd.getVisible())
590   {
591     Timers::getInstance()->cancelTimer(this, 1);
592     displayOSD(false);
593   }
594   else
595   {
596     displayOSD(true);
597   }*/
598 }
599
600 void VVideoLiveTV::doKey(int command)
601 {
602   if (!osd.getVisible()) // First key. prep the data
603   {
604     setNowNextData();
605     updatePosterBanner();
606     keying = 0;    
607   }
608
609   int i;
610   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
611   keyingInput[0] = command;
612   keying++;
613
614   char* keyingString = new char[numberWidth + 1];
615   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
616   keyingString[numberWidth] = '\0';
617
618   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
619   
620   if (keying == numberWidth)
621   {
622     UINT newChannel = 0;
623     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
624     
625     channelChange(NUMBER, newChannel);
626     osdChannelIndex = currentChannelIndex;
627     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
628     displayOSD(true); // this will put one back if required
629   }
630   else
631   {
632     osdChanNum.setText(keyingString);
633
634     if (!osd.getVisible())
635     {
636       osd.setVisible(true);
637       draw();
638     }
639     else
640     {
641       osdChanNum.draw();
642     }
643     boxstack->update(this, osd.getRegion());
644     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
645   }
646   delete[] keyingString;
647 }
648
649 void VVideoLiveTV::doTeletext(bool subtitlemode)
650 {
651   if (streamType !=VDR::VIDEO) return;
652   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
653   bool exists=true;
654
655   // Cancel keying
656   if (keying)
657   {
658     keying = 0;
659     // and reset the display - this is a copy from setNowNextData
660     char formatChanNum[20];
661     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
662     osdChanNum.setText(formatChanNum);
663     osdChanName.setText((*chanList)[osdChannelIndex]->name);
664   }
665   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
666   if (osd.getVisible()) clearScreen();
667   // Draw the teletxt
668   VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
669   if (vtxv==NULL) {
670        vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this, (PlayerLiveTV*)player);
671       ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv);
672       exists=false;
673   }
674   vtxv->setSubtitleMode(subtitlemode);
675   vtxv->draw();
676   draw();
677   
678   if (!exists) {
679       BoxStack::getInstance()->add(vtxv);
680   }
681   BoxStack::getInstance()->update(this);
682   BoxStack::getInstance()->update(vtxv); 
683 }
684
685 void VVideoLiveTV::doAudioSelector()
686 {
687   // If the osd is already visisble there might be a timer for it
688   Timers::getInstance()->cancelTimer(this, 1);
689   //This causes a deadlock with the timertrhread itself is locked
690
691
692   // Cancel keying
693   if (keying)
694   {
695     keying = 0;
696     // and reset the display - this is a copy from setNowNextData
697     char formatChanNum[20];
698     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
699     osdChanNum.setText(formatChanNum);
700     osdChanName.setText((*chanList)[osdChannelIndex]->name);
701   }
702   int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel();
703   int subtitleType=0x10;
704   if (!(static_cast<PlayerLiveTV*>(player))->isSubtitlesOn()) {
705       if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() &&
706           ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() 
707           ) {
708               subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage();
709               subtitleType=0x11;
710           
711       } else {
712           subtitleType=0xFF; //turnedOff
713           subtitleChannel=0;
714       }
715   }
716
717   // Draw the selector
718   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(),
719       subtitleType,subtitleChannel,NULL);
720
721   vas->setBackgroundColour(osdBack);
722   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
723   vas->draw();
724
725   // make vas != null and displayOSD will not set a timer or do any boxstack update
726   summary.setVisible(false);
727   if (osd.getVisible()) displayOSD(false);
728   else displayOSD(true);
729   draw();
730
731   BoxStack::getInstance()->add(vas);
732   BoxStack::getInstance()->update(this);        
733   BoxStack::getInstance()->update(vas);     
734 }      
735       
736 void VVideoLiveTV::doEPG()
737 {
738   if (osd.getVisible()) clearScreen();
739
740   if (!Command::getInstance()->advMenues()) {
741
742           VEpg* vepg = new VEpg(this, currentChannelIndex, chanList);
743           vepg->draw();
744           boxstack->add(vepg);
745           boxstack->update(vepg);
746   } else {
747           Channel* currentChannel = (*chanList)[osdChannelIndex];
748           VEpgListAdvanced  *vepg= new VEpgListAdvanced(this, chanList, currentChannel->number);
749           vepg->draw();
750           boxstack->add(vepg);
751           boxstack->update(vepg);
752   }
753 }
754
755 void VVideoLiveTV::setNowNextData()
756 {
757   delData();
758   
759   Channel* currentChannel = (*chanList)[osdChannelIndex];
760
761   char formatChanNum[20];
762   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
763   osdChanNum.setText(formatChanNum);
764   osdChanName.setText(currentChannel->name);
765
766   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
767
768   if (!eventList)
769   {
770     sl.addOption(tr("No channel data available"), 0, 1);
771   }
772   else
773   {
774     sort(eventList->begin(), eventList->end(), EventSorter());
775
776     char tempString[300];
777     char tempString2[300];
778     struct tm* btime;
779     Event* event;
780     int eventListSize = eventList->size();
781     for(int i = 0; i < eventListSize; i++)
782     {
783       event = (*eventList)[i];
784
785       //btime = localtime((time_t*)&event->time);
786       time_t etime = event->time;
787       btime = localtime(&etime);
788 #ifndef _MSC_VER
789       strftime(tempString2, 299, "%0H:%0M ", btime);
790 #else
791       strftime(tempString2, 299, "%H:%M ", btime);
792 #endif
793       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
794       
795       sl.addOption(tempString, (ULONG)event, (i==0));
796     }
797   }
798 }
799
800 void VVideoLiveTV::setSummaryData()
801 {
802   // If osd is not being displayed, sl will be filled with now, current channel
803   // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
804   Event* selectedEvent = (Event*)sl.getCurrentOptionData();
805   
806   if (!selectedEvent)
807   {
808     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");  
809     textSummary.setText(tr("No summary available"));
810   }
811   else
812   {
813     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);  
814     textSummary.setText(selectedEvent->description);
815   }
816 }
817
818 void VVideoLiveTV::displayOSD(bool newNowNextData)
819 {
820   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
821   osd.setVisible(true);
822   if (newNowNextData)
823   {
824     setNowNextData();
825     updatePosterBanner();
826     keying = 0;
827   }
828   osd.draw();
829   if (osdposterbanner.getVisible()) osdposterbanner.draw();
830   
831
832   Region  toupdate;
833   if (summary.getVisible())
834   {
835     setSummaryData();
836     summary.draw();
837     toupdate=osdSummaryRegion;
838   }
839   else
840   {
841         toupdate=*osd.getRegion();
842   }
843
844   if (osdposterbanner.getVisible()) {
845           boxstack->update(this);
846   } else {
847           boxstack->update(this, &toupdate);
848   }
849   
850   bool setTimer = true;
851   if (vas) setTimer = false;
852   if (summary.getVisible()) setTimer = false;
853   if (textUnavailable.getVisible()) setTimer = false;
854
855   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
856 }
857
858 void VVideoLiveTV::clearScreen()
859 {  
860   if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
861
862   textUnavailable.setVisible(false);
863   osd.setVisible(false);
864   summary.setVisible(false);
865   osdposterbanner.setVisible(false);
866
867   okTriggeredOSD = false;
868
869   draw();
870   boxstack->update(this);
871
872   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(false);
873 }
874
875 void VVideoLiveTV::showUnavailable()
876 {
877   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");  
878   textUnavailable.setVisible(true);
879   textUnavailable.draw();
880   
881   if (!osd.getVisible()) displayOSD(true);
882
883   boxstack->update(this, textUnavailable.getRegion());  
884 }
885
886 void VVideoLiveTV::setClock()
887 {
888   char timeString[20];
889   time_t t;
890   time(&t);
891   struct tm* tms = localtime(&t);
892   strftime(timeString, 19, "%H:%M", tms);
893   clock.setText(timeString);
894
895   time_t dt = 60 - (t % 60);  // seconds to the next minute
896   if (dt == 0) dt = 60; // advance a whole minute if necessary
897   dt += t;  // get a time_t value for it rather than using duration
898   // (so it will occur at the actual second and not second and a half)
899
900   Timers::getInstance()->setTimerT(this, 2, dt);
901 }
902
903 void VVideoLiveTV::timercall(int ref)
904 {
905   if (ref == 1)
906   {
907     if (keying)
908     {
909          Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); 
910       UINT newChannel = 0;
911       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
912       
913       Message* m = new Message();
914       m->message = Message::CHANNEL_CHANGE;
915       m->to = this;
916       m->parameter = newChannel;
917       m->tag = 1; // signal to call displayOSD();
918       Command::getInstance()->postMessageFromOuterSpace(m);
919       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); 
920     }
921     else
922     {
923         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); 
924       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
925       if (preBuffering < 100)
926       {
927         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");  
928         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
929         return;
930       }
931       bool osdpbvisible=osdposterbanner.getVisible();
932       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); 
933       osd.setVisible(false);
934       osdposterbanner.setVisible(false);
935       okTriggeredOSD = false;
936       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); 
937       draw();
938       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); 
939       if (osdpbvisible) boxstack->update(this);
940       else  boxstack->update(this, osd.getRegion());
941
942       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); 
943       (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(false);
944       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); 
945     }
946   }
947   else if (ref == 2)
948   {
949       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2  start."); 
950     setClock();
951     if (osd.getVisible())
952     {
953       clock.draw();
954       boxstack->update(this, osd.getRegion());
955     }
956     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); 
957   }
958 }
959
960 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
961 {
962   UINT newChannel = 0;
963   if (streamType ==VDR::VIDEO) {
964   VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
965     if (vtxt ) {
966          BoxStack::getInstance()->remove(vtxt);
967
968     }
969   }
970   if (changeType == INDEX)
971   {
972     newChannel = newData;
973   }
974   else if (changeType == NUMBER)
975   {
976     UINT i;
977     for(i = 0; i < chanList->size(); i++)
978     {
979       if ((*chanList)[i]->number == (UINT)newData)
980       {
981         newChannel = i;
982         break;
983       }
984     }
985
986     if (i == chanList->size())
987     {
988       // no such channel
989       return false;
990     }
991   }
992   else if (changeType == OFFSET)
993   {
994     if (newData == UP) newChannel = upChannel(currentChannelIndex);
995     else newChannel = downChannel(currentChannelIndex);
996   }
997   else if (changeType == PREVIOUS)
998   {
999     newChannel = previousChannelIndex;
1000   }
1001   else
1002   {
1003     return false; // bad input
1004   }
1005
1006   if (newChannel == currentChannelIndex) return true;
1007
1008   previousChannelIndex = currentChannelIndex;
1009   currentChannelIndex = newChannel;
1010   
1011   preBuffering = 0;
1012   
1013   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
1014   player->setChannel(currentChannelIndex);
1015   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
1016
1017   // Blank out the symbols
1018   sAspectRatio.setVisible(false);
1019   bufferBar.setPercent(0);
1020   sAudioChannels.setVisible(false);
1021   
1022   // Remove other stuff
1023   if (textUnavailable.getVisible())
1024   {
1025     textUnavailable.setVisible(false);
1026     
1027   }
1028   OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1029   if (osdv) {
1030           TVMediaInfo info;
1031           Channel* currentChannel = (*chanList)[currentChannelIndex];
1032           if (currentChannel) {
1033                   info.setChannelLogo(currentChannel->number);
1034                   if (streamType == VDR::RADIO) {
1035                           radioChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1036                           radioChannelLogo.setVisible(true);
1037                   }
1038                   osdChannelLogo.setTVMedia(info, WTVMedia::ZoomVertical);
1039
1040           }
1041   }
1042
1043   draw();
1044   BoxStack::getInstance()->update(this);
1045   
1046   return true;
1047 }
1048
1049 void VVideoLiveTV::processMessage(Message* m)
1050 {
1051   if (m->message == Message::MOUSE_LBDOWN)
1052   {
1053     //check if press is outside this view! then simulate cancel
1054     int x=(m->parameter>>16)-osd.getScreenX();
1055     int y=(m->parameter&0xFFFF)-osd.getScreenY();
1056     if (osd.getVisible()) {
1057     
1058         if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
1059             (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
1060             BoxStack::getInstance()->handleCommand(Remote::RED);
1061         } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
1062             (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
1063             BoxStack::getInstance()->handleCommand(Remote::GREEN);
1064         } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
1065             (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
1066             BoxStack::getInstance()->handleCommand(Remote::YELLOW);
1067         } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
1068             (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
1069             BoxStack::getInstance()->handleCommand(Remote::BLUE);
1070         } else {
1071             BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1072         }
1073
1074     } else {
1075         BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1076     }
1077   }
1078   else if (m->message == Message::CHANNEL_CHANGE)
1079   {
1080     channelChange(NUMBER, m->parameter);
1081     osdChannelIndex = currentChannelIndex;
1082     if (m->tag == 1) displayOSD(true);
1083   }
1084   else if (m->message == Message::CHILD_CLOSE)
1085   {
1086     if (m->from == vas)
1087     {
1088       vas = NULL;
1089       displayOSD(false);
1090     }
1091   }
1092   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
1093   {
1094     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %x", m->parameter);
1095     player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16,(m->parameter & 0xFF000000)>>24);
1096   } 
1097   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
1098   {
1099     if (streamType !=VDR::VIDEO) return;
1100       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %x", m->parameter);
1101       int type=((m->parameter & 0xFF0000)>>16);
1102       switch (type) {
1103       case 0x10: { //dvbsubtitle
1104           if (streamType == VDR::VIDEO){
1105               player->setSubtitleChannel((m->parameter & 0xFFFF));
1106               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(true);
1107               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1108               if (vtxt && vtxt->isInSubtitleMode()) {
1109                   BoxStack::getInstance()->remove(vtxt);
1110               }
1111           }
1112                  } break;
1113       case 0xFF: { //nosubtitles
1114           if (streamType == VDR::VIDEO){
1115               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1116               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1117               if (vtxt && vtxt->isInSubtitleMode()) {
1118                   BoxStack::getInstance()->remove(vtxt);
1119               }  
1120           }
1121                  } break;
1122       case 0x11: { //videotext
1123           (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1124           doTeletext(true);
1125           ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
1126                  } break;
1127       };
1128       if (vas) {
1129         BoxStack::getInstance()->update(vas);
1130         //BoxStack::getInstance()->update(&osd); //eveil error
1131       }
1132       BoxStack::getInstance()->update(this, osd.getRegion());
1133
1134       
1135   }
1136   else if (m->message == Message::PLAYER_EVENT)
1137   {
1138     switch(m->parameter)
1139     {
1140       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
1141       {
1142         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
1143         Command::getInstance()->connectionLost();
1144         break;
1145       }
1146       
1147       case PlayerLiveTV::STREAM_END:
1148       {
1149         // Message comes from playerlivetv through master mutex, so can do anything here
1150         showUnavailable();        
1151         break;
1152       }
1153       
1154       case PlayerLiveTV::ASPECT43:
1155       {
1156 #ifdef PAL_WSS
1157         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1158         {
1159           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
1160           wss.setWide(false);
1161           wss.draw();
1162           BoxStack::getInstance()->update(this, &wssRegion);
1163         }
1164 #endif
1165         
1166         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
1167         sAspectRatio.setVisible(true);
1168         
1169         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1170         {
1171           osd.draw();
1172           BoxStack::getInstance()->update(this, osd.getRegion());
1173         }
1174         
1175         break;
1176       }
1177       case PlayerLiveTV::ASPECT169:
1178       {
1179 #ifdef PAL_WSS
1180         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1181         {
1182           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
1183           wss.setWide(true);
1184           wss.draw();
1185           BoxStack::getInstance()->update(this, &wssRegion);
1186         }
1187 #endif
1188         
1189         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
1190         sAspectRatio.setVisible(true);
1191
1192         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1193         {
1194           osd.draw();
1195           BoxStack::getInstance()->update(this, osd.getRegion());
1196         }
1197                 
1198         break;
1199       }
1200       case PlayerLiveTV::PREBUFFERING:
1201       {
1202         preBuffering = m->tag;
1203         Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);
1204         bufferBar.setPercent(preBuffering);
1205
1206         if (osd.getVisible())
1207         {
1208           bufferBar.setVisible(true);
1209           bufferBar.draw();
1210           Region r;
1211           bufferBar.getRootBoxRegion(&r);                       ///////// FIXME !!!
1212           BoxStack::getInstance()->update(this, &r);
1213
1214           if (preBuffering == 100)
1215           {
1216             doAudioChannelSymbol();
1217           }
1218         }
1219       }
1220     }
1221   }
1222 }
1223
1224 void VVideoLiveTV::doAudioChannelSymbol()
1225 {
1226   // get the doobery
1227   Channel* currentChannel = (*chanList)[osdChannelIndex];
1228     
1229   bool multiAudio = false;
1230   if (Audio::getInstance()->supportsAc3()) {
1231           if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true;
1232   }
1233   if (currentChannel->numAPids > 1) multiAudio = true;
1234   
1235   // draw the doobery
1236   
1237   if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1238   else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1239   sAudioChannels.setVisible(true);
1240   
1241   if (osd.getVisible())
1242   {
1243     sAudioChannels.draw();
1244     Region r;
1245     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!
1246     // Fix this n'all.
1247     r.w = 32;
1248     r.h = 16;
1249     BoxStack::getInstance()->update(this, &r);
1250   }
1251 }
1252
1253 UINT VVideoLiveTV::upChannel(UINT index)
1254 {
1255   if (index == (chanList->size() - 1)) // at the end
1256     return 0; // so go to start
1257   else
1258     return index + 1;
1259 }
1260
1261 UINT VVideoLiveTV::downChannel(UINT index)
1262 {
1263   if (index == 0) // at the start
1264     return chanList->size() - 1; // so go to end
1265   else
1266     return index - 1;
1267 }
1268
1269 void VVideoLiveTV::toggleChopSides()
1270 {
1271   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1272
1273   if (videoMode == Video::NORMAL)
1274   {
1275     videoMode = Video::LETTERBOX;
1276     video->setMode(Video::LETTERBOX);
1277   }
1278   else
1279   {
1280     videoMode = Video::NORMAL;
1281     video->setMode(Video::NORMAL);
1282   }
1283 }
1284
1285 void VVideoLiveTV::updatePosterBanner()
1286 {
1287         OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1288         if (!osdv) {
1289                 osdposterbanner.setVisible(false);
1290                 return;
1291         }
1292         Event* toShow = NULL;
1293         if (eventList && eventList->size()) {
1294                 toShow = (*eventList)[0];
1295                 Channel* currentChannel = (*chanList)[osdChannelIndex];
1296                 toShow->loadinfos(currentChannel->number);
1297         }
1298
1299         if (toShow)
1300         {
1301                 TVMedia poster;
1302                 poster.height=0;
1303                 bool posterscale=true;
1304                 if (toShow->movieInfo) {
1305                         poster=toShow->movieInfo->poster;
1306                 }
1307                 if (toShow->seriesInfo) {
1308                         if (toShow->seriesInfo->banners.size()) {
1309                                 poster=toShow->seriesInfo->banners[0];
1310                                 posterscale=false;
1311                         } else if (toShow->seriesInfo->seasonposter.height) {
1312                                 poster=toShow->seriesInfo->seasonposter;
1313                         } else if (toShow->seriesInfo->posters.size()) {
1314                                 poster=toShow->seriesInfo->posters[0];
1315                         }
1316                 }
1317                 if (poster.height) {
1318                         osdposterbanner.setTVMedia(poster.info, posterscale ? WTVMedia::ZoomVertical : WTVMedia::ZoomHorizontal);
1319                         osdposterbanner.setVisible(true);
1320                 } else {
1321                         osdposterbanner.setVisible(false);
1322                 }
1323         } else {
1324                 osdposterbanner.setVisible(false);
1325         }
1326
1327 }
1328
1329
1330 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1331 {
1332   drawBitmap(posX, posY, bm,region);
1333   Region r;
1334   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1335   boxstack->update(this, &r);
1336 }
1337
1338 void VVideoLiveTV::clearOSD()
1339 {
1340   rectangle(area, DrawStyle(0,0,0,0));
1341   boxstack->update(this, &area);
1342 }
1343
1344 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1345 {
1346   Region r;
1347   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1348   //now convert to our display
1349   float scalex=720.f/((float) (region.framewidth+1));
1350   float scaley=576.f/((float) (region.frameheight+1));
1351   r.x=floor(scalex*((float)r.x));
1352   r.y=floor(scaley*((float)r.y));
1353   r.w=ceil(scalex*((float)r.w))+1.f;
1354   r.h=ceil(scaley*((float)r.h))+1.f;
1355   rectangle(r, DrawStyle(0,0,0,0));
1356   boxstack->update(this, &r);
1357 }