2 Copyright 2007-2008 Chris Tallon
4 This file is part of VOMP.
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.
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.
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.
21 #include "vvideolivetv.h"
23 #include "vchannellist.h"
25 #include "playerlive.h"
26 #include "playerlivetv.h"
27 #include "playerliveradio.h"
36 #include "vaudioselector.h"
42 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
44 vdr = VDR::getInstance();
45 boxstack = BoxStack::getInstance();
46 video = Video::getInstance();
51 vchannelList = tvchannelList;
52 numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
54 currentChannelIndex = 0;
55 previousChannelIndex = 0;
61 // Convert channel number to index
63 for(i = 0; i < chanList->size(); i++)
65 if ((*chanList)[i]->number == (UINT)initialChannelNumber)
67 currentChannelIndex = i;
75 videoMode = video->getMode();
77 if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
79 streamType = VDR::VIDEO;
80 player = new PlayerLiveTV(Command::getInstance(), this, chanList);
84 streamType = VDR::RADIO;
85 player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
89 setSize(video->getScreenWidth(), video->getScreenHeight());
91 Colour transparent(0, 0, 0, 0);
92 setBackgroundColour(transparent);
95 char* optionWSS = vdr->configLoad("General", "WSS");
98 if (strstr(optionWSS, "Yes")) dowss = true;
101 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
105 wss.setFormat(video->getFormat());
111 wssRegion.w = video->getScreenWidth();
115 Colour osdBack = Colour(0, 0, 0, 128);
117 osd.setBackgroundColour(osdBack);
118 osd.setPosition(0, video->getScreenHeight() - 150);
119 osd.setSize(video->getScreenWidth(), 150);
120 osd.setVisible(false);
123 clock.setBackgroundColour(osdBack);
124 clock.setPosition(osd.getWidth() - 100, 4);
125 clock.setSize(90, 30);
128 osdChanNum.setBackgroundColour(osdBack);
129 osdChanNum.setPosition(40, 4);
130 osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
131 osd.add(&osdChanNum);
133 osdChanName.setBackgroundColour(osdBack);
134 osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
135 osdChanName.setSize(300, 30);
136 osd.add(&osdChanName);
138 boxRed.setBackgroundColour(Colour::RED);
139 boxRed.setPosition(50, 104);
140 boxRed.setSize(18, 16);
143 boxGreen.setBackgroundColour(Colour::GREEN);
144 boxGreen.setPosition(220, 104);
145 boxGreen.setSize(18, 16);
148 boxYellow.setBackgroundColour(Colour::YELLOW);
149 boxYellow.setPosition(390, 104);
150 boxYellow.setSize(18, 16);
153 boxBlue.setBackgroundColour(Colour::BLUE);
154 boxBlue.setPosition(560, 104);
155 boxBlue.setSize(18, 16);
158 textRed.setBackgroundColour(osdBack);
159 textRed.setPosition(boxRed.getX()+18, 98);
160 textRed.setSize(120, 30);
161 textRed.setText("Summary");
164 if (streamType == VDR::VIDEO)
166 textGreen.setBackgroundColour(osdBack);
167 textGreen.setPosition(boxGreen.getX()+18, 98);
168 textGreen.setSize(120, 30);
169 textGreen.setText("Audio");
173 textYellow.setBackgroundColour(osdBack);
174 textYellow.setPosition(boxYellow.getX()+18, 98);
175 textYellow.setSize(120, 30);
176 textYellow.setText("");
177 osd.add(&textYellow);
179 textBlue.setBackgroundColour(osdBack);
180 textBlue.setPosition(boxBlue.getX()+18, 98);
181 textBlue.setSize(90, 30);
182 textBlue.setText("EPG");
185 sl.setBackgroundColour(osdBack);
186 sl.setPosition(70, 36);
192 summary.setBackgroundColour(osdBack);
193 summary.setPosition(0, video->getScreenHeight() - 300);
194 summary.setSize(video->getScreenWidth(), 150);
195 summary.setVisible(false);
198 textSummary.setBackgroundColour(osdBack);
199 textSummary.setPosition(40, 10);
200 textSummary.setSize(video->getScreenWidth() - 80, 130);
201 textSummary.setParaMode(true);
202 summary.add(&textSummary);
204 summaryBlackLine.setBackgroundColour(Colour::BLACK);
205 summaryBlackLine.setPosition(0, summary.getHeight() - 4);
206 summaryBlackLine.setSize(summary.getWidth(), 4);
207 summary.add(&summaryBlackLine);
210 Region r1 = summary.getRegionR();
211 Region r2 = osd.getRegionR();
212 osdSummaryRegion = r1 + r2;
215 VVideoLiveTV::~VVideoLiveTV()
220 video->setDefaultAspect();
224 void VVideoLiveTV::delData()
228 int eventListSize = eventList->size();
229 for(int i = 0; i < eventListSize; i++)
231 delete (*eventList)[i];
240 int VVideoLiveTV::handleCommand(int command)
246 if (osd.getVisible())
251 // else drop through to stop
256 vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
288 case Remote::CHANNELUP:
293 case Remote::DF_DOWN:
294 case Remote::CHANNELDOWN:
299 case Remote::PREVCHANNEL:
301 channelChange(PREVIOUS, 0);
333 // key in channel number
340 if (streamType == VDR::VIDEO) doAudioSelector();
358 void VVideoLiveTV::go()
362 boxstack->update(this);
367 player->go(currentChannelIndex);
370 void VVideoLiveTV::stop()
372 Timers::getInstance()->cancelTimer(this, 1);
373 Timers::getInstance()->cancelTimer(this, 2);
378 void VVideoLiveTV::doLeftRight(bool right)
380 if (osd.getVisible())
382 if (right) osdChannelIndex = upChannel(osdChannelIndex);
383 else osdChannelIndex = downChannel(osdChannelIndex);
387 osdChannelIndex = currentChannelIndex;
392 void VVideoLiveTV::doUpDown(bool down)
394 if (osd.getVisible())
408 void VVideoLiveTV::doChanUpDown(int which)
410 channelChange(OFFSET, which);
411 osdChannelIndex = currentChannelIndex;
415 void VVideoLiveTV::doOK()
417 if (osd.getVisible())
422 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
424 channelChange(NUMBER, newChannel);
425 osdChannelIndex = currentChannelIndex;
428 else if (osdChannelIndex == currentChannelIndex)
434 channelChange(INDEX, osdChannelIndex);
440 osdChannelIndex = currentChannelIndex;
445 void VVideoLiveTV::doSummary()
447 if (summary.getVisible())
449 summary.setVisible(false);
451 boxstack->update(this, summary.getRegion());
452 Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
456 summary.setVisible(true);
458 if (osd.getVisible())
460 Timers::getInstance()->cancelTimer(this, 1);
469 void VVideoLiveTV::doKey(int command)
471 if (!osd.getVisible()) // First key. prep the data
478 for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
479 keyingInput[0] = command;
482 char* keyingString = new char[numberWidth + 1];
483 for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
484 keyingString[numberWidth] = '\0';
486 for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
488 if (keying == numberWidth)
491 for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
493 channelChange(NUMBER, newChannel);
494 osdChannelIndex = currentChannelIndex;
495 Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
496 displayOSD(true); // this will put one back if required
500 osdChanNum.setText(keyingString);
502 if (!osd.getVisible())
504 osd.setVisible(true);
511 boxstack->update(this, osd.getRegion());
512 Timers::getInstance()->setTimerD(this, 1, 3); // 3s for keying input
514 delete[] keyingString;
517 void VVideoLiveTV::doAudioSelector()
519 // If the osd is already visisble there might be a timer for it
520 Timers::getInstance()->cancelTimer(this, 1);
526 // and reset the display - this is a copy from setNowNextData
527 char formatChanNum[20];
528 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
529 osdChanNum.setText(formatChanNum);
530 osdChanName.setText((*chanList)[osdChannelIndex]->name);
534 vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel());
535 Colour osdBack = Colour(0, 0, 0, 128);
536 vas->setBackgroundColour(osdBack);
537 vas->setPosition(0, osd.getScreenY() - vas->getHeight());
540 // make vas != null and displayOSD will not set a timer or do any boxstack update
541 summary.setVisible(false);
542 if (osd.getVisible()) displayOSD(false);
543 else displayOSD(true);
546 BoxStack::getInstance()->add(vas);
547 BoxStack::getInstance()->update(this);
548 BoxStack::getInstance()->update(vas);
551 void VVideoLiveTV::doEPG()
553 if (osd.getVisible()) clearScreen();
555 video->setMode(Video::QUARTER);
556 video->setPosition(170, 5); //TODO need to deal with 4:3 switching
558 VEpg* vepg = new VEpg(this, currentChannelIndex, VDR::VIDEO);
561 boxstack->update(vepg);
564 void VVideoLiveTV::setNowNextData()
568 Channel* currentChannel = (*chanList)[osdChannelIndex];
570 char formatChanNum[20];
571 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
572 osdChanNum.setText(formatChanNum);
573 osdChanName.setText(currentChannel->name);
575 eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
579 sl.addOption(tr("No channel data available"), 0, 1);
583 sort(eventList->begin(), eventList->end(), EventSorter());
585 char tempString[300];
586 char tempString2[300];
589 int eventListSize = eventList->size();
590 for(int i = 0; i < eventListSize; i++)
592 event = (*eventList)[i];
594 //btime = localtime((time_t*)&event->time);
595 time_t etime = event->time;
596 btime = localtime(&etime);
598 strftime(tempString2, 299, "%0H:%0M ", btime);
600 strftime(tempString2, 299, "%H:%M ", btime);
602 SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
604 sl.addOption(tempString, (ULONG)event, (i==0));
609 void VVideoLiveTV::setSummaryData()
611 // If osd is not being displayed, sl will be filled with now, current channel
612 // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
613 Event* selectedEvent = (Event*)sl.getCurrentOptionData();
617 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");
618 textSummary.setText(tr("No summary available"));
622 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);
623 textSummary.setText(selectedEvent->description);
627 void VVideoLiveTV::displayOSD(bool newNowNextData)
629 osd.setVisible(true);
637 if (summary.getVisible())
641 boxstack->update(this, &osdSummaryRegion);
645 boxstack->update(this, osd.getRegion());
646 Timers::getInstance()->setTimerD(this, 1, 4);
650 void VVideoLiveTV::clearScreen()
652 if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
654 osd.setVisible(false);
656 if (summary.getVisible())
658 summary.setVisible(false);
660 boxstack->update(this, &osdSummaryRegion);
665 boxstack->update(this, osd.getRegion());
669 void VVideoLiveTV::setClock()
674 struct tm* tms = localtime(&t);
675 strftime(timeString, 19, "%H:%M", tms);
676 clock.setText(timeString);
678 time_t dt = 60 - (t % 60); // seconds to the next minute
679 if (dt == 0) dt = 60; // advance a whole minute if necessary
680 dt += t; // get a time_t value for it rather than using duration
681 // (so it will occur at the actual second and not second and a half)
683 Timers::getInstance()->setTimerT(this, 2, dt);
686 void VVideoLiveTV::timercall(int ref)
692 // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
695 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
697 Message* m = new Message();
698 m->message = Message::CHANNEL_CHANGE;
700 m->parameter = newChannel;
701 m->tag = 1; // signal to call displayOSD();
702 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
706 osd.setVisible(false);
708 Message* m = new Message();
709 m->message = Message::REDRAW;
710 m->to = BoxStack::getInstance();
712 m->parameter = (ULONG)osd.getRegion();
713 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
719 if (osd.getVisible())
722 Message* m = new Message();
723 m->message = Message::REDRAW;
724 m->to = BoxStack::getInstance();
726 m->parameter = (ULONG)osd.getRegion();
727 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
732 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
736 if (changeType == INDEX)
738 newChannel = newData;
740 else if (changeType == NUMBER)
743 for(i = 0; i < chanList->size(); i++)
745 if ((*chanList)[i]->number == (UINT)newData)
752 if (i == chanList->size())
758 else if (changeType == OFFSET)
760 if (newData == UP) newChannel = upChannel(currentChannelIndex);
761 else newChannel = downChannel(currentChannelIndex);
763 else if (changeType == PREVIOUS)
765 newChannel = previousChannelIndex;
769 return false; // bad input
772 if (newChannel == currentChannelIndex) return true;
774 previousChannelIndex = currentChannelIndex;
775 currentChannelIndex = newChannel;
777 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
778 player->setChannel(currentChannelIndex);
779 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
784 void VVideoLiveTV::processMessage(Message* m)
786 if (m->message == Message::MOUSE_LBDOWN)
788 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
790 else if (m->message == Message::CHANNEL_CHANGE)
792 channelChange(NUMBER, m->parameter);
793 osdChannelIndex = currentChannelIndex;
794 if (m->tag == 1) displayOSD(true);
796 else if (m->message == Message::EPG_CLOSE)
798 video->setMode(videoMode);
800 else if (m->message == Message::CHILD_CLOSE)
808 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
810 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
811 player->setAudioChannel(m->parameter);
813 else if (m->message == Message::PLAYER_EVENT)
817 case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
819 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
820 // I can't handle this, send it to command
821 Message* m2 = new Message();
822 m2->to = Command::getInstance();
823 m2->message = Message::CONNECTION_LOST;
824 Command::getInstance()->postMessageNoLock(m2);
829 case PlayerLiveTV::STREAM_END:
831 // I can't handle this, send it to command - improve this
832 Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
833 m2->to = Command::getInstance();
834 m2->message = Message::STREAM_END;
835 Command::getInstance()->postMessageNoLock(m2);
840 case PlayerLiveTV::ASPECT43:
844 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
847 BoxStack::getInstance()->update(this, &wssRegion);
851 case PlayerLiveTV::ASPECT169:
855 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
858 BoxStack::getInstance()->update(this, &wssRegion);
866 UINT VVideoLiveTV::upChannel(UINT index)
868 if (index == (chanList->size() - 1)) // at the end
869 return 0; // so go to start
874 UINT VVideoLiveTV::downChannel(UINT index)
876 if (index == 0) // at the start
877 return chanList->size() - 1; // so go to end
882 void VVideoLiveTV::toggleChopSides()
884 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
886 if (videoMode == Video::NORMAL)
888 videoMode = Video::LETTERBOX;
889 video->setMode(Video::LETTERBOX);
893 videoMode = Video::NORMAL;
894 video->setMode(Video::NORMAL);