2 Copyright 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 "playerliveradio.h"
26 #include "demuxerts.h"
28 #include "messagequeue.h"
34 // ----------------------------------- Called from outside, one offs or info funcs
36 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
39 messageQueue = tmessageQueue;
40 messageReceiver = tmessageReceiver;
43 audio = Audio::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
50 Video::getInstance()->turnVideoOff();
53 PlayerLiveRadio::~PlayerLiveRadio()
55 if (initted) shutdown();
58 int PlayerLiveRadio::init()
60 if (initted) return 0;
62 demuxer = new DemuxerTS();
63 if (!demuxer) return 0;
65 if (!demuxer->init(this, audio, NULL, NULL, 0, 200000, 0))
67 logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");
79 int PlayerLiveRadio::shutdown()
81 if (!initted) return 0;
88 bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()
90 return demuxer->getmpAudioChannels();
93 bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()
95 return demuxer->getac3AudioChannels();
98 int PlayerLiveRadio::getCurrentAudioChannel()
100 return demuxer->getAID();
103 int* PlayerLiveRadio::getTeletxtSubtitlePages()
108 int PlayerLiveRadio::getCurrentSubtitleChannel()
110 return demuxer->getSubID();
113 void PlayerLiveRadio::setAudioChannel(int newChannel, int type, int streamtype)
115 demuxer->setAID(newChannel, type, streamtype, true);
118 void PlayerLiveRadio::setSubtitleChannel(int newChannel)
120 demuxer->setSubID(newChannel);
123 // ----------------------------------- Externally called events
125 void PlayerLiveRadio::go(ULONG index)
127 struct PLInstruction i;
128 i.instruction = I_SETCHANNEL;
129 i.channelIndex = index;
130 instructions.push(i);
134 void PlayerLiveRadio::setChannel(ULONG index)
136 logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");
137 struct PLInstruction i;
138 i.instruction = I_SETCHANNEL;
139 i.channelIndex = index;
140 instructions.push(i);
141 logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
142 threadSignalNoLock();
145 void PlayerLiveRadio::stop()
147 logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
148 struct PLInstruction i;
149 i.instruction = I_STOP;
150 instructions.push(i);
155 // ----------------------------------- Callback
157 void PlayerLiveRadio::call(void* caller)
161 // -----------------------------------
163 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)
166 // 0 = normal stream packet
168 // 2 = connection lost
174 Message* m = new Message();
176 m->to = messageReceiver;
177 m->message = Message::PLAYER_EVENT;
178 m->parameter = PlayerLiveRadio::STREAM_END;
179 messageQueue->postMessageFromOuterSpace(m);
182 if (streamChunks.size() < 11)
187 streamChunks.push(s);
188 threadSignalNoLock();
192 // Too many chunks in streamChunks, drop this chunk
194 logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
198 void PlayerLiveRadio::clearStreamChunks()
200 while(streamChunks.size())
202 logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
203 struct StreamChunk s = streamChunks.front();
209 void PlayerLiveRadio::chunkToDemuxer()
211 StreamChunk s = streamChunks.front();
213 //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
214 /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);
215 //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
219 void PlayerLiveRadio::switchState(UCHAR newState)
221 logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
225 case S_STOP: // FROM S_STOP
234 audio->setStreamType(Audio::MPEG2_PES);
235 audio->systemMuteOff();
248 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
255 case S_PREBUFFERING: // FROM S_PREBUFFERING
267 vdr->stopStreaming();
277 vdr->stopStreaming();
293 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
300 case S_PLAY: // FROM S_PLAY
306 vdr->stopStreaming();
314 case S_PREBUFFERING: // IS THIS HOW IT WORKS?
316 vdr->stopStreaming();
332 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
341 bool PlayerLiveRadio::checkError()
343 if (!vdr->isConnected())
347 Message* m = new Message();
349 m->to = messageReceiver;
350 m->message = Message::PLAYER_EVENT;
351 m->parameter = PlayerLiveRadio::CONNECTION_LOST;
352 messageQueue->postMessageFromOuterSpace(m);
359 void PlayerLiveRadio::optimizeInstructionQueue()
363 // Currently there are only 2 instruction types, so this is a bit overkill...
365 struct PLInstruction i;
366 while(instructions.size() > 1)
368 i = instructions.front();
369 if (i.instruction == I_SETCHANNEL)
371 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
373 else if (i.instruction == I_STOP)
375 return; // return here and ensure the next instruction will be stop
380 void PlayerLiveRadio::threadMethod()
384 while(!instructions.empty())
386 if (instructions.size() > 1)
388 logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");
389 optimizeInstructionQueue();
392 struct PLInstruction i = instructions.front();
395 if (i.instruction == I_SETCHANNEL)
397 logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
399 switchState(S_PREBUFFERING);
403 Channel* chan = (*chanList)[i.channelIndex];
408 if (chan->numAPids > 0)
411 while (j < chan->numAPids && !found)
413 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type))
415 demuxer->setAID(chan->apids[j].pid, 0, chan->apids[j].type, true);
416 audio->setStreamType(Audio::MPEG2_PES);
417 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid, chan->apids[j].type);
426 if (chan->numDPids > 0 && audio->maysupportAc3())
429 while (j < chan->numDPids && !found)
431 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type))
433 demuxer->setAID(chan->dpids[j].pid, 1, chan->dpids[j].type, true);
434 audio->setStreamType(Audio::MPEG2_PES);
435 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid, chan->dpids[j].type);
443 logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
447 int streamSuccess = vdr->streamChannel(chan->number, this);
448 if (!checkError() && !streamSuccess)
450 Message* m = new Message();
452 m->to = messageReceiver;
453 m->message = Message::PLAYER_EVENT;
454 m->parameter = PlayerLiveRadio::STREAM_END;
455 messageQueue->postMessageFromOuterSpace(m);
459 else if (i.instruction == I_STOP)
461 logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
472 while(streamChunks.size())
476 if (state == S_PREBUFFERING)
479 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
480 logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
482 Message* m = new Message();
484 m->to = messageReceiver;
485 m->message = Message::PLAYER_EVENT;
486 m->parameter = PlayerLiveRadio::PREBUFFERING;
487 m->tag = percentDone;
488 messageQueue->postMessageFromOuterSpace(m);
490 if (preBufferCount == preBufferAmount)
499 threadWaitForSignal(); // unlocks and waits for signal
503 logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");