]> 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       m->tag = 1; // signal to call displayOSD();
592       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
593     }
594     else
595     {
596       osd.setVisible(false);
597       draw();
598       Message* m = new Message();
599       m->message = Message::REDRAW;
600       m->to = BoxStack::getInstance();
601       m->from = this;
602       m->parameter = (ULONG)osd.getRegion();
603       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
604     }
605   }
606   else if (ref == 2)
607   {
608     setClock();
609     if (osd.getVisible())
610     {
611       clock.draw();
612       Message* m = new Message();
613       m->message = Message::REDRAW;
614       m->to = BoxStack::getInstance();
615       m->from = this;
616       m->parameter = (ULONG)osd.getRegion();
617       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.    
618     }
619   }
620 }
621
622 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
623 {
624   UINT newChannel = 0;
625
626   if (changeType == INDEX)
627   {
628     newChannel = newData;
629   }
630   else if (changeType == NUMBER)
631   {
632     UINT i;
633     for(i = 0; i < chanList->size(); i++)
634     {
635       if ((*chanList)[i]->number == (UINT)newData)
636       {
637         newChannel = i;
638         break;
639       }
640     }
641
642     if (i == chanList->size())
643     {
644       // no such channel
645       return false;
646     }
647   }
648   else if (changeType == OFFSET)
649   {
650     if (newData == UP) newChannel = upChannel(currentChannelIndex);
651     else newChannel = downChannel(currentChannelIndex);
652   }
653   else if (changeType == PREVIOUS)
654   {
655     newChannel = previousChannelIndex;
656   }
657   else
658   {
659     return false; // bad input
660   }
661
662   if (newChannel == currentChannelIndex) return true;
663
664   previousChannelIndex = currentChannelIndex;
665   currentChannelIndex = newChannel;
666   
667   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
668   player->setChannel(currentChannelIndex);
669   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
670
671   return true;
672 }
673
674 void VVideoLiveTV::processMessage(Message* m)
675 {
676   if (m->message == Message::MOUSE_LBDOWN)
677   {
678     BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
679   }
680   else if (m->message == Message::CHANNEL_CHANGE)
681   {
682     channelChange(NUMBER, m->parameter);
683     osdChannelIndex = currentChannelIndex;
684     if (m->tag == 1) displayOSD();
685   }
686   else if (m->message == Message::EPG_CLOSE)
687   {
688     video->setMode(videoMode);
689   }
690   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
691   {
692     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
693     player->setAudioChannel(m->parameter);
694   }
695   else if (m->message == Message::PLAYER_EVENT)
696   {
697     switch(m->parameter)
698     {
699       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
700       {
701         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
702         // I can't handle this, send it to command
703         Message* m2 = new Message();
704         m2->to = Command::getInstance();
705         m2->message = Message::CONNECTION_LOST;
706         Command::getInstance()->postMessageNoLock(m2);
707         break;
708       }
709       
710       /*
711       case PlayerLiveTV::STREAM_END:
712       {
713         // I can't handle this, send it to command - improve this
714         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
715         m2->to = Command::getInstance();
716         m2->message = Message::STREAM_END;
717         Command::getInstance()->postMessageNoLock(m2);
718         break;
719       }
720       */
721       
722       case PlayerLiveTV::ASPECT43:
723       {
724         if (dowss)
725         {
726           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
727           wss.setWide(false);
728           wss.draw();
729           BoxStack::getInstance()->update(this, &wssRegion);
730         }
731         break;
732       }
733       case PlayerLiveTV::ASPECT169:
734       {
735         if (dowss)
736         {
737           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
738           wss.setWide(true);
739           wss.draw();
740           BoxStack::getInstance()->update(this, &wssRegion);
741         }
742         break;
743       }
744     }
745   }
746 }
747
748 UINT VVideoLiveTV::upChannel(UINT index)
749 {
750   if (index == (chanList->size() - 1)) // at the end
751     return 0; // so go to start
752   else
753     return index + 1;
754 }
755
756 UINT VVideoLiveTV::downChannel(UINT index)
757 {
758   if (index == 0) // at the start
759     return chanList->size() - 1; // so go to end
760   else
761     return index - 1;
762 }
763
764 void VVideoLiveTV::toggleChopSides()
765 {
766   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
767
768   if (videoMode == Video::NORMAL)
769   {
770     videoMode = Video::LETTERBOX;
771     video->setMode(Video::LETTERBOX);
772   }
773   else
774   {
775     videoMode = Video::NORMAL;
776     video->setMode(Video::NORMAL);
777   }
778 }
779