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