2 Copyright 2008 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
21 #include "playerliveradio.h"
\r
25 #include "demuxerts.h"
\r
27 #include "messagequeue.h"
\r
29 #include "message.h"
\r
30 #include "channel.h"
\r
33 // ----------------------------------- Called from outside, one offs or info funcs
\r
35 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
\r
38 messageQueue = tmessageQueue;
\r
39 messageReceiver = tmessageReceiver;
\r
40 chanList = tchanList;
\r
42 audio = Audio::getInstance();
\r
43 logger = Log::getInstance();
\r
44 vdr = VDR::getInstance();
\r
49 Video::getInstance()->turnVideoOff();
\r
52 PlayerLiveRadio::~PlayerLiveRadio()
\r
54 if (initted) shutdown();
\r
57 int PlayerLiveRadio::init()
\r
59 if (initted) return 0;
\r
61 demuxer = new DemuxerTS();
\r
62 if (!demuxer) return 0;
\r
64 if (!demuxer->init(this, audio, NULL, NULL, 0, 200000,0))
\r
66 logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");
\r
78 int PlayerLiveRadio::shutdown()
\r
80 if (!initted) return 0;
\r
91 bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()
\r
93 return demuxer->getmpAudioChannels();
\r
96 bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()
\r
98 return demuxer->getac3AudioChannels();
\r
101 int PlayerLiveRadio::getCurrentAudioChannel()
\r
103 return demuxer->getAID();
\r
106 int *PlayerLiveRadio::getTeletxtSubtitlePages(){
\r
110 int PlayerLiveRadio::getCurrentSubtitleChannel(){
\r
111 return demuxer->getSubID();
\r
114 void PlayerLiveRadio::setAudioChannel(int newChannel, int type)
\r
116 demuxer->setAID(newChannel, type);
\r
119 void PlayerLiveRadio::setSubtitleChannel(int newChannel)
\r
121 demuxer->setSubID(newChannel);
\r
124 // ----------------------------------- Externally called events
\r
126 void PlayerLiveRadio::go(ULONG index)
\r
128 struct PLInstruction i;
\r
129 i.instruction = I_SETCHANNEL;
\r
130 i.channelIndex = index;
\r
131 instructions.push(i);
\r
135 void PlayerLiveRadio::setChannel(ULONG index)
\r
137 logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");
\r
138 struct PLInstruction i;
\r
139 i.instruction = I_SETCHANNEL;
\r
140 i.channelIndex = index;
\r
141 instructions.push(i);
\r
142 logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
\r
143 threadSignalNoLock();
\r
146 void PlayerLiveRadio::stop()
\r
148 logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
\r
149 struct PLInstruction i;
\r
150 i.instruction = I_STOP;
\r
151 instructions.push(i);
\r
156 // ----------------------------------- Callback
\r
158 void PlayerLiveRadio::call(void* caller)
\r
162 // -----------------------------------
\r
164 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)
\r
167 // 0 = normal stream packet
\r
169 // 2 = connection lost
\r
175 Message* m = new Message();
\r
177 m->to = messageReceiver;
\r
178 m->message = Message::PLAYER_EVENT;
\r
179 m->parameter = PlayerLiveRadio::STREAM_END;
\r
180 messageQueue->postMessageFromOuterSpace(m);
\r
183 if (streamChunks.size() < 11)
\r
188 streamChunks.push(s);
\r
189 threadSignalNoLock();
\r
193 // Too many chunks in streamChunks, drop this chunk
\r
195 logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
\r
199 void PlayerLiveRadio::clearStreamChunks()
\r
201 while(streamChunks.size())
\r
203 logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
\r
204 struct StreamChunk s = streamChunks.front();
\r
205 streamChunks.pop();
\r
210 void PlayerLiveRadio::chunkToDemuxer()
\r
212 StreamChunk s = streamChunks.front();
\r
213 streamChunks.pop();
\r
214 //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
\r
215 /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);
\r
216 //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
\r
220 void PlayerLiveRadio::switchState(UCHAR newState)
\r
222 logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
\r
226 case S_STOP: // FROM S_STOP
\r
230 case S_PREBUFFERING:
\r
235 audio->setStreamType(Audio::MPEG2_PES);
\r
236 audio->systemMuteOff();
\r
237 audio->doMuting();
\r
244 preBufferCount = 0;
\r
249 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
256 case S_PREBUFFERING: // FROM S_PREBUFFERING
\r
268 vdr->stopStreaming();
\r
269 clearStreamChunks();
\r
276 case S_PREBUFFERING:
\r
278 vdr->stopStreaming();
\r
279 clearStreamChunks();
\r
289 preBufferCount = 0;
\r
294 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
301 case S_PLAY: // FROM S_PLAY
\r
307 vdr->stopStreaming();
\r
308 clearStreamChunks();
\r
315 case S_PREBUFFERING: // IS THIS HOW IT WORKS?
\r
317 vdr->stopStreaming();
\r
318 clearStreamChunks();
\r
328 preBufferCount = 0;
\r
333 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
342 bool PlayerLiveRadio::checkError()
\r
344 if (!vdr->isConnected())
\r
346 switchState(S_STOP);
\r
348 Message* m = new Message();
\r
350 m->to = messageReceiver;
\r
351 m->message = Message::PLAYER_EVENT;
\r
352 m->parameter = PlayerLiveRadio::CONNECTION_LOST;
\r
353 messageQueue->postMessageFromOuterSpace(m);
\r
360 void PlayerLiveRadio::optimizeInstructionQueue()
\r
364 // Currently there are only 2 instruction types, so this is a bit overkill...
\r
366 struct PLInstruction i;
\r
367 while(instructions.size() > 1)
\r
369 i = instructions.front();
\r
370 if (i.instruction == I_SETCHANNEL)
\r
372 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
\r
374 else if (i.instruction == I_STOP)
\r
376 return; // return here and ensure the next instruction will be stop
\r
381 void PlayerLiveRadio::threadMethod()
\r
385 while(!instructions.empty())
\r
387 if (instructions.size() > 1)
\r
389 logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");
\r
390 optimizeInstructionQueue();
\r
393 struct PLInstruction i = instructions.front();
\r
394 instructions.pop();
\r
396 if (i.instruction == I_SETCHANNEL)
\r
398 logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
\r
400 switchState(S_PREBUFFERING);
\r
404 Channel* chan = (*chanList)[i.channelIndex];
\r
407 if (chan->numAPids > 0)
\r
409 demuxer->setAID(chan->apids[0].pid,0);
\r
410 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
\r
414 logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
\r
417 int streamSuccess = vdr->streamChannel(chan->number, this);
\r
418 if (!checkError() && !streamSuccess)
\r
420 Message* m = new Message();
\r
422 m->to = messageReceiver;
\r
423 m->message = Message::PLAYER_EVENT;
\r
424 m->parameter = PlayerLiveRadio::STREAM_END;
\r
425 messageQueue->postMessageFromOuterSpace(m);
\r
429 else if (i.instruction == I_STOP)
\r
431 logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
\r
432 switchState(S_STOP);
\r
440 if (stopNow) break;
\r
442 while(streamChunks.size())
\r
446 if (state == S_PREBUFFERING)
\r
449 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
\r
450 logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
\r
452 Message* m = new Message();
\r
454 m->to = messageReceiver;
\r
455 m->message = Message::PLAYER_EVENT;
\r
456 m->parameter = PlayerLiveRadio::PREBUFFERING;
\r
457 m->tag = percentDone;
\r
458 messageQueue->postMessageFromOuterSpace(m);
\r
460 if (preBufferCount == preBufferAmount)
\r
462 switchState(S_PLAY);
\r
469 threadWaitForSignal(); // unlocks and waits for signal
\r
473 logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");
\r