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