]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Updates for windows
[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 = new char[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   delete[] keyingString;
538 }
539
540 void VVideoLiveTV::displayOSD()
541 {
542   osd.setVisible(true);
543   doNowNext();
544   draw();
545   boxstack->update(this, osd.getRegion());
546   Timers::getInstance()->setTimerD(this, 1, 4);
547 }
548
549 void VVideoLiveTV::removeOSD()
550 {  
551   Timers::getInstance()->cancelTimer(this, 1);
552   osd.setVisible(false);
553   draw();
554   boxstack->update(this, osd.getRegion());
555 }
556
557 void VVideoLiveTV::setClock()
558 {
559   char timeString[20];
560   time_t t;
561   time(&t);
562   struct tm* tms = localtime(&t);
563   strftime(timeString, 19, "%H:%M", tms);
564   clock.setText(timeString);
565
566   time_t dt = 60 - (t % 60);  // seconds to the next minute
567   if (dt == 0) dt = 60; // advance a whole minute if necessary
568   dt += t;  // get a time_t value for it rather than using duration
569   // (so it will occur at the actual second and not second and a half)
570
571   Timers::getInstance()->setTimerT(this, 2, dt);
572 }
573
574 void VVideoLiveTV::doEPG()
575 {
576   if (osd.getVisible()) removeOSD();
577
578   video->setMode(Video::QUARTER);
579   video->setPosition(170, 5); //TODO need to deal with 4:3 switching
580
581   VEpg* vepg = new VEpg(this, currentChannelIndex, VDR::VIDEO);
582   vepg->draw();
583   boxstack->add(vepg);
584   boxstack->update(vepg);
585 }
586
587 void VVideoLiveTV::timercall(int ref)
588 {
589   if (ref == 1)
590   {
591     if (keying)
592     {
593       // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
594       
595       UINT newChannel = 0;
596       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
597       
598       Message* m = new Message();
599       m->message = Message::CHANNEL_CHANGE;
600       m->to = this;
601       m->parameter = newChannel;
602       m->tag = 1; // signal to call displayOSD();
603       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
604     }
605     else
606     {
607       osd.setVisible(false);
608       draw();
609       Message* m = new Message();
610       m->message = Message::REDRAW;
611       m->to = BoxStack::getInstance();
612       m->from = this;
613       m->parameter = (ULONG)osd.getRegion();
614       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
615     }
616   }
617   else if (ref == 2)
618   {
619     setClock();
620     if (osd.getVisible())
621     {
622       clock.draw();
623       Message* m = new Message();
624       m->message = Message::REDRAW;
625       m->to = BoxStack::getInstance();
626       m->from = this;
627       m->parameter = (ULONG)osd.getRegion();
628       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.    
629     }
630   }
631 }
632
633 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
634 {
635   UINT newChannel = 0;
636
637   if (changeType == INDEX)
638   {
639     newChannel = newData;
640   }
641   else if (changeType == NUMBER)
642   {
643     UINT i;
644     for(i = 0; i < chanList->size(); i++)
645     {
646       if ((*chanList)[i]->number == (UINT)newData)
647       {
648         newChannel = i;
649         break;
650       }
651     }
652
653     if (i == chanList->size())
654     {
655       // no such channel
656       return false;
657     }
658   }
659   else if (changeType == OFFSET)
660   {
661     if (newData == UP) newChannel = upChannel(currentChannelIndex);
662     else newChannel = downChannel(currentChannelIndex);
663   }
664   else if (changeType == PREVIOUS)
665   {
666     newChannel = previousChannelIndex;
667   }
668   else
669   {
670     return false; // bad input
671   }
672
673   if (newChannel == currentChannelIndex) return true;
674
675   previousChannelIndex = currentChannelIndex;
676   currentChannelIndex = newChannel;
677   
678   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
679   player->setChannel(currentChannelIndex);
680   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
681
682   return true;
683 }
684
685 void VVideoLiveTV::processMessage(Message* m)
686 {
687   if (m->message == Message::MOUSE_LBDOWN)
688   {
689     BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
690   }
691   else if (m->message == Message::CHANNEL_CHANGE)
692   {
693     channelChange(NUMBER, m->parameter);
694     osdChannelIndex = currentChannelIndex;
695     if (m->tag == 1) displayOSD();
696   }
697   else if (m->message == Message::EPG_CLOSE)
698   {
699     video->setMode(videoMode);
700   }
701   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
702   {
703     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
704     player->setAudioChannel(m->parameter);
705   }
706   else if (m->message == Message::PLAYER_EVENT)
707   {
708     switch(m->parameter)
709     {
710       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
711       {
712         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
713         // I can't handle this, send it to command
714         Message* m2 = new Message();
715         m2->to = Command::getInstance();
716         m2->message = Message::CONNECTION_LOST;
717         Command::getInstance()->postMessageNoLock(m2);
718         break;
719       }
720       
721       /*
722       case PlayerLiveTV::STREAM_END:
723       {
724         // I can't handle this, send it to command - improve this
725         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
726         m2->to = Command::getInstance();
727         m2->message = Message::STREAM_END;
728         Command::getInstance()->postMessageNoLock(m2);
729         break;
730       }
731       */
732       
733       case PlayerLiveTV::ASPECT43:
734       {
735         if (dowss)
736         {
737           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
738           wss.setWide(false);
739           wss.draw();
740           BoxStack::getInstance()->update(this, &wssRegion);
741         }
742         break;
743       }
744       case PlayerLiveTV::ASPECT169:
745       {
746         if (dowss)
747         {
748           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
749           wss.setWide(true);
750           wss.draw();
751           BoxStack::getInstance()->update(this, &wssRegion);
752         }
753         break;
754       }
755     }
756   }
757 }
758
759 UINT VVideoLiveTV::upChannel(UINT index)
760 {
761   if (index == (chanList->size() - 1)) // at the end
762     return 0; // so go to start
763   else
764     return index + 1;
765 }
766
767 UINT VVideoLiveTV::downChannel(UINT index)
768 {
769   if (index == 0) // at the start
770     return chanList->size() - 1; // so go to end
771   else
772     return index - 1;
773 }
774
775 void VVideoLiveTV::toggleChopSides()
776 {
777   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
778
779   if (videoMode == Video::NORMAL)
780   {
781     videoMode = Video::LETTERBOX;
782     video->setMode(Video::LETTERBOX);
783   }
784   else
785   {
786     videoMode = Video::NORMAL;
787     video->setMode(Video::NORMAL);
788   }
789 }
790