]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Preparations for dynamic mode switching
[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         if (Remote::getInstance()->mayHaveFewButtons()) {\r
299               if (okTriggeredOSD) doUpDown(false);\r
300               else doChanUpDown(UP);\r
301         } else {\r
302                 doUpDown(false);\r
303         }\r
304       return 2;\r
305     }\r
306     case Remote::DOWN:\r
307     {\r
308       if (Remote::getInstance()->mayHaveFewButtons()) {\r
309           if (okTriggeredOSD) doUpDown(true);\r
310           else doChanUpDown(DOWN);\r
311       } else {\r
312           doUpDown(true);\r
313       }\r
314       return 2;\r
315     }\r
316     case Remote::LEFT:\r
317     {\r
318       doLeftRight(false);\r
319       return 2;\r
320     }\r
321     case Remote::RIGHT:\r
322     {\r
323       doLeftRight(true);\r
324       return 2;\r
325     }\r
326     // Continue new remote only...\r
327     case Remote::CHANNELUP:\r
328     {\r
329       doChanUpDown(UP);\r
330       return 2;\r
331     }\r
332     case Remote::CHANNELDOWN:\r
333     {\r
334       doChanUpDown(DOWN);\r
335       return 2;\r
336     }\r
337 \r
338     // END NEW REMOTE ONLY, START OLD REMOTE ONLY\r
339     \r
340     // DF_LEFT and DF_RIGHT never get here because they are stolen\r
341     // by command as vol- and vol+\r
342     \r
343     // Old remote. Decide what to do based on whether\r
344     // OK was pressed - osd shown manually, use up/down for epg nav\r
345     // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch-\r
346     \r
347     case Remote::DF_UP:\r
348     {\r
349       // Old remote, decide what to do based on okTriggeredOSD\r
350       if (okTriggeredOSD) doUpDown(false);\r
351       else doChanUpDown(UP);\r
352       return 2;\r
353     }\r
354     case Remote::DF_DOWN:\r
355     {\r
356       // Old remote, decide what to do based on okTriggeredOSD\r
357       if (okTriggeredOSD) doUpDown(true);\r
358       else doChanUpDown(DOWN);\r
359       return 2;\r
360     }\r
361 \r
362     // END NEW/OLD REMOTE STUFF\r
363 \r
364     case Remote::PREVCHANNEL:\r
365     {\r
366       channelChange(PREVIOUS, 0);\r
367       osdChannelIndex = currentChannelIndex;\r
368       displayOSD(true);\r
369       return 2;\r
370     }\r
371     case Remote::OK:\r
372     {\r
373       doOK();\r
374       return 2;\r
375     }\r
376     case Remote::RED:\r
377     case Remote::MENU:\r
378     {\r
379       doSummary();\r
380       return 2;\r
381     }\r
382     case Remote::FULL:\r
383     case Remote::TV:\r
384     {\r
385       toggleChopSides();\r
386       return 2;\r
387     }\r
388 \r
389     case Remote::ZERO:\r
390     case Remote::ONE:\r
391     case Remote::TWO:\r
392     case Remote::THREE:\r
393     case Remote::FOUR:\r
394     case Remote::FIVE:\r
395     case Remote::SIX:\r
396     case Remote::SEVEN:\r
397     case Remote::EIGHT:\r
398     case Remote::NINE:\r
399     {\r
400       // key in channel number\r
401       doKey(command);\r
402       return 2;\r
403     }\r
404 \r
405     case Remote::GREEN:\r
406     {\r
407       if (streamType == VDR::VIDEO) doAudioSelector();\r
408       return 2;   \r
409     }\r
410     case Remote::YELLOW:\r
411     {\r
412       if (streamType ==VDR::VIDEO) doTeletext(); //TODO: Add a selector for subtitles or teletext\r
413       return 2;\r
414     }\r
415     case Remote::GUIDE:\r
416     case Remote::BLUE:\r
417     {\r
418       doEPG();\r
419       return 2;\r
420     }\r
421     case Remote::RECORD:\r
422       if (streamType == VDR::VIDEO)\r
423         (static_cast<PlayerLiveTV*>(player))->toggleSubtitles();\r
424       return 2;\r
425   }\r
426 \r
427   return 1;\r
428 }\r
429 \r
430 void VVideoLiveTV::go()\r
431 {\r
432   playing = true;\r
433   draw();\r
434   boxstack->update(this);\r
435 \r
436   setClock();\r
437   displayOSD(true);\r
438   \r
439   player->go(currentChannelIndex);\r
440 }\r
441 \r
442 void VVideoLiveTV::stop()\r
443 {\r
444   Timers::getInstance()->cancelTimer(this, 1);\r
445   Timers::getInstance()->cancelTimer(this, 2);\r
446   player->stop();\r
447   playing = false;\r
448 }\r
449 \r
450 void VVideoLiveTV::doLeftRight(bool right)\r
451 {\r
452   if (osd.getVisible())\r
453   {\r
454     if (right) osdChannelIndex = upChannel(osdChannelIndex);\r
455     else       osdChannelIndex = downChannel(osdChannelIndex);\r
456   }\r
457   else\r
458   {\r
459     osdChannelIndex = currentChannelIndex;\r
460   }\r
461   displayOSD(true);\r
462 }\r
463 \r
464 void VVideoLiveTV::doUpDown(bool down)\r
465 {\r
466   if (osd.getVisible())\r
467   {\r
468     if (down) sl.down();\r
469     else      sl.up();\r
470     sl.draw();\r
471     \r
472     displayOSD(false);\r
473   }\r
474   else\r
475   {\r
476     displayOSD(true);\r
477   }\r
478 }\r
479 \r
480 void VVideoLiveTV::doChanUpDown(int which)\r
481 {\r
482   channelChange(OFFSET, which);\r
483   osdChannelIndex = currentChannelIndex;\r
484   displayOSD(true);\r
485 }\r
486 \r
487 void VVideoLiveTV::doOK()\r
488 {\r
489   if (osd.getVisible())\r
490   {\r
491     if (keying)\r
492     {\r
493       UINT newChannel = 0;\r
494       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);\r
495       \r
496       channelChange(NUMBER, newChannel);\r
497       osdChannelIndex = currentChannelIndex;\r
498       displayOSD(true);\r
499     }\r
500     else if (osdChannelIndex == currentChannelIndex)\r
501     {\r
502       clearScreen();\r
503     }\r
504     else\r
505     {\r
506       channelChange(INDEX, osdChannelIndex);\r
507       displayOSD(true);\r
508     }\r
509   }\r
510   else\r
511   {\r
512     osdChannelIndex = currentChannelIndex;\r
513     displayOSD(true);\r
514     okTriggeredOSD = true;\r
515   }\r
516 }\r
517 \r
518 void VVideoLiveTV::doSummary()\r
519 {\r
520   if (summary.getVisible())\r
521   {\r
522     summary.setVisible(false);\r
523     draw();\r
524     boxstack->update(this, summary.getRegion());\r
525     Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd\r
526     return;\r
527   }\r
528 \r
529   summary.setVisible(true);\r
530 \r
531   if (osd.getVisible())\r
532   {\r
533     Timers::getInstance()->cancelTimer(this, 1);\r
534     displayOSD(false);\r
535   }\r
536   else\r
537   {\r
538     displayOSD(true);\r
539   }\r
540 }\r
541 \r
542 void VVideoLiveTV::doKey(int command)\r
543 {\r
544   if (!osd.getVisible()) // First key. prep the data\r
545   {\r
546     setNowNextData();\r
547     keying = 0;    \r
548   }\r
549 \r
550   int i;\r
551   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];\r
552   keyingInput[0] = command;\r
553   keying++;\r
554 \r
555   char* keyingString = new char[numberWidth + 1];\r
556   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';\r
557   keyingString[numberWidth] = '\0';\r
558 \r
559   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;\r
560   \r
561   if (keying == numberWidth)\r
562   {\r
563     UINT newChannel = 0;\r
564     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);\r
565     \r
566     channelChange(NUMBER, newChannel);\r
567     osdChannelIndex = currentChannelIndex;\r
568     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,\r
569     displayOSD(true); // this will put one back if required\r
570   }\r
571   else\r
572   {\r
573     osdChanNum.setText(keyingString);\r
574 \r
575     if (!osd.getVisible())\r
576     {\r
577       osd.setVisible(true);\r
578       draw();\r
579     }\r
580     else\r
581     {\r
582       osdChanNum.draw();\r
583     }\r
584     boxstack->update(this, osd.getRegion());\r
585     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input\r
586   }\r
587   delete[] keyingString;\r
588 }\r
589 \r
590 void VVideoLiveTV::doTeletext(bool subtitlemode)\r
591 {\r
592   if (streamType !=VDR::VIDEO) return;\r
593   bool exists=true;\r
594 \r
595   // Cancel keying\r
596   if (keying)\r
597   {\r
598     keying = 0;\r
599     // and reset the display - this is a copy from setNowNextData\r
600     char formatChanNum[20];\r
601     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);\r
602     osdChanNum.setText(formatChanNum);\r
603     osdChanName.setText((*chanList)[osdChannelIndex]->name);\r
604   }\r
605   if (osd.getVisible()) clearScreen();\r
606   // Draw the teletxt\r
607   VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();\r
608   if (vtxv==NULL) {\r
609        vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this);\r
610       ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv);\r
611       exists=false;\r
612   }\r
613   vtxv->setSubtitleMode(subtitlemode);\r
614   vtxv->draw();\r
615   draw();\r
616   \r
617   if (!exists) {\r
618       BoxStack::getInstance()->add(vtxv);\r
619   }\r
620   BoxStack::getInstance()->update(this);\r
621   BoxStack::getInstance()->update(vtxv); \r
622 }\r
623 \r
624 void VVideoLiveTV::doAudioSelector()\r
625 {\r
626   // If the osd is already visisble there might be a timer for it\r
627   Timers::getInstance()->cancelTimer(this, 1);\r
628   //This causes a deadlock with the timertrhread itself is locked\r
629 \r
630 \r
631   // Cancel keying\r
632   if (keying)\r
633   {\r
634     keying = 0;\r
635     // and reset the display - this is a copy from setNowNextData\r
636     char formatChanNum[20];\r
637     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);\r
638     osdChanNum.setText(formatChanNum);\r
639     osdChanName.setText((*chanList)[osdChannelIndex]->name);\r
640   }\r
641   int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel();\r
642   int subtitleType=0x10;\r
643   if (!(static_cast<PlayerLiveTV*>(player))->isSubtitlesOn()) {\r
644       if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() &&\r
645           ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() \r
646           ) {\r
647               subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage();\r
648               subtitleType=0x11;\r
649           \r
650       } else {\r
651           subtitleType=0xFF; //turnedOff\r
652           subtitleChannel=0;\r
653       }\r
654   }\r
655 \r
656   // Draw the selector\r
657   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(),\r
658       subtitleType,subtitleChannel,NULL);\r
659 \r
660   vas->setBackgroundColour(osdBack);\r
661   vas->setPosition(0, osd.getScreenY() - vas->getHeight());\r
662   vas->draw();\r
663 \r
664   // make vas != null and displayOSD will not set a timer or do any boxstack update\r
665   summary.setVisible(false);\r
666   if (osd.getVisible()) displayOSD(false);\r
667   else displayOSD(true);\r
668   draw();\r
669 \r
670   BoxStack::getInstance()->add(vas);\r
671   BoxStack::getInstance()->update(this);        \r
672   BoxStack::getInstance()->update(vas);     \r
673 }      \r
674       \r
675 void VVideoLiveTV::doEPG()\r
676 {\r
677   if (osd.getVisible()) clearScreen();\r
678 \r
679   video->setMode(Video::QUARTER);\r
680   video->setPosition(170, 5); //TODO need to deal with 4:3 switching\r
681 \r
682   VEpg* vepg = new VEpg(this, currentChannelIndex, streamType);\r
683   vepg->draw();\r
684   boxstack->add(vepg);\r
685   boxstack->update(vepg);\r
686 }\r
687 \r
688 void VVideoLiveTV::setNowNextData()\r
689 {\r
690   delData();\r
691   \r
692   Channel* currentChannel = (*chanList)[osdChannelIndex];\r
693 \r
694   char formatChanNum[20];\r
695   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);\r
696   osdChanNum.setText(formatChanNum);\r
697   osdChanName.setText(currentChannel->name);\r
698 \r
699   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);\r
700 \r
701   if (!eventList)\r
702   {\r
703     sl.addOption(tr("No channel data available"), 0, 1);\r
704   }\r
705   else\r
706   {\r
707     sort(eventList->begin(), eventList->end(), EventSorter());\r
708 \r
709     char tempString[300];\r
710     char tempString2[300];\r
711     struct tm* btime;\r
712     Event* event;\r
713     int eventListSize = eventList->size();\r
714     for(int i = 0; i < eventListSize; i++)\r
715     {\r
716       event = (*eventList)[i];\r
717 \r
718       //btime = localtime((time_t*)&event->time);\r
719       time_t etime = event->time;\r
720       btime = localtime(&etime);\r
721 #ifndef _MSC_VER\r
722       strftime(tempString2, 299, "%0H:%0M ", btime);\r
723 #else\r
724       strftime(tempString2, 299, "%H:%M ", btime);\r
725 #endif\r
726       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);\r
727       \r
728       sl.addOption(tempString, (ULONG)event, (i==0));\r
729     }\r
730   }\r
731 }\r
732 \r
733 void VVideoLiveTV::setSummaryData()\r
734 {\r
735   // If osd is not being displayed, sl will be filled with now, current channel\r
736   // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now\r
737   Event* selectedEvent = (Event*)sl.getCurrentOptionData();\r
738   \r
739   if (!selectedEvent)\r
740   {\r
741     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");  \r
742     textSummary.setText(tr("No summary available"));\r
743   }\r
744   else\r
745   {\r
746     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);  \r
747     textSummary.setText(selectedEvent->description);\r
748   }\r
749 }\r
750 \r
751 void VVideoLiveTV::displayOSD(bool newNowNextData)\r
752 {\r
753   osd.setVisible(true);\r
754   if (newNowNextData)\r
755   {\r
756     setNowNextData();\r
757     keying = 0;\r
758   }\r
759   osd.draw();\r
760   \r
761   if (summary.getVisible())\r
762   {\r
763     setSummaryData();\r
764     summary.draw();\r
765     boxstack->update(this, &osdSummaryRegion);\r
766   }\r
767   else\r
768   {\r
769     boxstack->update(this, osd.getRegion());\r
770   }\r
771   \r
772   bool setTimer = true;\r
773   if (vas) setTimer = false;\r
774   if (summary.getVisible()) setTimer = false;\r
775   if (textUnavailable.getVisible()) setTimer = false;\r
776 \r
777   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);\r
778 }\r
779 \r
780 void VVideoLiveTV::clearScreen()\r
781 {  \r
782   if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);\r
783 \r
784   textUnavailable.setVisible(false);\r
785   osd.setVisible(false);\r
786   summary.setVisible(false);\r
787 \r
788   okTriggeredOSD = false;\r
789 \r
790   draw();\r
791   boxstack->update(this);\r
792 }\r
793 \r
794 void VVideoLiveTV::showUnavailable()\r
795 {\r
796   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");  \r
797   textUnavailable.setVisible(true);\r
798   textUnavailable.draw();\r
799   \r
800   if (!osd.getVisible()) displayOSD(true);\r
801 \r
802   boxstack->update(this, textUnavailable.getRegion());  \r
803 }\r
804 \r
805 void VVideoLiveTV::setClock()\r
806 {\r
807   char timeString[20];\r
808   time_t t;\r
809   time(&t);\r
810   struct tm* tms = localtime(&t);\r
811   strftime(timeString, 19, "%H:%M", tms);\r
812   clock.setText(timeString);\r
813 \r
814   time_t dt = 60 - (t % 60);  // seconds to the next minute\r
815   if (dt == 0) dt = 60; // advance a whole minute if necessary\r
816   dt += t;  // get a time_t value for it rather than using duration\r
817   // (so it will occur at the actual second and not second and a half)\r
818 \r
819   Timers::getInstance()->setTimerT(this, 2, dt);\r
820 }\r
821 \r
822 void VVideoLiveTV::timercall(int ref)\r
823 {\r
824   if (ref == 1)\r
825   {\r
826     if (keying)\r
827     {\r
828          Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); \r
829       UINT newChannel = 0;\r
830       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);\r
831       \r
832       Message* m = new Message();\r
833       m->message = Message::CHANNEL_CHANGE;\r
834       m->to = this;\r
835       m->parameter = newChannel;\r
836       m->tag = 1; // signal to call displayOSD();\r
837       Command::getInstance()->postMessageFromOuterSpace(m);\r
838       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); \r
839     }\r
840     else\r
841     {\r
842         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); \r
843       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar\r
844       if (preBuffering < 100)\r
845       {\r
846         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");  \r
847         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s\r
848         return;\r
849       }\r
850       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); \r
851       osd.setVisible(false);\r
852       okTriggeredOSD = false;\r
853       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); \r
854       draw();\r
855       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); \r
856       boxstack->update(this, osd.getRegion());\r
857 \r
858       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); \r
859       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); \r
860     }\r
861   }\r
862   else if (ref == 2)\r
863   {\r
864       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2  start."); \r
865     setClock();\r
866     if (osd.getVisible())\r
867     {\r
868       clock.draw();\r
869       boxstack->update(this, osd.getRegion());\r
870     }\r
871     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); \r
872   }\r
873 }\r
874 \r
875 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)\r
876 {\r
877   UINT newChannel = 0;\r
878   if (streamType ==VDR::VIDEO) {\r
879   VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();\r
880     if (vtxt ) {\r
881          BoxStack::getInstance()->remove(vtxt);\r
882 \r
883     }\r
884   }\r
885   if (changeType == INDEX)\r
886   {\r
887     newChannel = newData;\r
888   }\r
889   else if (changeType == NUMBER)\r
890   {\r
891     UINT i;\r
892     for(i = 0; i < chanList->size(); i++)\r
893     {\r
894       if ((*chanList)[i]->number == (UINT)newData)\r
895       {\r
896         newChannel = i;\r
897         break;\r
898       }\r
899     }\r
900 \r
901     if (i == chanList->size())\r
902     {\r
903       // no such channel\r
904       return false;\r
905     }\r
906   }\r
907   else if (changeType == OFFSET)\r
908   {\r
909     if (newData == UP) newChannel = upChannel(currentChannelIndex);\r
910     else newChannel = downChannel(currentChannelIndex);\r
911   }\r
912   else if (changeType == PREVIOUS)\r
913   {\r
914     newChannel = previousChannelIndex;\r
915   }\r
916   else\r
917   {\r
918     return false; // bad input\r
919   }\r
920 \r
921   if (newChannel == currentChannelIndex) return true;\r
922 \r
923   previousChannelIndex = currentChannelIndex;\r
924   currentChannelIndex = newChannel;\r
925   \r
926   preBuffering = 0;\r
927   \r
928   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);\r
929   player->setChannel(currentChannelIndex);\r
930   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);\r
931 \r
932   // Blank out the symbols\r
933   sAspectRatio.setVisible(false);\r
934   bufferBar.setPercent(0);\r
935   sAudioChannels.setVisible(false);\r
936   \r
937   // Remove other stuff\r
938   if (textUnavailable.getVisible())\r
939   {\r
940     textUnavailable.setVisible(false);\r
941     \r
942   }\r
943 \r
944     draw();\r
945     BoxStack::getInstance()->update(this);\r
946   \r
947   return true;\r
948 }\r
949 \r
950 void VVideoLiveTV::processMessage(Message* m)\r
951 {\r
952   if (m->message == Message::MOUSE_LBDOWN)\r
953   {\r
954     //check if press is outside this view! then simulate cancel\r
955     int x=(m->parameter>>16)-osd.getScreenX();\r
956     int y=(m->parameter&0xFFFF)-osd.getScreenY();\r
957     if (osd.getVisible()) {\r
958     \r
959         if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&\r
960             (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {\r
961             BoxStack::getInstance()->handleCommand(Remote::RED);\r
962         } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&\r
963             (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){\r
964             BoxStack::getInstance()->handleCommand(Remote::GREEN);\r
965         } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&\r
966             (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){\r
967             BoxStack::getInstance()->handleCommand(Remote::YELLOW);\r
968         } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&\r
969             (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){\r
970             BoxStack::getInstance()->handleCommand(Remote::BLUE);\r
971         } else {\r
972             BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press\r
973         }\r
974 \r
975     } else {\r
976         BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press\r
977     }\r
978   }\r
979   else if (m->message == Message::CHANNEL_CHANGE)\r
980   {\r
981     channelChange(NUMBER, m->parameter);\r
982     osdChannelIndex = currentChannelIndex;\r
983     if (m->tag == 1) displayOSD(true);\r
984   }\r
985   else if (m->message == Message::EPG_CLOSE)\r
986   {\r
987     video->setMode(videoMode);\r
988   }\r
989   else if (m->message == Message::CHILD_CLOSE)\r
990   {\r
991     if (m->from == vas)\r
992     {\r
993       vas = NULL;\r
994       displayOSD(false);\r
995     }\r
996   }\r
997   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)\r
998   {\r
999     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);\r
1000     player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16,(m->parameter & 0xFF000000)>>24);\r
1001   } \r
1002   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)\r
1003   {\r
1004     if (streamType !=VDR::VIDEO) return;\r
1005       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %i", m->parameter);\r
1006       int type=((m->parameter & 0xFF0000)>>16);\r
1007       switch (type) {\r
1008       case 0x10: { //dvbsubtitle\r
1009           if (streamType == VDR::VIDEO){\r
1010               player->setSubtitleChannel((m->parameter & 0xFFFF));\r
1011               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(true);\r
1012               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();\r
1013               if (vtxt && vtxt->isInSubtitleMode()) {\r
1014                   BoxStack::getInstance()->remove(vtxt);\r
1015               }\r
1016           }\r
1017                  } break;\r
1018       case 0xFF: { //nosubtitles\r
1019           if (streamType == VDR::VIDEO){\r
1020               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);\r
1021               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();\r
1022               if (vtxt && vtxt->isInSubtitleMode()) {\r
1023                   BoxStack::getInstance()->remove(vtxt);\r
1024               }  \r
1025           }\r
1026                  } break;\r
1027       case 0x11: { //videotext\r
1028           (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);\r
1029           doTeletext(true);\r
1030           ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));\r
1031                  } break;\r
1032       };\r
1033       if (vas) {\r
1034         BoxStack::getInstance()->update(vas);\r
1035         //BoxStack::getInstance()->update(&osd); //eveil error\r
1036       }\r
1037       BoxStack::getInstance()->update(this, osd.getRegion());\r
1038 \r
1039       \r
1040   }\r
1041   else if (m->message == Message::PLAYER_EVENT)\r
1042   {\r
1043     switch(m->parameter)\r
1044     {\r
1045       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected\r
1046       {\r
1047         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");\r
1048         Command::getInstance()->connectionLost();\r
1049         break;\r
1050       }\r
1051       \r
1052       case PlayerLiveTV::STREAM_END:\r
1053       {\r
1054         // Message comes from playerlivetv through master mutex, so can do anything here\r
1055         showUnavailable();        \r
1056         break;\r
1057       }\r
1058       \r
1059       case PlayerLiveTV::ASPECT43:\r
1060       {\r
1061 #ifdef PAL_WSS\r
1062         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)\r
1063         {\r
1064           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");\r
1065           wss.setWide(false);\r
1066           wss.draw();\r
1067           BoxStack::getInstance()->update(this, &wssRegion);\r
1068         }\r
1069 #endif\r
1070         \r
1071         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;\r
1072         sAspectRatio.setVisible(true);\r
1073         \r
1074         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change\r
1075         {\r
1076           osd.draw();\r
1077           BoxStack::getInstance()->update(this, osd.getRegion());\r
1078         }\r
1079         \r
1080         break;\r
1081       }\r
1082       case PlayerLiveTV::ASPECT169:\r
1083       {\r
1084 #ifdef PAL_WSS\r
1085         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)\r
1086         {\r
1087           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");\r
1088           wss.setWide(true);\r
1089           wss.draw();\r
1090           BoxStack::getInstance()->update(this, &wssRegion);\r
1091         }\r
1092 #endif\r
1093         \r
1094         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;\r
1095         sAspectRatio.setVisible(true);\r
1096 \r
1097         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change\r
1098         {\r
1099           osd.draw();\r
1100           BoxStack::getInstance()->update(this, osd.getRegion());\r
1101         }\r
1102                 \r
1103         break;\r
1104       }\r
1105       case PlayerLiveTV::PREBUFFERING:\r
1106       {\r
1107         preBuffering = m->tag;\r
1108         Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);\r
1109         bufferBar.setPercent(preBuffering);\r
1110 \r
1111         if (osd.getVisible())\r
1112         {\r
1113           bufferBar.setVisible(true);\r
1114           bufferBar.draw();\r
1115           Region r;\r
1116           bufferBar.getRootBoxRegion(&r);                       ///////// FIXME !!!\r
1117           BoxStack::getInstance()->update(this, &r);\r
1118 \r
1119           if (preBuffering == 100)\r
1120           {\r
1121             doAudioChannelSymbol();\r
1122           }\r
1123         }\r
1124       }\r
1125     }\r
1126   }\r
1127 }\r
1128 \r
1129 void VVideoLiveTV::doAudioChannelSymbol()\r
1130 {\r
1131   // get the doobery\r
1132   Channel* currentChannel = (*chanList)[osdChannelIndex];\r
1133     \r
1134   bool multiAudio = false;\r
1135   if (Audio::getInstance()->supportsAc3()) {\r
1136           if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true;\r
1137   }\r
1138   if (currentChannel->numAPids > 1) multiAudio = true;\r
1139   \r
1140   // draw the doobery\r
1141   \r
1142   if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;\r
1143   else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;\r
1144   sAudioChannels.setVisible(true);\r
1145   \r
1146   if (osd.getVisible())\r
1147   {\r
1148     sAudioChannels.draw();\r
1149     Region r;\r
1150     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!\r
1151     // Fix this n'all.\r
1152     r.w = 32;\r
1153     r.h = 16;\r
1154     BoxStack::getInstance()->update(this, &r);\r
1155   }\r
1156 }\r
1157 \r
1158 UINT VVideoLiveTV::upChannel(UINT index)\r
1159 {\r
1160   if (index == (chanList->size() - 1)) // at the end\r
1161     return 0; // so go to start\r
1162   else\r
1163     return index + 1;\r
1164 }\r
1165 \r
1166 UINT VVideoLiveTV::downChannel(UINT index)\r
1167 {\r
1168   if (index == 0) // at the start\r
1169     return chanList->size() - 1; // so go to end\r
1170   else\r
1171     return index - 1;\r
1172 }\r
1173 \r
1174 void VVideoLiveTV::toggleChopSides()\r
1175 {\r
1176   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs\r
1177 \r
1178   if (videoMode == Video::NORMAL)\r
1179   {\r
1180     videoMode = Video::LETTERBOX;\r
1181     video->setMode(Video::LETTERBOX);\r
1182   }\r
1183   else\r
1184   {\r
1185     videoMode = Video::NORMAL;\r
1186     video->setMode(Video::NORMAL);\r
1187   }\r
1188 }\r
1189 \r
1190 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)\r
1191 {\r
1192   drawBitmap(posX, posY, bm,region);\r
1193   Region r;\r
1194   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();\r
1195   boxstack->update(this, &r);\r
1196 }\r
1197 \r
1198 void VVideoLiveTV::clearOSD()\r
1199 {\r
1200   rectangle(area, DrawStyle(0,0,0,0));\r
1201   boxstack->update(this, &area);\r
1202 }\r
1203 \r
1204 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)\r
1205 {\r
1206   Region r;\r
1207   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;\r
1208   //now convert to our display\r
1209   float scalex=720.f/((float) (region.framewidth+1));\r
1210   float scaley=576.f/((float) (region.frameheight+1));\r
1211   r.x=floor(scalex*((float)r.x));\r
1212   r.y=floor(scaley*((float)r.y));\r
1213   r.w=ceil(scalex*((float)r.w));\r
1214   r.h=ceil(scaley*((float)r.h));\r
1215   rectangle(r, DrawStyle(0,0,0,0));\r
1216   boxstack->update(this, &r);\r
1217 }\r