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)
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 if (!demuxer->init(this, audio, video, 2097152, 524288, subtitles))
76 logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
92 int PlayerLiveTV::shutdown()
94 if (!initted) return 0;
104 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
106 return demuxer->getmpAudioChannels();
109 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
111 return demuxer->getac3AudioChannels();
114 int PlayerLiveTV::getCurrentAudioChannel()
116 return demuxer->getAID();
119 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
121 return demuxer->setAID(newChannel,type);
124 bool PlayerLiveTV::toggleSubtitles()
126 if (!subtitlesShowing)
128 subtitlesShowing = true;
133 subtitlesShowing = false;
136 return subtitlesShowing;
139 // ----------------------------------- Externally called events
141 void PlayerLiveTV::go(ULONG index)
143 struct PLInstruction i;
144 i.instruction = I_SETCHANNEL;
145 i.channelIndex = index;
146 instructions.push(i);
150 void PlayerLiveTV::setChannel(ULONG index)
152 logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
153 struct PLInstruction i;
154 i.instruction = I_SETCHANNEL;
155 i.channelIndex = index;
156 instructions.push(i);
157 threadSignalNoLock();
160 void PlayerLiveTV::stop()
162 logger->log("PlayerLiveTV", Log::DEBUG, "stop");
163 struct PLInstruction i;
164 i.instruction = I_STOP;
165 instructions.push(i);
170 // ----------------------------------- Callback
172 void PlayerLiveTV::call(void* caller)
174 if (caller == demuxer)
176 logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
178 int dxCurrentAspect = demuxer->getAspectRatio();
179 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
181 if (video->getTVsize() == Video::ASPECT16X9)
183 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
184 video->setAspectRatio(Video::ASPECT4X3);
188 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
191 Message* m = new Message();
193 m->to = messageReceiver;
194 m->message = Message::PLAYER_EVENT;
195 m->parameter = PlayerLiveTV::ASPECT43;
196 messageQueue->postMessageFromOuterSpace(m);
198 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
200 if (video->getTVsize() == Video::ASPECT16X9)
202 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
203 video->setAspectRatio(Video::ASPECT16X9);
207 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
210 Message* m = new Message();
212 m->to = messageReceiver;
213 m->message = Message::PLAYER_EVENT;
214 m->parameter = PlayerLiveTV::ASPECT169;
215 messageQueue->postMessageFromOuterSpace(m);
219 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
222 else if (caller == &afeed)
224 if (state == S_VIDEOSTARTUP)
226 logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
228 threadSignalNoLock();
233 // -----------------------------------
235 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
238 // 0 = normal stream packet
240 // 2 = connection lost
242 // logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
248 Message* m = new Message();
250 m->to = messageReceiver;
251 m->message = Message::PLAYER_EVENT;
252 m->parameter = PlayerLiveTV::STREAM_END;
253 messageQueue->postMessageFromOuterSpace(m);
256 if (streamChunks.size() < 11)
261 streamChunks.push(s);
262 threadSignalNoLock();
266 // Too many chunks in streamChunks, drop this chunk
268 logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
272 void PlayerLiveTV::clearStreamChunks()
274 while(streamChunks.size())
276 logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
277 struct StreamChunk s = streamChunks.front();
283 void PlayerLiveTV::chunkToDemuxer()
285 StreamChunk s = streamChunks.front();
287 //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
288 /*int a = */demuxer->put((UCHAR*)s.data, s.len);
289 //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
293 void PlayerLiveTV::switchState(UCHAR newState)
295 logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
299 case S_STOP: // FROM S_STOP
314 audio->setStreamType(Audio::MPEG2_PES);
331 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
338 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
351 vdr->stopStreaming();
366 audio->setStreamType(Audio::MPEG2_PES);
383 vdr->stopStreaming();
398 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
405 case S_PREBUFFERING: // FROM S_PREBUFFERING
418 vdr->stopStreaming();
434 audio->setStreamType(Audio::MPEG2_PES);
451 vdr->stopStreaming();
466 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
473 case S_PLAY: // FROM S_PLAY
479 vdr->stopStreaming();
494 vdr->stopStreaming();
510 audio->setStreamType(Audio::MPEG2_PES);
527 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
536 bool PlayerLiveTV::checkError()
538 if (!vdr->isConnected())
540 if (state != S_STOP) switchState(S_STOP);
542 Message* m = new Message();
544 m->to = messageReceiver;
545 m->message = Message::PLAYER_EVENT;
546 m->parameter = PlayerLiveTV::CONNECTION_LOST;
547 messageQueue->postMessageFromOuterSpace(m);
554 void PlayerLiveTV::optimizeInstructionQueue()
558 // Currently there are only 2 instruction types, so this is a bit overkill...
560 struct PLInstruction i;
561 while(instructions.size() > 1)
563 i = instructions.front();
564 if (i.instruction == I_SETCHANNEL)
566 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
568 else if (i.instruction == I_STOP)
570 return; // return here and ensure the next instruction will be stop
575 void PlayerLiveTV::threadMethod()
579 if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
581 switchState(S_PREBUFFERING);
582 videoStartup = false;
588 while(!instructions.empty())
590 if (instructions.size() > 1) optimizeInstructionQueue();
592 struct PLInstruction i = instructions.front();
595 if (i.instruction == I_SETCHANNEL)
597 logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
599 switchState(S_VIDEOSTARTUP);
603 Channel* chan = (*chanList)[i.channelIndex];
605 demuxer->setVID(chan->vpid);
606 if (chan->numAPids > 0)
608 demuxer->setAID(chan->apids[0].pid,0);
609 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
613 logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
615 if (chan->numSPids > 0)
616 demuxer->setSubID(chan->spids[0].pid);
618 int streamSuccess = vdr->streamChannel(chan->number, this);
619 if (!checkError() && !streamSuccess)
621 Message* m = new Message();
623 m->to = messageReceiver;
624 m->message = Message::PLAYER_EVENT;
625 m->parameter = PlayerLiveTV::STREAM_END;
626 messageQueue->postMessageFromOuterSpace(m);
630 else if (i.instruction == I_STOP)
632 logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
643 while(streamChunks.size())
647 if (state == S_PREBUFFERING)
650 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
651 logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
653 Message* m = new Message();
655 m->to = messageReceiver;
656 m->message = Message::PLAYER_EVENT;
657 m->parameter = PlayerLiveTV::PREBUFFERING;
658 m->tag = percentDone;
659 messageQueue->postMessageFromOuterSpace(m);
661 if (preBufferCount == preBufferAmount)
670 threadWaitForSignal(); // unlocks and waits for signal
674 logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");