]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Live TV updates
[vompclient.git] / vvideolivetv.cc
1 /*
2     Copyright 2007 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "vvideolivetv.h"
22
23 #include "vchannellist.h"
24 #include "video.h"
25 #include "playerlivetv.h"
26 #include "channel.h"
27 #include "boxstack.h"
28 #include "colour.h"
29 #include "osd.h"
30 #include "command.h"
31 #include "i18n.h"
32 #include "wtextbox.h"
33 #include "remote.h"
34 #include "vaudioselector.h"
35 #include "colour.h"
36 #include "event.h"
37 #include "timers.h"
38 #include "vepg.h"
39
40 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
41 {
42   vdr = VDR::getInstance();
43   boxstack = BoxStack::getInstance();
44   video = Video::getInstance();
45
46   chanList = tchanList;
47   vchannelList = tvchannelList;
48   numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
49
50   currentChannelIndex = 0;
51   previousChannelIndex = 0;
52   osdChannelIndex = 0;
53   keying = 0;
54
55   playing = false;
56
57   // Convert channel number to index
58   UINT i;
59   for(i = 0; i < chanList->size(); i++)
60   {
61     if ((*chanList)[i]->number == (UINT)initialChannelNumber)
62     {
63       currentChannelIndex = i;
64       osdChannelIndex = i;
65       break;
66     }
67   }
68
69   eventList = NULL;
70
71   videoMode = video->getMode();
72   player = new PlayerLiveTV(Command::getInstance(), this, chanList);
73   player->init();
74
75   setSize(video->getScreenWidth(), video->getScreenHeight());
76   createBuffer();
77   Colour transparent(0, 0, 0, 0);
78   setBackgroundColour(transparent);
79
80   dowss = false;
81   char* optionWSS = vdr->configLoad("General", "WSS");
82   if (optionWSS)
83   {
84     if (strstr(optionWSS, "Yes")) dowss = true;
85     delete[] optionWSS;
86   }
87   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
88
89   if (dowss)
90   {
91     wss.setFormat(video->getFormat());
92     wss.setWide(true);
93     add(&wss);
94     
95     wssRegion.x = 0;
96     wssRegion.y = 6;
97     wssRegion.w = video->getScreenWidth();
98     wssRegion.h = 2;
99   }
100   
101   Colour osdBack = Colour(0, 0, 0, 128);
102   
103   osd.setBackgroundColour(osdBack);
104   osd.setPosition(0, video->getScreenHeight() - 150);
105   osd.setSize(video->getScreenWidth(), 150);
106   osd.setVisible(false);
107   add(&osd);
108   
109   clock.setBackgroundColour(osdBack);
110   clock.setPosition(osd.getWidth() - 100, 4);
111   clock.setSize(90, 30);
112   osd.add(&clock);
113
114   osdChanNum.setBackgroundColour(osdBack);
115   osdChanNum.setPosition(40, 4);
116   osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
117   osd.add(&osdChanNum);  
118
119   osdChanName.setBackgroundColour(osdBack);
120   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
121   osdChanName.setSize(300, 30);
122   osd.add(&osdChanName);
123   
124   boxRed.setBackgroundColour(Colour::RED);
125   boxRed.setPosition(50, 104);
126   boxRed.setSize(18, 16);
127   osd.add(&boxRed);
128
129   boxGreen.setBackgroundColour(Colour::GREEN);
130   boxGreen.setPosition(220, 104);
131   boxGreen.setSize(18, 16);
132   osd.add(&boxGreen);
133
134   boxYellow.setBackgroundColour(Colour::YELLOW);
135   boxYellow.setPosition(390, 104);
136   boxYellow.setSize(18, 16);
137   osd.add(&boxYellow);
138
139   boxBlue.setBackgroundColour(Colour::BLUE);
140   boxBlue.setPosition(560, 104);
141   boxBlue.setSize(18, 16);
142   osd.add(&boxBlue);  
143   
144   textRed.setBackgroundColour(osdBack);
145   textRed.setPosition(boxRed.getX()+18, 98);
146   textRed.setSize(120, 30);
147   textRed.setText("Summary");
148   osd.add(&textRed);  
149     
150   textGreen.setBackgroundColour(osdBack);
151   textGreen.setPosition(boxGreen.getX()+18, 98);
152   textGreen.setSize(120, 30);
153   textGreen.setText("Audio");
154   osd.add(&textGreen);  
155     
156   textYellow.setBackgroundColour(osdBack);
157   textYellow.setPosition(boxYellow.getX()+18, 98);
158   textYellow.setSize(120, 30);
159   textYellow.setText("");
160   osd.add(&textYellow);  
161     
162   textBlue.setBackgroundColour(osdBack);
163   textBlue.setPosition(boxBlue.getX()+18, 98);
164   textBlue.setSize(90, 30);
165   textBlue.setText("EPG");
166   osd.add(&textBlue);  
167     
168   sl.setBackgroundColour(osdBack);
169   sl.setPosition(70, 36);
170   sl.setSize(500, 58);
171   sl.setNoLoop();
172   osd.add(&sl);
173 }
174
175 VVideoLiveTV::~VVideoLiveTV()
176 {
177   if (playing) stop();
178
179   delete player;
180   video->setDefaultAspect();
181 }
182
183 int VVideoLiveTV::handleCommand(int command)
184 {
185   switch(command)
186   {
187     case Remote::BACK:
188     {
189       if (osd.getVisible())
190       {
191         removeOSD();
192         return 2;
193       }
194       // else drop through to stop
195     }
196     case Remote::STOP:
197     case Remote::MENU:
198     {
199       stop();
200       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
201       return 4;
202     }
203     case Remote::UP:
204     {
205       // New remote only
206       // epg data up
207       doUp();
208       return 2;
209     }
210     case Remote::DOWN:
211     {
212       // New remote only
213       // epg data down
214       doDown();
215       return 2;
216     }
217     case Remote::LEFT:
218     {
219       // New remote only
220       // epg data ch down
221       doLeft();
222       return 2;
223     }
224     case Remote::RIGHT:
225     {
226       // New remote only
227       // epg data ch up
228       doRight();
229       return 2;
230     }
231     case Remote::DF_UP:
232     case Remote::CHANNELUP:
233     {
234       doChanUp();
235       return 2;
236     }
237     case Remote::DF_DOWN:
238     case Remote::CHANNELDOWN:
239     {
240       doChanDown();
241       return 2;
242     }
243     case Remote::PREVCHANNEL:
244     {
245       channelChange(PREVIOUS, 0);
246       return 2;
247     }
248     case Remote::OK:
249     {
250       doOK();
251       return 2;
252     }
253     case Remote::RED:
254     {
255       return 2;
256     }
257     case Remote::FULL:
258     case Remote::TV:
259     {
260       toggleChopSides();
261       return 2;
262     }
263
264     case Remote::ZERO:
265     case Remote::ONE:
266     case Remote::TWO:
267     case Remote::THREE:
268     case Remote::FOUR:
269     case Remote::FIVE:
270     case Remote::SIX:
271     case Remote::SEVEN:
272     case Remote::EIGHT:
273     case Remote::NINE:
274     {
275       // key in channel number
276       doKey(command);
277       return 2;
278     }
279
280     case Remote::GREEN:
281     {
282       /*
283       VAudioSelector* vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((Player*)player)->getCurrentAudioChannel());
284       vas->setBackgroundColour(Colour::VIEWBACKGROUND);
285       vas->setPosition(0, getHeight()-200);
286       vas->draw();
287       BoxStack::getInstance()->add(vas);
288       BoxStack::getInstance()->update(vas);        
289       */
290     }
291     case Remote::YELLOW:
292     {
293       return 2;
294     }
295     case Remote::GUIDE:
296     case Remote::BLUE:
297     {
298       doEPG();
299       return 2;
300     }
301   }
302
303   return 1;
304 }
305
306 void VVideoLiveTV::go()
307 {
308   playing = true;
309   draw();
310   boxstack->update(this);
311
312   setClock();
313   displayOSD();
314   
315   player->go(currentChannelIndex);
316 }
317
318 void VVideoLiveTV::stop()
319 {
320   Timers::getInstance()->cancelTimer(this, 1);
321   Timers::getInstance()->cancelTimer(this, 2);
322   player->stop();
323   playing = false;
324 }
325
326 void VVideoLiveTV::doNowNext()
327 {
328   delData();
329   keying = 0;
330   
331   Channel* currentChannel = (*chanList)[osdChannelIndex];
332
333   char formatChanNum[20];
334   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
335   osdChanNum.setText(formatChanNum);
336   osdChanName.setText(currentChannel->name);
337
338   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
339
340   if (!eventList)
341   {
342     sl.addOption(tr("No channel data available"), 0, 1);
343   }
344   else
345   {
346     sort(eventList->begin(), eventList->end(), EventSorter());
347
348     char tempString[300];
349     char tempString2[300];
350     struct tm* btime;
351     Event* event;
352     int eventListSize = eventList->size();
353     for(int i = 0; i < eventListSize; i++)
354     {
355       event = (*eventList)[i];
356
357       //btime = localtime((time_t*)&event->time);
358       time_t etime = event->time;
359       btime = localtime(&etime);
360 #ifndef _MSC_VER
361       strftime(tempString2, 299, "%0H:%0M ", btime);
362 #else
363       strftime(tempString2, 299, "%H:%M ", btime);
364 #endif
365       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
366       sl.addOption(tempString, (ULONG)event, (i==0));
367     }
368   }
369 }
370
371 void VVideoLiveTV::delData()
372 {
373   if (eventList)
374   {
375     int eventListSize = eventList->size();
376     for(int i = 0; i < eventListSize; i++)
377     {
378       delete (*eventList)[i];
379     }
380     eventList->clear();
381     delete eventList;
382
383   }
384   sl.clear();
385 }
386
387 void VVideoLiveTV::doOK()
388 {
389   if (osd.getVisible())
390   {
391     if (keying)
392     {
393       UINT newChannel = 0;
394       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
395       
396       channelChange(NUMBER, newChannel);
397       osdChannelIndex = currentChannelIndex;
398       displayOSD();
399     }
400     else if (osdChannelIndex == currentChannelIndex)
401     {
402       removeOSD();
403     }
404     else
405     {
406       channelChange(INDEX, osdChannelIndex);
407       displayOSD();
408     }
409   }
410   else
411   {
412     osdChannelIndex = currentChannelIndex;
413     displayOSD();
414   }
415 }
416
417 void VVideoLiveTV::doLeft()
418 {
419   if (osd.getVisible())
420     osdChannelIndex = downChannel(osdChannelIndex);
421   else
422     osdChannelIndex = currentChannelIndex;
423
424   displayOSD();
425 }
426
427 void VVideoLiveTV::doRight()
428 {
429   if (osd.getVisible())
430     osdChannelIndex = upChannel(osdChannelIndex);
431   else
432     osdChannelIndex = currentChannelIndex;
433
434   displayOSD();
435 }
436
437 void VVideoLiveTV::doUp()
438 {
439   if (osd.getVisible())
440   {
441     sl.up();
442     sl.draw();
443     boxstack->update(this, osd.getRegion());
444     Timers::getInstance()->setTimerD(this, 1, 8);  // arrows pressed, go to 8s timer
445   }
446   else
447   {
448     displayOSD();
449   }
450 }
451
452 void VVideoLiveTV::doDown()
453 {
454   if (osd.getVisible())
455   {
456     sl.down();
457     sl.draw();
458     boxstack->update(this, osd.getRegion());
459     Timers::getInstance()->setTimerD(this, 1, 8);  // arrows pressed, go to 8s timer
460   }
461   else
462   {
463     displayOSD();
464   }
465 }
466
467 void VVideoLiveTV::doChanUp()
468 {
469   channelChange(OFFSET, UP);
470   osdChannelIndex = currentChannelIndex;
471
472   displayOSD();
473 }
474
475 void VVideoLiveTV::doChanDown()
476 {
477   channelChange(OFFSET, DOWN);
478   osdChannelIndex = currentChannelIndex;
479
480   displayOSD();
481 }
482
483 void VVideoLiveTV::doKey(int command)
484 {
485   if (!osd.getVisible()) // First key. prep the data
486   {
487     doNowNext();
488   }
489
490   int i;
491   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
492   keyingInput[0] = command;
493   keying++;
494
495   char keyingString[numberWidth+1];
496   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
497   keyingString[numberWidth] = '\0';
498
499   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
500   
501   if (keying == numberWidth)
502   {
503     UINT newChannel = 0;
504     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
505     
506     channelChange(NUMBER, newChannel);
507     osdChannelIndex = currentChannelIndex;
508     displayOSD();
509   }
510   else
511   {
512     // Special, modified displayOSD
513     if (!osd.getVisible())
514     {
515       osd.setVisible(true);
516       osdChanNum.setText(keyingString);
517       draw();
518     }
519     else
520     {
521       osdChanNum.setText(keyingString);
522       osdChanNum.draw();
523     }
524     boxstack->update(this, osd.getRegion());
525     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
526   }
527 }
528
529 void VVideoLiveTV::displayOSD()
530 {
531   osd.setVisible(true);
532   doNowNext();
533   draw();
534   boxstack->update(this, osd.getRegion());
535   Timers::getInstance()->setTimerD(this, 1, 4);
536 }
537
538 void VVideoLiveTV::removeOSD()
539 {  
540   Timers::getInstance()->cancelTimer(this, 1);
541   osd.setVisible(false);
542   draw();
543   boxstack->update(this, osd.getRegion());
544 }
545
546 void VVideoLiveTV::setClock()
547 {
548   char timeString[20];
549   time_t t;
550   time(&t);
551   struct tm* tms = localtime(&t);
552   strftime(timeString, 19, "%H:%M", tms);
553   clock.setText(timeString);
554
555   time_t dt = 60 - (t % 60);  // seconds to the next minute
556   if (dt == 0) dt = 60; // advance a whole minute if necessary
557   dt += t;  // get a time_t value for it rather than using duration
558   // (so it will occur at the actual second and not second and a half)
559
560   Timers::getInstance()->setTimerT(this, 2, dt);
561 }
562
563 void VVideoLiveTV::doEPG()
564 {
565   if (osd.getVisible()) removeOSD();
566
567   video->setMode(Video::QUARTER);
568   video->setPosition(170, 5); //TODO need to deal with 4:3 switching
569
570   VEpg* vepg = new VEpg(this, currentChannelIndex, VDR::VIDEO);
571   vepg->draw();
572   boxstack->add(vepg);
573   boxstack->update(vepg);
574 }
575
576 void VVideoLiveTV::timercall(int ref)
577 {
578   if (ref == 1)
579   {
580     if (keying)
581     {
582       // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
583       
584       UINT newChannel = 0;
585       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
586       
587       Message* m = new Message();
588       m->message = Message::CHANNEL_CHANGE;
589       m->to = this;
590       m->parameter = newChannel;
591       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
592     }
593     else
594     {
595       osd.setVisible(false);
596       draw();
597       Message* m = new Message();
598       m->message = Message::REDRAW;
599       m->to = BoxStack::getInstance();
600       m->from = this;
601       m->parameter = (ULONG)osd.getRegion();
602       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
603     }
604   }
605   else if (ref == 2)
606   {
607     setClock();
608     if (osd.getVisible())
609     {
610       clock.draw();
611       Message* m = new Message();
612       m->message = Message::REDRAW;
613       m->to = BoxStack::getInstance();
614       m->from = this;
615       m->parameter = (ULONG)osd.getRegion();
616       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.    
617     }
618   }
619 }
620
621 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
622 {
623   UINT newChannel = 0;
624
625   if (changeType == INDEX)
626   {
627     newChannel = newData;
628   }
629   else if (changeType == NUMBER)
630   {
631     UINT i;
632     for(i = 0; i < chanList->size(); i++)
633     {
634       if ((*chanList)[i]->number == (UINT)newData)
635       {
636         newChannel = i;
637         break;
638       }
639     }
640
641     if (i == chanList->size())
642     {
643       // no such channel
644       return false;
645     }
646   }
647   else if (changeType == OFFSET)
648   {
649     if (newData == UP) newChannel = upChannel(currentChannelIndex);
650     else newChannel = downChannel(currentChannelIndex);
651   }
652   else if (changeType == PREVIOUS)
653   {
654     newChannel = previousChannelIndex;
655   }
656   else
657   {
658     return false; // bad input
659   }
660
661   if (newChannel == currentChannelIndex) return true;
662
663   previousChannelIndex = currentChannelIndex;
664   currentChannelIndex = newChannel;
665   
666   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
667   player->setChannel(currentChannelIndex);
668   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
669
670   return true;
671 }
672
673 void VVideoLiveTV::processMessage(Message* m)
674 {
675   if (m->message == Message::MOUSE_LBDOWN)
676   {
677     BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
678   }
679   else if (m->message == Message::CHANNEL_CHANGE)
680   {
681     channelChange(NUMBER, m->parameter);
682     osdChannelIndex = currentChannelIndex;
683   }
684   else if (m->message == Message::EPG_CLOSE)
685   {
686     video->setMode(videoMode);
687   }
688   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
689   {
690     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
691     player->setAudioChannel(m->parameter);
692   }
693   else if (m->message == Message::PLAYER_EVENT)
694   {
695     switch(m->parameter)
696     {
697       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
698       {
699         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
700         // I can't handle this, send it to command
701         Message* m2 = new Message();
702         m2->to = Command::getInstance();
703         m2->message = Message::CONNECTION_LOST;
704         Command::getInstance()->postMessageNoLock(m2);
705         break;
706       }
707       
708       /*
709       case PlayerLiveTV::STREAM_END:
710       {
711         // I can't handle this, send it to command - improve this
712         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
713         m2->to = Command::getInstance();
714         m2->message = Message::STREAM_END;
715         Command::getInstance()->postMessageNoLock(m2);
716         break;
717       }
718       */
719       
720       case PlayerLiveTV::ASPECT43:
721       {
722         if (dowss)
723         {
724           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
725           wss.setWide(false);
726           wss.draw();
727           BoxStack::getInstance()->update(this, &wssRegion);
728         }
729         break;
730       }
731       case PlayerLiveTV::ASPECT169:
732       {
733         if (dowss)
734         {
735           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
736           wss.setWide(true);
737           wss.draw();
738           BoxStack::getInstance()->update(this, &wssRegion);
739         }
740         break;
741       }
742     }
743   }
744 }
745
746 UINT VVideoLiveTV::upChannel(UINT index)
747 {
748   if (index == (chanList->size() - 1)) // at the end
749     return 0; // so go to start
750   else
751     return index + 1;
752 }
753
754 UINT VVideoLiveTV::downChannel(UINT index)
755 {
756   if (index == 0) // at the start
757     return chanList->size() - 1; // so go to end
758   else
759     return index - 1;
760 }
761
762 void VVideoLiveTV::toggleChopSides()
763 {
764   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
765
766   if (videoMode == Video::NORMAL)
767   {
768     videoMode = Video::LETTERBOX;
769     video->setMode(Video::LETTERBOX);
770   }
771   else
772   {
773     videoMode = Video::NORMAL;
774     video->setMode(Video::NORMAL);
775   }
776 }
777