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;
62 // Convert channel number to index
64 for(i = 0; i < chanList->size(); i++)
66 if ((*chanList)[i]->number == (UINT)initialChannelNumber)
68 currentChannelIndex = i;
76 videoMode = video->getMode();
78 if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
80 streamType = VDR::VIDEO;
81 player = new PlayerLiveTV(Command::getInstance(), this, chanList);
85 streamType = VDR::RADIO;
86 player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
90 setSize(video->getScreenWidth(), video->getScreenHeight());
92 Colour transparent(0, 0, 0, 0);
93 setBackgroundColour(transparent);
96 char* optionWSS = vdr->configLoad("General", "WSS");
99 if (strstr(optionWSS, "Yes")) dowss = true;
102 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
106 wss.setFormat(video->getFormat());
112 wssRegion.w = video->getScreenWidth();
116 // This variable is set to true if the user pressed OK to bring the OSD on screen
117 // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling
118 okTriggeredOSD = false;
120 Colour osdBack = Colour(0, 0, 0, 128);
122 osd.setBackgroundColour(osdBack);
123 osd.setPosition(0, video->getScreenHeight() - 150);
124 osd.setSize(video->getScreenWidth(), 150);
125 osd.setVisible(false);
128 clock.setBackgroundColour(osdBack);
129 clock.setPosition(osd.getWidth() - 100, 4);
130 clock.setSize(90, 30);
133 osdChanNum.setBackgroundColour(osdBack);
134 osdChanNum.setPosition(50, 4);
135 osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
136 osd.add(&osdChanNum);
138 osdChanName.setBackgroundColour(osdBack);
139 osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
140 osdChanName.setSize(300, 30);
141 osd.add(&osdChanName);
143 boxRed.setBackgroundColour(Colour::RED);
144 boxRed.setPosition(54, 104);
145 boxRed.setSize(18, 16);
148 boxGreen.setBackgroundColour(Colour::GREEN);
149 boxGreen.setPosition(220, 104);
150 boxGreen.setSize(18, 16);
153 boxYellow.setBackgroundColour(Colour::YELLOW);
154 boxYellow.setPosition(390, 104);
155 boxYellow.setSize(18, 16);
158 boxBlue.setBackgroundColour(Colour::BLUE);
159 boxBlue.setPosition(560, 104);
160 boxBlue.setSize(18, 16);
163 textRed.setBackgroundColour(osdBack);
164 textRed.setPosition(boxRed.getX2(), 98);
165 textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30);
166 textRed.setText(tr("Summary"));
169 if (streamType == VDR::VIDEO)
171 textGreen.setBackgroundColour(osdBack);
172 textGreen.setPosition(boxGreen.getX2(), 98);
173 textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30);
174 textGreen.setText(tr("Audio"));
178 textYellow.setBackgroundColour(osdBack);
179 textYellow.setPosition(boxYellow.getX2(), 98);
180 textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30);
181 textYellow.setText("");
182 osd.add(&textYellow);
184 textBlue.setBackgroundColour(osdBack);
185 textBlue.setPosition(boxBlue.getX2(), 98);
186 textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30);
187 textBlue.setText(tr("EPG"));
190 sl.setBackgroundColour(osdBack);
191 sl.setPosition(70, 36);
197 summary.setBackgroundColour(osdBack);
198 summary.setPosition(0, video->getScreenHeight() - 300);
199 summary.setSize(video->getScreenWidth(), 150);
200 summary.setVisible(false);
203 textSummary.setBackgroundColour(osdBack);
204 textSummary.setPosition(40, 10);
205 textSummary.setSize(video->getScreenWidth() - 80, 130);
206 textSummary.setParaMode(true);
207 summary.add(&textSummary);
209 summaryBlackLine.setBackgroundColour(Colour::BLACK);
210 summaryBlackLine.setPosition(0, summary.getHeight() - 4);
211 summaryBlackLine.setSize(summary.getWidth(), 4);
212 summary.add(&summaryBlackLine);
214 sAspectRatio.setPosition(osd.getWidth() - 90, 40);
215 sAspectRatio.nextColour = Colour::SELECTHIGHLIGHT;
216 sAspectRatio.setVisible(false);
217 osd.add(&sAspectRatio);
219 bufferBar.setPosition(osd.getWidth() - 90, 70);
220 bufferBar.setSize(40, 20);
221 bufferBar.setVisible(true);
224 sAudioChannels.setPosition(osd.getWidth() - 130, 40);
225 sAudioChannels.nextColour = Colour::SELECTHIGHLIGHT;
226 sAudioChannels.setVisible(false);
227 osd.add(&sAudioChannels);
229 textUnavailable.setBackgroundColour(osdBack);
230 textUnavailable.setPosition(60, 30);
231 textUnavailable.setSize(osd.getWidth() - 120, 30);
232 textUnavailable.setText(tr("Channel Unavailable"));
233 textUnavailable.setVisible(false);
234 add(&textUnavailable);
237 Region r1 = summary.getRegionR();
238 Region r2 = osd.getRegionR();
239 osdSummaryRegion = r1 + r2;
242 void VVideoLiveTV::preDelete()
247 VVideoLiveTV::~VVideoLiveTV()
250 video->setDefaultAspect();
254 void VVideoLiveTV::delData()
258 int eventListSize = eventList->size();
259 for(int i = 0; i < eventListSize; i++)
261 delete (*eventList)[i];
270 int VVideoLiveTV::handleCommand(int command)
276 if (osd.getVisible() && !textUnavailable.getVisible())
281 // else drop through to stop
286 vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
290 // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there
311 // Continue new remote only...
312 case Remote::CHANNELUP:
317 case Remote::CHANNELDOWN:
323 // END NEW REMOTE ONLY, START OLD REMOTE ONLY
325 // DF_LEFT and DF_RIGHT never get here because they are stolen
326 // by command as vol- and vol+
328 // Old remote. Decide what to do based on whether
329 // OK was pressed - osd shown manually, use up/down for epg nav
330 // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch-
334 // Old remote, decide what to do based on okTriggeredOSD
335 if (okTriggeredOSD) doUpDown(false);
336 else doChanUpDown(UP);
339 case Remote::DF_DOWN:
341 // Old remote, decide what to do based on okTriggeredOSD
342 if (okTriggeredOSD) doUpDown(true);
343 else doChanUpDown(DOWN);
347 // END NEW/OLD REMOTE STUFF
349 case Remote::PREVCHANNEL:
351 channelChange(PREVIOUS, 0);
352 osdChannelIndex = currentChannelIndex;
385 // key in channel number
392 if (streamType == VDR::VIDEO) doAudioSelector();
410 void VVideoLiveTV::go()
414 boxstack->update(this);
419 player->go(currentChannelIndex);
422 void VVideoLiveTV::stop()
424 Timers::getInstance()->cancelTimer(this, 1);
425 Timers::getInstance()->cancelTimer(this, 2);
430 void VVideoLiveTV::doLeftRight(bool right)
432 if (osd.getVisible())
434 if (right) osdChannelIndex = upChannel(osdChannelIndex);
435 else osdChannelIndex = downChannel(osdChannelIndex);
439 osdChannelIndex = currentChannelIndex;
444 void VVideoLiveTV::doUpDown(bool down)
446 if (osd.getVisible())
460 void VVideoLiveTV::doChanUpDown(int which)
462 channelChange(OFFSET, which);
463 osdChannelIndex = currentChannelIndex;
467 void VVideoLiveTV::doOK()
469 if (osd.getVisible())
474 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
476 channelChange(NUMBER, newChannel);
477 osdChannelIndex = currentChannelIndex;
480 else if (osdChannelIndex == currentChannelIndex)
486 channelChange(INDEX, osdChannelIndex);
492 osdChannelIndex = currentChannelIndex;
494 okTriggeredOSD = true;
498 void VVideoLiveTV::doSummary()
500 if (summary.getVisible())
502 summary.setVisible(false);
504 boxstack->update(this, summary.getRegion());
505 Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
509 summary.setVisible(true);
511 if (osd.getVisible())
513 Timers::getInstance()->cancelTimer(this, 1);
522 void VVideoLiveTV::doKey(int command)
524 if (!osd.getVisible()) // First key. prep the data
531 for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
532 keyingInput[0] = command;
535 char* keyingString = new char[numberWidth + 1];
536 for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
537 keyingString[numberWidth] = '\0';
539 for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
541 if (keying == numberWidth)
544 for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
546 channelChange(NUMBER, newChannel);
547 osdChannelIndex = currentChannelIndex;
548 Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
549 displayOSD(true); // this will put one back if required
553 osdChanNum.setText(keyingString);
555 if (!osd.getVisible())
557 osd.setVisible(true);
564 boxstack->update(this, osd.getRegion());
565 Timers::getInstance()->setTimerD(this, 1, 3); // 3s for keying input
567 delete[] keyingString;
570 void VVideoLiveTV::doAudioSelector()
572 // If the osd is already visisble there might be a timer for it
573 Timers::getInstance()->cancelTimer(this, 1);
579 // and reset the display - this is a copy from setNowNextData
580 char formatChanNum[20];
581 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
582 osdChanNum.setText(formatChanNum);
583 osdChanName.setText((*chanList)[osdChannelIndex]->name);
587 vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel());
588 Colour osdBack = Colour(0, 0, 0, 128);
589 vas->setBackgroundColour(osdBack);
590 vas->setPosition(0, osd.getScreenY() - vas->getHeight());
593 // make vas != null and displayOSD will not set a timer or do any boxstack update
594 summary.setVisible(false);
595 if (osd.getVisible()) displayOSD(false);
596 else displayOSD(true);
599 BoxStack::getInstance()->add(vas);
600 BoxStack::getInstance()->update(this);
601 BoxStack::getInstance()->update(vas);
604 void VVideoLiveTV::doEPG()
606 if (osd.getVisible()) clearScreen();
608 video->setMode(Video::QUARTER);
609 video->setPosition(170, 5); //TODO need to deal with 4:3 switching
611 VEpg* vepg = new VEpg(this, currentChannelIndex, streamType);
614 boxstack->update(vepg);
617 void VVideoLiveTV::setNowNextData()
621 Channel* currentChannel = (*chanList)[osdChannelIndex];
623 char formatChanNum[20];
624 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
625 osdChanNum.setText(formatChanNum);
626 osdChanName.setText(currentChannel->name);
628 eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
632 sl.addOption(tr("No channel data available"), 0, 1);
636 sort(eventList->begin(), eventList->end(), EventSorter());
638 char tempString[300];
639 char tempString2[300];
642 int eventListSize = eventList->size();
643 for(int i = 0; i < eventListSize; i++)
645 event = (*eventList)[i];
647 //btime = localtime((time_t*)&event->time);
648 time_t etime = event->time;
649 btime = localtime(&etime);
651 strftime(tempString2, 299, "%0H:%0M ", btime);
653 strftime(tempString2, 299, "%H:%M ", btime);
655 SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
657 sl.addOption(tempString, (ULONG)event, (i==0));
662 void VVideoLiveTV::setSummaryData()
664 // If osd is not being displayed, sl will be filled with now, current channel
665 // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
666 Event* selectedEvent = (Event*)sl.getCurrentOptionData();
670 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");
671 textSummary.setText(tr("No summary available"));
675 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);
676 textSummary.setText(selectedEvent->description);
680 void VVideoLiveTV::displayOSD(bool newNowNextData)
682 osd.setVisible(true);
690 if (summary.getVisible())
694 boxstack->update(this, &osdSummaryRegion);
698 boxstack->update(this, osd.getRegion());
701 bool setTimer = true;
702 if (vas) setTimer = false;
703 if (summary.getVisible()) setTimer = false;
704 if (textUnavailable.getVisible()) setTimer = false;
706 if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
709 void VVideoLiveTV::clearScreen()
711 if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
713 textUnavailable.setVisible(false);
714 osd.setVisible(false);
715 summary.setVisible(false);
717 okTriggeredOSD = false;
720 boxstack->update(this);
723 void VVideoLiveTV::showUnavailable()
725 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");
726 textUnavailable.setVisible(true);
727 textUnavailable.draw();
729 if (!osd.getVisible()) displayOSD(true);
731 boxstack->update(this, textUnavailable.getRegion());
734 void VVideoLiveTV::setClock()
739 struct tm* tms = localtime(&t);
740 strftime(timeString, 19, "%H:%M", tms);
741 clock.setText(timeString);
743 time_t dt = 60 - (t % 60); // seconds to the next minute
744 if (dt == 0) dt = 60; // advance a whole minute if necessary
745 dt += t; // get a time_t value for it rather than using duration
746 // (so it will occur at the actual second and not second and a half)
748 Timers::getInstance()->setTimerT(this, 2, dt);
751 void VVideoLiveTV::timercall(int ref)
758 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
760 Message* m = new Message();
761 m->message = Message::CHANNEL_CHANGE;
763 m->parameter = newChannel;
764 m->tag = 1; // signal to call displayOSD();
765 Command::getInstance()->postMessageFromOuterSpace(m);
769 // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
770 if (preBuffering < 100)
772 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");
773 Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
777 osd.setVisible(false);
778 okTriggeredOSD = false;
780 boxstack->update(this, osd.getRegion());
786 if (osd.getVisible())
789 boxstack->update(this, osd.getRegion());
794 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
798 if (changeType == INDEX)
800 newChannel = newData;
802 else if (changeType == NUMBER)
805 for(i = 0; i < chanList->size(); i++)
807 if ((*chanList)[i]->number == (UINT)newData)
814 if (i == chanList->size())
820 else if (changeType == OFFSET)
822 if (newData == UP) newChannel = upChannel(currentChannelIndex);
823 else newChannel = downChannel(currentChannelIndex);
825 else if (changeType == PREVIOUS)
827 newChannel = previousChannelIndex;
831 return false; // bad input
834 if (newChannel == currentChannelIndex) return true;
836 previousChannelIndex = currentChannelIndex;
837 currentChannelIndex = newChannel;
841 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
842 player->setChannel(currentChannelIndex);
843 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
845 // Blank out the symbols
846 sAspectRatio.setVisible(false);
847 bufferBar.setPercent(0);
848 sAudioChannels.setVisible(false);
850 // Remove other stuff
851 if (textUnavailable.getVisible())
853 textUnavailable.setVisible(false);
855 BoxStack::getInstance()->update(this);
861 void VVideoLiveTV::processMessage(Message* m)
863 if (m->message == Message::MOUSE_LBDOWN)
865 //check if press is outside this view! then simulate cancel
866 int x=(m->parameter>>16)-osd.getScreenX();
867 int y=(m->parameter&0xFFFF)-osd.getScreenY();
868 if (osd.getVisible()) {
870 if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
871 (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
872 BoxStack::getInstance()->handleCommand(Remote::RED);
873 } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
874 (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
875 BoxStack::getInstance()->handleCommand(Remote::GREEN);
876 } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
877 (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
878 BoxStack::getInstance()->handleCommand(Remote::YELLOW);
879 } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
880 (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
881 BoxStack::getInstance()->handleCommand(Remote::BLUE);
883 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
887 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
890 else if (m->message == Message::CHANNEL_CHANGE)
892 channelChange(NUMBER, m->parameter);
893 osdChannelIndex = currentChannelIndex;
894 if (m->tag == 1) displayOSD(true);
896 else if (m->message == Message::EPG_CLOSE)
898 video->setMode(videoMode);
900 else if (m->message == Message::CHILD_CLOSE)
908 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
910 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
911 player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16);
913 else if (m->message == Message::PLAYER_EVENT)
917 case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
919 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
920 Command::getInstance()->connectionLost();
924 case PlayerLiveTV::STREAM_END:
926 // Message comes from playerlivetv through master mutex, so can do anything here
931 case PlayerLiveTV::ASPECT43:
933 if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
935 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
938 BoxStack::getInstance()->update(this, &wssRegion);
941 sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
942 sAspectRatio.setVisible(true);
944 if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
947 BoxStack::getInstance()->update(this, osd.getRegion());
952 case PlayerLiveTV::ASPECT169:
954 if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
956 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
959 BoxStack::getInstance()->update(this, &wssRegion);
962 sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
963 sAspectRatio.setVisible(true);
965 if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
968 BoxStack::getInstance()->update(this, osd.getRegion());
973 case PlayerLiveTV::PREBUFFERING:
975 preBuffering = m->tag;
976 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);
977 bufferBar.setPercent(preBuffering);
979 if (osd.getVisible())
981 bufferBar.setVisible(true);
984 bufferBar.getRootBoxRegion(&r); ///////// FIXME !!!
985 BoxStack::getInstance()->update(this, &r);
987 if (preBuffering == 100)
989 doAudioChannelSymbol();
997 void VVideoLiveTV::doAudioChannelSymbol()
1000 Channel* currentChannel = (*chanList)[osdChannelIndex];
1002 bool multiAudio = false;
1004 if (currentChannel->numDPids > 1) multiAudio = true;
1006 if (currentChannel->numAPids > 1) multiAudio = true;
1010 if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1011 else sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1012 sAudioChannels.setVisible(true);
1014 if (osd.getVisible())
1016 sAudioChannels.draw();
1018 sAudioChannels.getRootBoxRegion(&r); ///////// FIXME !!!
1022 BoxStack::getInstance()->update(this, &r);
1026 UINT VVideoLiveTV::upChannel(UINT index)
1028 if (index == (chanList->size() - 1)) // at the end
1029 return 0; // so go to start
1034 UINT VVideoLiveTV::downChannel(UINT index)
1036 if (index == 0) // at the start
1037 return chanList->size() - 1; // so go to end
1042 void VVideoLiveTV::toggleChopSides()
1044 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1046 if (videoMode == Video::NORMAL)
1048 videoMode = Video::LETTERBOX;
1049 video->setMode(Video::LETTERBOX);
1053 videoMode = Video::NORMAL;
1054 video->setMode(Video::NORMAL);