]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Updates to new streaming protocol and live tv
[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(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   player->go(0);
315 }
316
317 void VVideoLiveTV::stop()
318 {
319   Timers::getInstance()->cancelTimer(this, 1);
320   Timers::getInstance()->cancelTimer(this, 2);
321   player->stop();
322   playing = false;
323 }
324
325 void VVideoLiveTV::doNowNext()
326 {
327   delData();
328   keying = 0;
329   
330   Channel* currentChannel = (*chanList)[osdChannelIndex];
331
332   char formatChanNum[20];
333   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
334   osdChanNum.setText(formatChanNum);
335   osdChanName.setText(currentChannel->name);
336
337   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
338
339   if (!eventList)
340   {
341     sl.addOption(tr("No channel data available"), 0, 1);
342   }
343   else
344   {
345     sort(eventList->begin(), eventList->end(), EventSorter());
346
347     char tempString[300];
348     char tempString2[300];
349     struct tm* btime;
350     Event* event;
351     int eventListSize = eventList->size();
352     for(int i = 0; i < eventListSize; i++)
353     {
354       event = (*eventList)[i];
355
356       //btime = localtime((time_t*)&event->time);
357       time_t etime = event->time;
358       btime = localtime(&etime);
359 #ifndef _MSC_VER
360       strftime(tempString2, 299, "%0H:%0M ", btime);
361 #else
362       strftime(tempString2, 299, "%H:%M ", btime);
363 #endif
364       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
365       sl.addOption(tempString, (ULONG)event, (i==0));
366     }
367   }
368 }
369
370 void VVideoLiveTV::delData()
371 {
372   if (eventList)
373   {
374     int eventListSize = eventList->size();
375     for(int i = 0; i < eventListSize; i++)
376     {
377       delete (*eventList)[i];
378     }
379     eventList->clear();
380     delete eventList;
381
382   }
383   sl.clear();
384 }
385
386 void VVideoLiveTV::doOK()
387 {
388   if (osd.getVisible())
389   {
390     if (keying)
391     {
392       UINT newChannel = 0;
393       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
394       
395       channelChange(NUMBER, newChannel);
396       osdChannelIndex = currentChannelIndex;
397       displayOSD();
398     }
399     else if (osdChannelIndex == currentChannelIndex)
400     {
401       removeOSD();
402     }
403     else
404     {
405       channelChange(INDEX, osdChannelIndex);
406       displayOSD();
407     }
408   }
409   else
410   {
411     osdChannelIndex = currentChannelIndex;
412     displayOSD();
413   }
414 }
415
416 void VVideoLiveTV::doLeft()
417 {
418   if (osd.getVisible())
419     osdChannelIndex = downChannel(osdChannelIndex);
420   else
421     osdChannelIndex = currentChannelIndex;
422
423   displayOSD();
424 }
425
426 void VVideoLiveTV::doRight()
427 {
428   if (osd.getVisible())
429     osdChannelIndex = upChannel(osdChannelIndex);
430   else
431     osdChannelIndex = currentChannelIndex;
432
433   displayOSD();
434 }
435
436 void VVideoLiveTV::doUp()
437 {
438   if (osd.getVisible())
439   {
440     sl.up();
441     sl.draw();
442     boxstack->update(this, osd.getRegion());
443     Timers::getInstance()->setTimerD(this, 1, 8);  // arrows pressed, go to 8s timer
444   }
445   else
446   {
447     displayOSD();
448   }
449 }
450
451 void VVideoLiveTV::doDown()
452 {
453   if (osd.getVisible())
454   {
455     sl.down();
456     sl.draw();
457     boxstack->update(this, osd.getRegion());
458     Timers::getInstance()->setTimerD(this, 1, 8);  // arrows pressed, go to 8s timer
459   }
460   else
461   {
462     displayOSD();
463   }
464 }
465
466 void VVideoLiveTV::doChanUp()
467 {
468   channelChange(OFFSET, UP);
469   osdChannelIndex = currentChannelIndex;
470
471   displayOSD();
472 }
473
474 void VVideoLiveTV::doChanDown()
475 {
476   channelChange(OFFSET, DOWN);
477   osdChannelIndex = currentChannelIndex;
478
479   displayOSD();
480 }
481
482 void VVideoLiveTV::doKey(int command)
483 {
484   if (!osd.getVisible()) // First key. prep the data
485   {
486     doNowNext();
487   }
488
489   int i;
490   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
491   keyingInput[0] = command;
492   keying++;
493
494   char keyingString[numberWidth+1];
495   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
496   keyingString[numberWidth] = '\0';
497
498   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
499   
500   if (keying == numberWidth)
501   {
502     UINT newChannel = 0;
503     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
504     
505     channelChange(NUMBER, newChannel);
506     osdChannelIndex = currentChannelIndex;
507     displayOSD();
508   }
509   else
510   {
511     // Special, modified displayOSD
512     if (!osd.getVisible())
513     {
514       osd.setVisible(true);
515       osdChanNum.setText(keyingString);
516       draw();
517     }
518     else
519     {
520       osdChanNum.setText(keyingString);
521       osdChanNum.draw();
522     }
523     boxstack->update(this, osd.getRegion());
524     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
525   }
526 }
527
528 void VVideoLiveTV::displayOSD()
529 {
530   osd.setVisible(true);
531   doNowNext();
532   draw();
533   boxstack->update(this, osd.getRegion());
534   Timers::getInstance()->setTimerD(this, 1, 4);
535 }
536
537 void VVideoLiveTV::removeOSD()
538 {  
539   Timers::getInstance()->cancelTimer(this, 1);
540   osd.setVisible(false);
541   draw();
542   boxstack->update(this, osd.getRegion());
543 }
544
545 void VVideoLiveTV::setClock()
546 {
547   char timeString[20];
548   time_t t;
549   time(&t);
550   struct tm* tms = localtime(&t);
551   strftime(timeString, 19, "%H:%M", tms);
552   clock.setText(timeString);
553
554   time_t dt = 60 - (t % 60);  // seconds to the next minute
555   if (dt == 0) dt = 60; // advance a whole minute if necessary
556   dt += t;  // get a time_t value for it rather than using duration
557   // (so it will occur at the actual second and not second and a half)
558
559   Timers::getInstance()->setTimerT(this, 2, dt);
560 }
561
562 void VVideoLiveTV::doEPG()
563 {
564   if (osd.getVisible()) removeOSD();
565
566   video->setMode(Video::QUARTER);
567   video->setPosition(170, 5); //TODO need to deal with 4:3 switching
568
569   VEpg* vepg = new VEpg(NULL, currentChannelIndex, VDR::VIDEO);
570   vepg->draw();
571   boxstack->add(vepg);
572   boxstack->update(vepg);
573 }
574
575 void VVideoLiveTV::timercall(int ref)
576 {
577   if (ref == 1)
578   {
579     if (keying)
580     {
581       // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
582       
583       UINT newChannel = 0;
584       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10, i);
585       
586       Message* m = new Message();
587       m->message = Message::CHANNEL_CHANGE;
588       m->to = this;
589       m->parameter = newChannel;
590       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
591     }
592     else
593     {
594       osd.setVisible(false);
595       draw();
596       Message* m = new Message();
597       m->message = Message::REDRAW;
598       m->to = BoxStack::getInstance();
599       m->from = this;
600       m->parameter = (ULONG)osd.getRegion();
601       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.
602     }
603   }
604   else if (ref == 2)
605   {
606     setClock();
607     if (osd.getVisible())
608     {
609       clock.draw();
610       Message* m = new Message();
611       m->message = Message::REDRAW;
612       m->to = BoxStack::getInstance();
613       m->from = this;
614       m->parameter = (ULONG)osd.getRegion();
615       Command::getInstance()->postMessageFromOuterSpace(m);  // FIXME cjt yeah you know what.    
616     }
617   }
618 }
619
620 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
621 {
622   UINT newChannel = 0;
623
624   if (changeType == INDEX)
625   {
626     newChannel = newData;
627   }
628   else if (changeType == NUMBER)
629   {
630     UINT i;
631     for(i = 0; i < chanList->size(); i++)
632     {
633       if ((*chanList)[i]->number == (UINT)newData)
634       {
635         newChannel = i;
636         break;
637       }
638     }
639
640     if (i == chanList->size())
641     {
642       // no such channel
643       return false;
644     }
645   }
646   else if (changeType == OFFSET)
647   {
648     if (newData == UP) newChannel = upChannel(currentChannelIndex);
649     else newChannel = downChannel(currentChannelIndex);
650   }
651   else if (changeType == PREVIOUS)
652   {
653     newChannel = previousChannelIndex;
654   }
655   else
656   {
657     return false; // bad input
658   }
659
660   if (newChannel == currentChannelIndex) return true;
661
662   previousChannelIndex = currentChannelIndex;
663   currentChannelIndex = newChannel;
664   
665   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
666
667   player->setChannel(currentChannelIndex);
668   return true;
669 }
670
671 void VVideoLiveTV::processMessage(Message* m)
672 {
673   if (m->message == Message::MOUSE_LBDOWN)
674   {
675     BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
676   }
677   else if (m->message == Message::CHANNEL_CHANGE)
678   {
679     channelChange(NUMBER, m->parameter);
680     osdChannelIndex = currentChannelIndex;
681     displayOSD();
682   }
683   else if (m->message == Message::EPG_CLOSE)
684   {
685     video->setMode(videoMode);
686   }
687   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
688   {
689     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
690     player->setAudioChannel(m->parameter);
691   }
692   else if (m->message == Message::PLAYER_EVENT)
693   {
694     switch(m->parameter)
695     {
696       case PlayerLiveTV::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       
707       /*
708       case PlayerLiveTV::STREAM_END:
709       {
710         // I can't handle this, send it to command - improve this
711         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
712         m2->to = Command::getInstance();
713         m2->message = Message::STREAM_END;
714         Command::getInstance()->postMessageNoLock(m2);
715         break;
716       }
717       */
718       
719       case PlayerLiveTV::ASPECT43:
720       {
721         if (dowss)
722         {
723           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
724           wss.setWide(false);
725           wss.draw();
726           BoxStack::getInstance()->update(this, &wssRegion);
727         }
728         break;
729       }
730       case PlayerLiveTV::ASPECT169:
731       {
732         if (dowss)
733         {
734           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
735           wss.setWide(true);
736           wss.draw();
737           BoxStack::getInstance()->update(this, &wssRegion);
738         }
739         break;
740       }
741     }
742   }
743 }
744
745 UINT VVideoLiveTV::upChannel(UINT index)
746 {
747   if (index == (chanList->size() - 1)) // at the end
748     return 0; // so go to start
749   else
750     return index + 1;
751 }
752
753 UINT VVideoLiveTV::downChannel(UINT index)
754 {
755   if (index == 0) // at the start
756     return chanList->size() - 1; // so go to end
757   else
758     return index - 1;
759 }
760
761 void VVideoLiveTV::toggleChopSides()
762 {
763   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
764
765   if (videoMode == Video::NORMAL)
766   {
767     videoMode = Video::LETTERBOX;
768     video->setMode(Video::LETTERBOX);
769   }
770   else
771   {
772     videoMode = Video::NORMAL;
773     video->setMode(Video::NORMAL);
774   }
775 }
776