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