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