2 Copyright 2007 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 "playerlivetv.h"
26 #include "demuxerts.h"
28 #include "messagequeue.h"
32 #include "dvbsubtitles.h"
33 #include "osdreceiver.h"
35 // ----------------------------------- Called from outside, one offs or info funcs
37 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
38 : vfeed(this), afeed(this), tfeed(this)
40 messageQueue = tmessageQueue;
41 messageReceiver = tmessageReceiver;
42 osdReceiver = tosdReceiver;
45 audio = Audio::getInstance();
46 video = Video::getInstance();
47 logger = Log::getInstance();
48 vdr = VDR::getInstance();
51 subtitlesShowing = false;
60 PlayerLiveTV::~PlayerLiveTV()
62 if (initted) shutdown();
65 int PlayerLiveTV::init()
67 if (initted) return 0;
69 demuxer = new DemuxerTS();
70 if (!demuxer) return 0;
71 subtitles = new DVBSubtitles(osdReceiver);
72 if (!subtitles) return 0;
74 teletext = new TeletextDecoderVBIEBU();
76 if (!demuxer->init(this, audio, video, teletext, 2097152, 524288, 65536, subtitles))
78 logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
95 int PlayerLiveTV::shutdown()
97 if (!initted) return 0;
108 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
110 return demuxer->getmpAudioChannels();
113 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
115 return demuxer->getac3AudioChannels();
118 int PlayerLiveTV::getCurrentAudioChannel()
120 return demuxer->getAID();
123 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
125 return demuxer->setAID(newChannel,type);
128 void PlayerLiveTV::setSubtitleChannel(int newChannel)
130 return demuxer->setSubID(newChannel);
133 int *PlayerLiveTV::getTeletxtSubtitlePages()
135 return teletext->getSubtitlePages();
138 int PlayerLiveTV::getCurrentSubtitleChannel(){
139 return demuxer->getSubID();
142 bool PlayerLiveTV::toggleSubtitles()
144 if (!subtitlesShowing)
146 subtitlesShowing = true;
151 subtitlesShowing = false;
154 return subtitlesShowing;
158 void PlayerLiveTV::turnSubtitlesOn(bool ison) {
161 subtitlesShowing = true;
166 subtitlesShowing = false;
171 // ----------------------------------- Externally called events
173 void PlayerLiveTV::go(ULONG index)
175 struct PLInstruction i;
176 i.instruction = I_SETCHANNEL;
177 i.channelIndex = index;
178 instructions.push(i);
182 void PlayerLiveTV::setChannel(ULONG index)
184 logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
185 struct PLInstruction i;
186 i.instruction = I_SETCHANNEL;
187 i.channelIndex = index;
188 instructions.push(i);
189 threadSignalNoLock();
192 void PlayerLiveTV::stop()
194 logger->log("PlayerLiveTV", Log::DEBUG, "stop");
195 struct PLInstruction i;
196 i.instruction = I_STOP;
197 instructions.push(i);
202 // ----------------------------------- Callback
204 void PlayerLiveTV::call(void* caller)
206 if (caller == demuxer)
208 logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
210 int dxCurrentAspect = demuxer->getAspectRatio();
211 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
213 if (video->getTVsize() == Video::ASPECT16X9)
215 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
216 video->setAspectRatio(Video::ASPECT4X3);
220 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
223 Message* m = new Message();
225 m->to = messageReceiver;
226 m->message = Message::PLAYER_EVENT;
227 m->parameter = PlayerLiveTV::ASPECT43;
228 messageQueue->postMessageFromOuterSpace(m);
230 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
232 if (video->getTVsize() == Video::ASPECT16X9)
234 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
235 video->setAspectRatio(Video::ASPECT16X9);
239 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
242 Message* m = new Message();
244 m->to = messageReceiver;
245 m->message = Message::PLAYER_EVENT;
246 m->parameter = PlayerLiveTV::ASPECT169;
247 messageQueue->postMessageFromOuterSpace(m);
251 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
254 else if (caller == &afeed)
256 if (state == S_VIDEOSTARTUP)
258 logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
260 threadSignalNoLock();
265 // -----------------------------------
267 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
270 // 0 = normal stream packet
272 // 2 = connection lost
274 // logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
280 Message* m = new Message();
282 m->to = messageReceiver;
283 m->message = Message::PLAYER_EVENT;
284 m->parameter = PlayerLiveTV::STREAM_END;
285 messageQueue->postMessageFromOuterSpace(m);
288 if (streamChunks.size() < 11)
293 streamChunks.push(s);
294 threadSignalNoLock();
298 // Too many chunks in streamChunks, drop this chunk
300 logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
304 void PlayerLiveTV::clearStreamChunks()
306 while(streamChunks.size())
308 logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
309 struct StreamChunk s = streamChunks.front();
315 void PlayerLiveTV::chunkToDemuxer()
317 StreamChunk s = streamChunks.front();
319 //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
320 /*int a = */demuxer->put((UCHAR*)s.data, s.len);
321 //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
325 void PlayerLiveTV::switchState(UCHAR newState)
327 logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
331 case S_STOP: // FROM S_STOP
346 audio->setStreamType(Audio::MPEG2_PES);
364 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
371 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
384 vdr->stopStreaming();
400 audio->setStreamType(Audio::MPEG2_PES);
417 vdr->stopStreaming();
423 video->stop(); video->blank();
432 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
439 case S_PREBUFFERING: // FROM S_PREBUFFERING
452 vdr->stopStreaming();
469 audio->setStreamType(Audio::MPEG2_PES);
487 vdr->stopStreaming();
503 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
510 case S_PLAY: // FROM S_PLAY
516 vdr->stopStreaming();
532 vdr->stopStreaming();
549 audio->setStreamType(Audio::MPEG2_PES);
567 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
576 bool PlayerLiveTV::checkError()
578 if (!vdr->isConnected())
580 if (state != S_STOP) switchState(S_STOP);
582 Message* m = new Message();
584 m->to = messageReceiver;
585 m->message = Message::PLAYER_EVENT;
586 m->parameter = PlayerLiveTV::CONNECTION_LOST;
587 messageQueue->postMessageFromOuterSpace(m);
594 void PlayerLiveTV::optimizeInstructionQueue()
598 // Currently there are only 2 instruction types, so this is a bit overkill...
600 struct PLInstruction i;
601 while(instructions.size() > 1)
603 i = instructions.front();
604 if (i.instruction == I_SETCHANNEL)
606 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
608 else if (i.instruction == I_STOP)
610 return; // return here and ensure the next instruction will be stop
615 void PlayerLiveTV::threadMethod()
619 if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
621 switchState(S_PREBUFFERING);
622 videoStartup = false;
628 while(!instructions.empty())
630 if (instructions.size() > 1) optimizeInstructionQueue();
632 struct PLInstruction i = instructions.front();
635 if (i.instruction == I_SETCHANNEL)
637 logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
639 switchState(S_VIDEOSTARTUP);
643 Channel* chan = (*chanList)[i.channelIndex];
645 demuxer->setVID(chan->vpid);
646 if (chan->numAPids > 0)
648 demuxer->setAID(chan->apids[0].pid,0);
649 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
653 logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
655 if (chan->numSPids > 0)
656 demuxer->setSubID(chan->spids[0].pid);
657 demuxer->setTID(chan->tpid);
658 teletext->ResetDecoder();
659 int streamSuccess = vdr->streamChannel(chan->number, this);
660 if (!checkError() && !streamSuccess)
662 Message* m = new Message();
664 m->to = messageReceiver;
665 m->message = Message::PLAYER_EVENT;
666 m->parameter = PlayerLiveTV::STREAM_END;
667 messageQueue->postMessageFromOuterSpace(m);
671 else if (i.instruction == I_STOP)
673 logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
684 while(streamChunks.size())
688 if (state == S_PREBUFFERING)
691 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
692 logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
694 Message* m = new Message();
696 m->to = messageReceiver;
697 m->message = Message::PLAYER_EVENT;
698 m->parameter = PlayerLiveTV::PREBUFFERING;
699 m->tag = percentDone;
700 messageQueue->postMessageFromOuterSpace(m);
702 if (preBufferCount == preBufferAmount)
711 threadWaitForSignal(); // unlocks and waits for signal
715 logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");