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 Colour osdBack = Colour(0, 0, 0, 128);
118 osd.setBackgroundColour(osdBack);
119 osd.setPosition(0, video->getScreenHeight() - 150);
120 osd.setSize(video->getScreenWidth(), 150);
121 osd.setVisible(false);
124 clock.setBackgroundColour(osdBack);
125 clock.setPosition(osd.getWidth() - 100, 4);
126 clock.setSize(90, 30);
129 osdChanNum.setBackgroundColour(osdBack);
130 osdChanNum.setPosition(40, 4);
131 osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
132 osd.add(&osdChanNum);
134 osdChanName.setBackgroundColour(osdBack);
135 osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
136 osdChanName.setSize(300, 30);
137 osd.add(&osdChanName);
139 boxRed.setBackgroundColour(Colour::RED);
140 boxRed.setPosition(50, 104);
141 boxRed.setSize(18, 16);
144 boxGreen.setBackgroundColour(Colour::GREEN);
145 boxGreen.setPosition(220, 104);
146 boxGreen.setSize(18, 16);
149 boxYellow.setBackgroundColour(Colour::YELLOW);
150 boxYellow.setPosition(390, 104);
151 boxYellow.setSize(18, 16);
154 boxBlue.setBackgroundColour(Colour::BLUE);
155 boxBlue.setPosition(560, 104);
156 boxBlue.setSize(18, 16);
159 textRed.setBackgroundColour(osdBack);
160 textRed.setPosition(boxRed.getX()+18, 98);
161 textRed.setSize(120, 30);
162 textRed.setText(tr("Summary"));
165 if (streamType == VDR::VIDEO)
167 textGreen.setBackgroundColour(osdBack);
168 textGreen.setPosition(boxGreen.getX()+18, 98);
169 textGreen.setSize(120, 30);
170 textGreen.setText(tr("Audio"));
174 textYellow.setBackgroundColour(osdBack);
175 textYellow.setPosition(boxYellow.getX()+18, 98);
176 textYellow.setSize(120, 30);
177 textYellow.setText("");
178 osd.add(&textYellow);
180 textBlue.setBackgroundColour(osdBack);
181 textBlue.setPosition(boxBlue.getX()+18, 98);
182 textBlue.setSize(90, 30);
183 textBlue.setText(tr("EPG"));
186 sl.setBackgroundColour(osdBack);
187 sl.setPosition(70, 36);
193 summary.setBackgroundColour(osdBack);
194 summary.setPosition(0, video->getScreenHeight() - 300);
195 summary.setSize(video->getScreenWidth(), 150);
196 summary.setVisible(false);
199 textSummary.setBackgroundColour(osdBack);
200 textSummary.setPosition(40, 10);
201 textSummary.setSize(video->getScreenWidth() - 80, 130);
202 textSummary.setParaMode(true);
203 summary.add(&textSummary);
205 summaryBlackLine.setBackgroundColour(Colour::BLACK);
206 summaryBlackLine.setPosition(0, summary.getHeight() - 4);
207 summaryBlackLine.setSize(summary.getWidth(), 4);
208 summary.add(&summaryBlackLine);
210 sAspectRatio.setPosition(osd.getWidth() - 90, 40);
211 sAspectRatio.nextColour = Colour::SELECTHIGHLIGHT;
212 sAspectRatio.setVisible(false);
213 osd.add(&sAspectRatio);
215 bufferBar.setPosition(osd.getWidth() - 90, 70);
216 bufferBar.setSize(40, 20);
217 bufferBar.setVisible(true);
220 sAudioChannels.setPosition(osd.getWidth() - 130, 40);
221 sAudioChannels.nextColour = Colour::SELECTHIGHLIGHT;
222 sAudioChannels.setVisible(false);
223 osd.add(&sAudioChannels);
225 textUnavailable.setBackgroundColour(osdBack);
226 textUnavailable.setPosition(60, 30);
227 textUnavailable.setSize(osd.getWidth() - 120, 30);
228 textUnavailable.setText(tr("Channel Unavailable"));
229 textUnavailable.setVisible(false);
230 add(&textUnavailable);
233 Region r1 = summary.getRegionR();
234 Region r2 = osd.getRegionR();
235 osdSummaryRegion = r1 + r2;
238 VVideoLiveTV::~VVideoLiveTV()
243 video->setDefaultAspect();
247 void VVideoLiveTV::delData()
251 int eventListSize = eventList->size();
252 for(int i = 0; i < eventListSize; i++)
254 delete (*eventList)[i];
263 int VVideoLiveTV::handleCommand(int command)
269 if (osd.getVisible())
274 // else drop through to stop
279 vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
311 case Remote::CHANNELUP:
316 case Remote::DF_DOWN:
317 case Remote::CHANNELDOWN:
322 case Remote::PREVCHANNEL:
324 channelChange(PREVIOUS, 0);
325 osdChannelIndex = currentChannelIndex;
358 // key in channel number
365 if (streamType == VDR::VIDEO) doAudioSelector();
383 void VVideoLiveTV::go()
387 boxstack->update(this);
392 player->go(currentChannelIndex);
395 void VVideoLiveTV::stop()
397 Timers::getInstance()->cancelTimer(this, 1);
398 Timers::getInstance()->cancelTimer(this, 2);
403 void VVideoLiveTV::doLeftRight(bool right)
405 if (osd.getVisible())
407 if (right) osdChannelIndex = upChannel(osdChannelIndex);
408 else osdChannelIndex = downChannel(osdChannelIndex);
412 osdChannelIndex = currentChannelIndex;
417 void VVideoLiveTV::doUpDown(bool down)
419 if (osd.getVisible())
433 void VVideoLiveTV::doChanUpDown(int which)
435 channelChange(OFFSET, which);
436 osdChannelIndex = currentChannelIndex;
440 void VVideoLiveTV::doOK()
442 if (osd.getVisible())
447 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
449 channelChange(NUMBER, newChannel);
450 osdChannelIndex = currentChannelIndex;
453 else if (osdChannelIndex == currentChannelIndex)
459 channelChange(INDEX, osdChannelIndex);
465 osdChannelIndex = currentChannelIndex;
470 void VVideoLiveTV::doSummary()
472 if (summary.getVisible())
474 summary.setVisible(false);
476 boxstack->update(this, summary.getRegion());
477 Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
481 summary.setVisible(true);
483 if (osd.getVisible())
485 Timers::getInstance()->cancelTimer(this, 1);
494 void VVideoLiveTV::doKey(int command)
496 if (!osd.getVisible()) // First key. prep the data
503 for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
504 keyingInput[0] = command;
507 char* keyingString = new char[numberWidth + 1];
508 for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
509 keyingString[numberWidth] = '\0';
511 for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
513 if (keying == numberWidth)
516 for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
518 channelChange(NUMBER, newChannel);
519 osdChannelIndex = currentChannelIndex;
520 Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
521 displayOSD(true); // this will put one back if required
525 osdChanNum.setText(keyingString);
527 if (!osd.getVisible())
529 osd.setVisible(true);
536 boxstack->update(this, osd.getRegion());
537 Timers::getInstance()->setTimerD(this, 1, 3); // 3s for keying input
539 delete[] keyingString;
542 void VVideoLiveTV::doAudioSelector()
544 // If the osd is already visisble there might be a timer for it
545 Timers::getInstance()->cancelTimer(this, 1);
551 // and reset the display - this is a copy from setNowNextData
552 char formatChanNum[20];
553 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
554 osdChanNum.setText(formatChanNum);
555 osdChanName.setText((*chanList)[osdChannelIndex]->name);
559 vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel());
560 Colour osdBack = Colour(0, 0, 0, 128);
561 vas->setBackgroundColour(osdBack);
562 vas->setPosition(0, osd.getScreenY() - vas->getHeight());
565 // make vas != null and displayOSD will not set a timer or do any boxstack update
566 summary.setVisible(false);
567 if (osd.getVisible()) displayOSD(false);
568 else displayOSD(true);
571 BoxStack::getInstance()->add(vas);
572 BoxStack::getInstance()->update(this);
573 BoxStack::getInstance()->update(vas);
576 void VVideoLiveTV::doEPG()
578 if (osd.getVisible()) clearScreen();
580 video->setMode(Video::QUARTER);
581 video->setPosition(170, 5); //TODO need to deal with 4:3 switching
583 VEpg* vepg = new VEpg(this, currentChannelIndex, VDR::VIDEO);
586 boxstack->update(vepg);
589 void VVideoLiveTV::setNowNextData()
593 Channel* currentChannel = (*chanList)[osdChannelIndex];
595 char formatChanNum[20];
596 SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
597 osdChanNum.setText(formatChanNum);
598 osdChanName.setText(currentChannel->name);
600 eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
604 sl.addOption(tr("No channel data available"), 0, 1);
608 sort(eventList->begin(), eventList->end(), EventSorter());
610 char tempString[300];
611 char tempString2[300];
614 int eventListSize = eventList->size();
615 for(int i = 0; i < eventListSize; i++)
617 event = (*eventList)[i];
619 //btime = localtime((time_t*)&event->time);
620 time_t etime = event->time;
621 btime = localtime(&etime);
623 strftime(tempString2, 299, "%0H:%0M ", btime);
625 strftime(tempString2, 299, "%H:%M ", btime);
627 SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
629 sl.addOption(tempString, (ULONG)event, (i==0));
634 void VVideoLiveTV::setSummaryData()
636 // If osd is not being displayed, sl will be filled with now, current channel
637 // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
638 Event* selectedEvent = (Event*)sl.getCurrentOptionData();
642 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");
643 textSummary.setText(tr("No summary available"));
647 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);
648 textSummary.setText(selectedEvent->description);
652 void VVideoLiveTV::displayOSD(bool newNowNextData)
654 osd.setVisible(true);
662 if (summary.getVisible())
666 boxstack->update(this, &osdSummaryRegion);
670 boxstack->update(this, osd.getRegion());
673 bool setTimer = true;
674 if (vas) setTimer = false;
675 if (summary.getVisible()) setTimer = false;
676 if (textUnavailable.getVisible()) setTimer = false;
678 if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
681 void VVideoLiveTV::clearScreen()
683 if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
685 textUnavailable.setVisible(false);
686 osd.setVisible(false);
687 summary.setVisible(false);
690 boxstack->update(this);
693 void VVideoLiveTV::showUnavailable()
695 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");
696 textUnavailable.setVisible(true);
697 textUnavailable.draw();
699 if (!osd.getVisible()) displayOSD(true);
701 boxstack->update(this, textUnavailable.getRegion());
704 void VVideoLiveTV::setClock()
709 struct tm* tms = localtime(&t);
710 strftime(timeString, 19, "%H:%M", tms);
711 clock.setText(timeString);
713 time_t dt = 60 - (t % 60); // seconds to the next minute
714 if (dt == 0) dt = 60; // advance a whole minute if necessary
715 dt += t; // get a time_t value for it rather than using duration
716 // (so it will occur at the actual second and not second and a half)
718 Timers::getInstance()->setTimerT(this, 2, dt);
721 void VVideoLiveTV::timercall(int ref)
727 // Really, now that cancelTimer basically protects us from deletion, why can't we execute gui stuff here?
730 for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
732 Message* m = new Message();
733 m->message = Message::CHANNEL_CHANGE;
735 m->parameter = newChannel;
736 m->tag = 1; // signal to call displayOSD();
737 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
741 // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
742 if (preBuffering < 100)
744 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");
745 Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
749 osd.setVisible(false);
751 Message* m = new Message();
752 m->message = Message::REDRAW;
753 m->to = BoxStack::getInstance();
755 m->parameter = (ULONG)osd.getRegion();
756 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
762 if (osd.getVisible())
765 Message* m = new Message();
766 m->message = Message::REDRAW;
767 m->to = BoxStack::getInstance();
769 m->parameter = (ULONG)osd.getRegion();
770 Command::getInstance()->postMessageFromOuterSpace(m); // FIXME cjt yeah you know what.
775 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
779 if (changeType == INDEX)
781 newChannel = newData;
783 else if (changeType == NUMBER)
786 for(i = 0; i < chanList->size(); i++)
788 if ((*chanList)[i]->number == (UINT)newData)
795 if (i == chanList->size())
801 else if (changeType == OFFSET)
803 if (newData == UP) newChannel = upChannel(currentChannelIndex);
804 else newChannel = downChannel(currentChannelIndex);
806 else if (changeType == PREVIOUS)
808 newChannel = previousChannelIndex;
812 return false; // bad input
815 if (newChannel == currentChannelIndex) return true;
817 previousChannelIndex = currentChannelIndex;
818 currentChannelIndex = newChannel;
822 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
823 player->setChannel(currentChannelIndex);
824 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
826 // Blank out the symbols
827 sAspectRatio.setVisible(false);
828 bufferBar.setPercent(0);
829 sAudioChannels.setVisible(false);
831 // Remove other stuff
832 if (textUnavailable.getVisible())
834 textUnavailable.setVisible(false);
836 BoxStack::getInstance()->update(this);
842 void VVideoLiveTV::processMessage(Message* m)
844 if (m->message == Message::MOUSE_LBDOWN)
846 //check if press is outside this view! then simulate cancel
847 int x=(m->parameter>>16)-osd.getScreenX();
848 int y=(m->parameter&0xFFFF)-osd.getScreenY();
849 if (osd.getVisible()) {
851 if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
852 (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
853 BoxStack::getInstance()->handleCommand(Remote::RED);
854 } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
855 (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
856 BoxStack::getInstance()->handleCommand(Remote::GREEN);
857 } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
858 (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
859 BoxStack::getInstance()->handleCommand(Remote::YELLOW);
860 } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
861 (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
862 BoxStack::getInstance()->handleCommand(Remote::BLUE);
864 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
868 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
871 else if (m->message == Message::CHANNEL_CHANGE)
873 channelChange(NUMBER, m->parameter);
874 osdChannelIndex = currentChannelIndex;
875 if (m->tag == 1) displayOSD(true);
877 else if (m->message == Message::EPG_CLOSE)
879 video->setMode(videoMode);
881 else if (m->message == Message::CHILD_CLOSE)
889 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
891 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter);
892 player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16);
894 else if (m->message == Message::PLAYER_EVENT)
898 case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
900 Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
901 // I can't handle this, send it to command
902 Message* m2 = new Message();
903 m2->to = Command::getInstance();
904 m2->message = Message::CONNECTION_LOST;
905 Command::getInstance()->postMessageNoLock(m2);
909 case PlayerLiveTV::STREAM_END:
911 // Message comes from playerlivetv through master mutex, so can do anything here
916 case PlayerLiveTV::ASPECT43:
918 if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
920 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
923 BoxStack::getInstance()->update(this, &wssRegion);
926 sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
927 sAspectRatio.setVisible(true);
929 if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
932 BoxStack::getInstance()->update(this, osd.getRegion());
937 case PlayerLiveTV::ASPECT169:
939 if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
941 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
944 BoxStack::getInstance()->update(this, &wssRegion);
947 sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
948 sAspectRatio.setVisible(true);
950 if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
953 BoxStack::getInstance()->update(this, osd.getRegion());
958 case PlayerLiveTV::PREBUFFERING:
960 preBuffering = m->tag;
961 Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);
962 bufferBar.setPercent(preBuffering);
964 if (osd.getVisible())
966 bufferBar.setVisible(true);
969 bufferBar.getRootBoxRegion(&r); ///////// FIXME !!!
970 BoxStack::getInstance()->update(this, &r);
972 if (preBuffering == 100)
974 doAudioChannelSymbol();
982 void VVideoLiveTV::doAudioChannelSymbol()
985 Channel* currentChannel = (*chanList)[osdChannelIndex];
987 bool multiAudio = false;
989 if (currentChannel->numDPids > 1) multiAudio = true;
991 if (currentChannel->numAPids > 1) multiAudio = true;
995 if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
996 else sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
997 sAudioChannels.setVisible(true);
999 if (osd.getVisible())
1001 sAudioChannels.draw();
1003 sAudioChannels.getRootBoxRegion(&r); ///////// FIXME !!!
1007 BoxStack::getInstance()->update(this, &r);
1011 UINT VVideoLiveTV::upChannel(UINT index)
1013 if (index == (chanList->size() - 1)) // at the end
1014 return 0; // so go to start
1019 UINT VVideoLiveTV::downChannel(UINT index)
1021 if (index == 0) // at the start
1022 return chanList->size() - 1; // so go to end
1027 void VVideoLiveTV::toggleChopSides()
1029 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1031 if (videoMode == Video::NORMAL)
1033 videoMode = Video::LETTERBOX;
1034 video->setMode(Video::LETTERBOX);
1038 videoMode = Video::NORMAL;
1039 video->setMode(Video::NORMAL);