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, see <https://www.gnu.org/licenses/>.
20 #include "playerliveradio.h"
25 #include "demuxerts.h"
27 #include "messagequeue.h"
33 // ----------------------------------- Called from outside, one offs or info funcs
35 PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
38 messageQueue = tmessageQueue;
39 messageReceiver = tmessageReceiver;
42 audio = Audio::getInstance();
43 logger = Log::getInstance();
44 vdr = VDR::getInstance();
49 Video::getInstance()->turnVideoOff();
52 PlayerLiveRadio::~PlayerLiveRadio()
54 if (initted) shutdown();
57 int PlayerLiveRadio::init()
59 if (initted) return 0;
61 demuxer = new DemuxerTS();
62 if (!demuxer) return 0;
64 if (!demuxer->init(this, audio, NULL, NULL, 0, 200000, 0))
66 logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");
78 int PlayerLiveRadio::shutdown()
80 if (!initted) return 0;
87 bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()
89 return demuxer->getmpAudioChannels();
92 bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()
94 return demuxer->getac3AudioChannels();
97 int PlayerLiveRadio::getCurrentAudioChannel()
99 return demuxer->getAID();
102 int* PlayerLiveRadio::getTeletxtSubtitlePages()
107 int PlayerLiveRadio::getCurrentSubtitleChannel()
109 return demuxer->getSubID();
112 void PlayerLiveRadio::setAudioChannel(int newChannel, int type, int streamtype)
114 demuxer->setAID(newChannel, type, streamtype, true);
117 void PlayerLiveRadio::setSubtitleChannel(int newChannel)
119 demuxer->setSubID(newChannel);
122 // ----------------------------------- Externally called events
124 void PlayerLiveRadio::go(ULONG index)
126 struct PLInstruction i;
127 i.instruction = I_SETCHANNEL;
128 i.channelIndex = index;
129 instructions.push(i);
133 void PlayerLiveRadio::setChannel(ULONG index)
135 logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");
136 struct PLInstruction i;
137 i.instruction = I_SETCHANNEL;
138 i.channelIndex = index;
139 instructions.push(i);
140 logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
141 threadSignalNoLock();
144 void PlayerLiveRadio::stop()
146 logger->log("PlayerLiveRadio", Log::DEBUG, "stop");
147 struct PLInstruction i;
148 i.instruction = I_STOP;
149 instructions.push(i);
154 // ----------------------------------- Callback
156 void PlayerLiveRadio::call(void* caller)
160 // -----------------------------------
162 void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)
165 // 0 = normal stream packet
167 // 2 = connection lost
173 Message* m = new Message();
175 m->to = messageReceiver;
176 m->message = Message::PLAYER_EVENT;
177 m->parameter = PlayerLiveRadio::STREAM_END;
178 messageQueue->postMessageFromOuterSpace(m);
181 if (streamChunks.size() < 11)
186 streamChunks.push(s);
187 threadSignalNoLock();
191 // Too many chunks in streamChunks, drop this chunk
193 logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");
197 void PlayerLiveRadio::clearStreamChunks()
199 while(streamChunks.size())
201 logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");
202 struct StreamChunk s = streamChunks.front();
208 void PlayerLiveRadio::chunkToDemuxer()
210 StreamChunk s = streamChunks.front();
212 //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
213 /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);
214 //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);
218 void PlayerLiveRadio::switchState(UCHAR newState)
220 logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);
224 case S_STOP: // FROM S_STOP
233 audio->setStreamType(Audio::MPEG2_PES);
234 audio->systemMuteOff();
247 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
254 case S_PREBUFFERING: // FROM S_PREBUFFERING
266 vdr->stopStreaming();
276 vdr->stopStreaming();
292 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
299 case S_PLAY: // FROM S_PLAY
305 vdr->stopStreaming();
313 case S_PREBUFFERING: // IS THIS HOW IT WORKS?
315 vdr->stopStreaming();
331 logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
340 bool PlayerLiveRadio::checkError()
342 if (!vdr->isConnected())
346 Message* m = new Message();
348 m->to = messageReceiver;
349 m->message = Message::PLAYER_EVENT;
350 m->parameter = PlayerLiveRadio::CONNECTION_LOST;
351 messageQueue->postMessageFromOuterSpace(m);
358 void PlayerLiveRadio::optimizeInstructionQueue()
362 // Currently there are only 2 instruction types, so this is a bit overkill...
364 struct PLInstruction i;
365 while(instructions.size() > 1)
367 i = instructions.front();
368 if (i.instruction == I_SETCHANNEL)
370 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
372 else if (i.instruction == I_STOP)
374 return; // return here and ensure the next instruction will be stop
379 void PlayerLiveRadio::threadMethod()
383 while(!instructions.empty())
385 if (instructions.size() > 1)
387 logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");
388 optimizeInstructionQueue();
391 struct PLInstruction i = instructions.front();
394 if (i.instruction == I_SETCHANNEL)
396 logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");
398 switchState(S_PREBUFFERING);
402 Channel* chan = (*chanList)[i.channelIndex];
407 if (chan->numAPids > 0)
410 while (j < chan->numAPids && !found)
412 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type))
414 demuxer->setAID(chan->apids[j].pid, 0, chan->apids[j].type, true);
415 audio->setStreamType(Audio::MPEG2_PES);
416 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid, chan->apids[j].type);
425 if (chan->numDPids > 0 && audio->maysupportAc3())
428 while (j < chan->numDPids && !found)
430 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type))
432 demuxer->setAID(chan->dpids[j].pid, 1, chan->dpids[j].type, true);
433 audio->setStreamType(Audio::MPEG2_PES);
434 logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid, chan->dpids[j].type);
442 logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");
446 int streamSuccess = vdr->streamChannel(chan->number, this);
447 if (!checkError() && !streamSuccess)
449 Message* m = new Message();
451 m->to = messageReceiver;
452 m->message = Message::PLAYER_EVENT;
453 m->parameter = PlayerLiveRadio::STREAM_END;
454 messageQueue->postMessageFromOuterSpace(m);
458 else if (i.instruction == I_STOP)
460 logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");
471 while(streamChunks.size())
475 if (state == S_PREBUFFERING)
478 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
479 logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);
481 Message* m = new Message();
483 m->to = messageReceiver;
484 m->message = Message::PLAYER_EVENT;
485 m->parameter = PlayerLiveRadio::PREBUFFERING;
486 m->tag = percentDone;
487 messageQueue->postMessageFromOuterSpace(m);
489 if (preBufferCount == preBufferAmount)
498 threadWaitForSignal(); // unlocks and waits for signal
502 logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");