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"
33 // ----------------------------------- Called from outside, one offs or info funcs
35 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
36 : vfeed(this), afeed(this)
38 messageQueue = tmessageQueue;
39 messageReceiver = tmessageReceiver;
42 audio = Audio::getInstance();
43 video = Video::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
56 PlayerLiveTV::~PlayerLiveTV()
58 if (initted) shutdown();
61 int PlayerLiveTV::init()
63 if (initted) return 0;
65 demuxer = new DemuxerTS();
66 if (!demuxer) return 0;
68 if (!demuxer->init(this, audio, video, 2097152, 524288))
70 logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
86 int PlayerLiveTV::shutdown()
88 if (!initted) return 0;
97 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
99 return demuxer->getmpAudioChannels();
102 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
104 return demuxer->getac3AudioChannels();
107 int PlayerLiveTV::getCurrentAudioChannel()
109 return demuxer->getAID();
112 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
114 return demuxer->setAID(newChannel,type);
117 // ----------------------------------- Externally called events
119 void PlayerLiveTV::go(ULONG index)
121 struct PLInstruction i;
122 i.instruction = I_SETCHANNEL;
123 i.channelIndex = index;
124 instructions.push(i);
128 void PlayerLiveTV::setChannel(ULONG index)
130 logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
131 struct PLInstruction i;
132 i.instruction = I_SETCHANNEL;
133 i.channelIndex = index;
134 instructions.push(i);
135 threadSignalNoLock();
138 void PlayerLiveTV::stop()
140 logger->log("PlayerLiveTV", Log::DEBUG, "stop");
141 struct PLInstruction i;
142 i.instruction = I_STOP;
143 instructions.push(i);
148 // ----------------------------------- Callback
150 void PlayerLiveTV::call(void* caller)
152 if (caller == demuxer)
154 logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
156 int dxCurrentAspect = demuxer->getAspectRatio();
157 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
159 if (video->getTVsize() == Video::ASPECT16X9)
161 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
162 video->setAspectRatio(Video::ASPECT4X3);
166 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
169 Message* m = new Message();
171 m->to = messageReceiver;
172 m->message = Message::PLAYER_EVENT;
173 m->parameter = PlayerLiveTV::ASPECT43;
174 messageQueue->postMessageFromOuterSpace(m);
176 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
178 if (video->getTVsize() == Video::ASPECT16X9)
180 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
181 video->setAspectRatio(Video::ASPECT16X9);
185 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
188 Message* m = new Message();
190 m->to = messageReceiver;
191 m->message = Message::PLAYER_EVENT;
192 m->parameter = PlayerLiveTV::ASPECT169;
193 messageQueue->postMessageFromOuterSpace(m);
197 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
200 else if (caller == &afeed)
202 if (state == S_VIDEOSTARTUP)
204 logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
206 threadSignalNoLock();
211 // -----------------------------------
213 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
216 // 0 = normal stream packet
218 // 2 = connection lost
220 // logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
226 Message* m = new Message();
228 m->to = messageReceiver;
229 m->message = Message::PLAYER_EVENT;
230 m->parameter = PlayerLiveTV::STREAM_END;
231 messageQueue->postMessageFromOuterSpace(m);
234 if (streamChunks.size() < 11)
239 streamChunks.push(s);
240 threadSignalNoLock();
244 // Too many chunks in streamChunks, drop this chunk
246 logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
250 void PlayerLiveTV::clearStreamChunks()
252 while(streamChunks.size())
254 logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
255 struct StreamChunk s = streamChunks.front();
261 void PlayerLiveTV::chunkToDemuxer()
263 StreamChunk s = streamChunks.front();
265 //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
266 /*int a = */demuxer->put((UCHAR*)s.data, s.len);
267 //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
271 void PlayerLiveTV::switchState(UCHAR newState)
273 logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
277 case S_STOP: // FROM S_STOP
292 audio->setStreamType(Audio::MPEG2_PES);
308 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
315 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
328 vdr->stopStreaming();
342 audio->setStreamType(Audio::MPEG2_PES);
358 vdr->stopStreaming();
372 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
379 case S_PREBUFFERING: // FROM S_PREBUFFERING
392 vdr->stopStreaming();
407 audio->setStreamType(Audio::MPEG2_PES);
423 vdr->stopStreaming();
437 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
444 case S_PLAY: // FROM S_PLAY
450 vdr->stopStreaming();
464 vdr->stopStreaming();
479 audio->setStreamType(Audio::MPEG2_PES);
495 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
504 bool PlayerLiveTV::checkError()
506 if (!vdr->isConnected())
508 if (state != S_STOP) switchState(S_STOP);
510 Message* m = new Message();
512 m->to = messageReceiver;
513 m->message = Message::PLAYER_EVENT;
514 m->parameter = PlayerLiveTV::CONNECTION_LOST;
515 messageQueue->postMessageFromOuterSpace(m);
522 void PlayerLiveTV::optimizeInstructionQueue()
526 // Currently there are only 2 instruction types, so this is a bit overkill...
528 struct PLInstruction i;
529 while(instructions.size() > 1)
531 i = instructions.front();
532 if (i.instruction == I_SETCHANNEL)
534 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
536 else if (i.instruction == I_STOP)
538 return; // return here and ensure the next instruction will be stop
543 void PlayerLiveTV::threadMethod()
547 if (videoStartup) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
549 switchState(S_PREBUFFERING);
550 videoStartup = false;
556 while(!instructions.empty())
558 if (instructions.size() > 1) optimizeInstructionQueue();
560 struct PLInstruction i = instructions.front();
563 if (i.instruction == I_SETCHANNEL)
565 logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
567 switchState(S_VIDEOSTARTUP);
571 Channel* chan = (*chanList)[i.channelIndex];
573 demuxer->setVID(chan->vpid);
574 if (chan->numAPids > 0)
576 demuxer->setAID(chan->apids[0].pid,0);
577 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
581 logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
584 int streamSuccess = vdr->streamChannel(chan->number, this);
585 if (!checkError() && !streamSuccess)
587 Message* m = new Message();
589 m->to = messageReceiver;
590 m->message = Message::PLAYER_EVENT;
591 m->parameter = PlayerLiveTV::STREAM_END;
592 messageQueue->postMessageFromOuterSpace(m);
596 else if (i.instruction == I_STOP)
598 logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
609 while(streamChunks.size())
613 if (state == S_PREBUFFERING)
616 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
617 logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
619 Message* m = new Message();
621 m->to = messageReceiver;
622 m->message = Message::PLAYER_EVENT;
623 m->parameter = PlayerLiveTV::PREBUFFERING;
624 m->tag = percentDone;
625 messageQueue->postMessageFromOuterSpace(m);
627 if (preBufferCount == preBufferAmount)
636 threadWaitForSignal(); // unlocks and waits for signal
640 logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");