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