2 Copyright 2008-2020 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/>.
29 #include "demuxerts.h"
31 #include "messagequeue.h"
37 #include "playerradiolive.h"
39 // ----------------------------------- Called from outside, one offs or info funcs
41 PlayerRadioLive::PlayerRadioLive(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)
42 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this), chanList(tchanList)
44 audio = Audio::getInstance();
45 logger = Log::getInstance();
46 vdr = VDR::getInstance();
48 Video::getInstance()->turnVideoOff();
51 PlayerRadioLive::~PlayerRadioLive()
53 if (initted) shutdown();
56 int PlayerRadioLive::init()
58 if (initted) return 0;
60 demuxer = new DemuxerTS();
61 if (!demuxer) return 0;
63 if (!demuxer->init(this, audio, NULL, NULL, 0, 200000, 0))
65 logger->log("PlayerRadioLive", Log::ERR, "Demuxer failed to init");
76 int PlayerRadioLive::shutdown()
78 if (!initted) return 0;
79 if (state != S_STOP) // FIXME check when this is called and how. This is not thread-sync bullet proof as-is.
81 logger->log("PlayerRadioLive", Log::DEBUG, "state is not stop, calling");
89 bool* PlayerRadioLive::getDemuxerMpegAudioChannels()
91 return demuxer->getmpAudioChannels();
94 bool* PlayerRadioLive::getDemuxerAc3AudioChannels()
96 return demuxer->getac3AudioChannels();
99 int PlayerRadioLive::getCurrentAudioChannel()
101 return demuxer->getAID();
104 int* PlayerRadioLive::getTeletxtSubtitlePages()
109 int PlayerRadioLive::getCurrentSubtitleChannel()
111 return demuxer->getSubID();
114 void PlayerRadioLive::setAudioChannel(int newChannel, int type, int streamtype)
116 demuxer->setAID(newChannel, type, streamtype, true);
119 void PlayerRadioLive::setSubtitleChannel(int newChannel)
121 demuxer->setSubID(newChannel);
124 // ----------------------------------- Externally called events
126 void PlayerRadioLive::go(ULONG index)
128 playerThreadMutex.lock();
130 struct PLInstruction i;
131 i.instruction = I_SETCHANNEL;
132 i.channelIndex = index;
133 instructions.push(i);
135 playerThread = std::thread([this]
137 playerThreadMutex.lock();
138 playerThreadMutex.unlock();
141 playerThreadMutex.unlock();
144 void PlayerRadioLive::setChannel(ULONG index)
146 logger->log("PlayerRadioLive", Log::DEBUG, "setChannel");
147 struct PLInstruction i;
148 i.instruction = I_SETCHANNEL;
149 i.channelIndex = index;
150 instructions.push(i);
151 logger->log("PlayerRadioLive", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());
152 playerThreadCond.notify_one();
155 void PlayerRadioLive::stop()
157 logger->log("PlayerRadioLive", Log::DEBUG, "stop");
158 playerThreadMutex.lock();
159 struct PLInstruction i;
160 i.instruction = I_STOP;
161 instructions.push(i);
162 playerThreadCond.notify_one();
163 playerThreadMutex.unlock();
165 logger->log("PlayerRadioLive", Log::DEBUG, "stop successful");
168 // ----------------------------------- Callback
170 void PlayerRadioLive::call(void* /*caller*/)
174 // -----------------------------------
176 void PlayerRadioLive::streamReceive(ULONG flag, void* data, ULONG len)
179 // 0 = normal stream packet
181 // 2 = connection lost
187 Message* m = new Message();
189 m->to = messageReceiver;
190 m->message = Message::PLAYER_EVENT;
191 m->parameter = PlayerRadioLive::STREAM_END;
192 messageQueue->postMessage(m);
195 if (streamChunks.size() < 11)
200 streamChunks.push(s);
201 playerThreadCond.notify_one();
205 // Too many chunks in streamChunks, drop this chunk
207 logger->log("PlayerRadioLive", Log::WARN, "Dropped chunk");
211 void PlayerRadioLive::clearStreamChunks()
213 while(streamChunks.size())
215 logger->log("PlayerRadioLive", Log::DEBUG, "Dropping chunk from old stream");
216 struct StreamChunk s = streamChunks.front();
222 void PlayerRadioLive::chunkToDemuxer()
224 StreamChunk s = streamChunks.front();
226 //logger->log("PlayerRadioLive", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
227 /*int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
228 //logger->log("PlayerRadioLive", Log::DEBUG, "put %i to demuxer", a);
232 void PlayerRadioLive::switchState(UCHAR newState)
234 logger->log("PlayerRadioLive", Log::DEBUG, "Switch from state %u to state %u", state, newState);
238 case S_STOP: // FROM S_STOP
247 audio->setStreamType(Audio::MPEG2_PES);
248 audio->systemMuteOff();
261 logger->log("PlayerRadioLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
268 case S_PREBUFFERING: // FROM S_PREBUFFERING
280 vdr->stopStreaming();
290 vdr->stopStreaming();
306 logger->log("PlayerRadioLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
313 case S_PLAY: // FROM S_PLAY
319 vdr->stopStreaming();
327 case S_PREBUFFERING: // IS THIS HOW IT WORKS?
329 vdr->stopStreaming();
345 logger->log("PlayerRadioLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
354 bool PlayerRadioLive::checkError()
356 if (!vdr->isConnected())
360 Message* m = new Message();
362 m->to = messageReceiver;
363 m->message = Message::PLAYER_EVENT;
364 m->parameter = PlayerRadioLive::CONNECTION_LOST;
365 messageQueue->postMessage(m);
372 void PlayerRadioLive::optimizeInstructionQueue()
376 // Currently there are only 2 instruction types, so this is a bit overkill...
378 struct PLInstruction i;
379 while(instructions.size() > 1)
381 i = instructions.front();
382 if (i.instruction == I_SETCHANNEL)
384 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
386 else if (i.instruction == I_STOP)
388 return; // return here and ensure the next instruction will be stop
393 void PlayerRadioLive::threadMethod()
395 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
399 while(!instructions.empty())
401 if (instructions.size() > 1)
403 logger->log("PlayerRadioLive", Log::DEBUG, "Should optimise");
404 optimizeInstructionQueue();
407 struct PLInstruction i = instructions.front();
410 if (i.instruction == I_SETCHANNEL)
412 logger->log("PlayerRadioLive", Log::DEBUG, "start new stream");
414 switchState(S_PREBUFFERING);
418 Channel* chan = (*chanList)[i.channelIndex];
423 if (chan->numAPids > 0)
426 while (j < chan->numAPids && !found)
428 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type))
430 demuxer->setAID(chan->apids[j].pid, 0, chan->apids[j].type, true);
431 audio->setStreamType(Audio::MPEG2_PES);
432 logger->log("PlayerRadioLive", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid, chan->apids[j].type);
441 if (chan->numDPids > 0 && audio->maysupportAc3())
444 while (j < chan->numDPids && !found)
446 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type))
448 demuxer->setAID(chan->dpids[j].pid, 1, chan->dpids[j].type, true);
449 audio->setStreamType(Audio::MPEG2_PES);
450 logger->log("PlayerRadioLive", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid, chan->dpids[j].type);
458 logger->log("PlayerRadioLive", Log::WARN, "Demuxer no pids!");
462 int streamSuccess = vdr->streamChannel(chan->number, this);
463 if (!checkError() && !streamSuccess)
465 Message* m = new Message();
467 m->to = messageReceiver;
468 m->message = Message::PLAYER_EVENT;
469 m->parameter = PlayerRadioLive::STREAM_END;
470 messageQueue->postMessage(m);
474 else if (i.instruction == I_STOP)
476 logger->log("PlayerRadioLive", Log::DEBUG, "Stopping");
483 while(streamChunks.size())
487 if (state == S_PREBUFFERING)
490 ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
492 logger->log("PlayerRadioLive", Log::DEBUG, "Prebuffering %lu%%", percentDone);
494 Message* m = new Message();
496 m->to = messageReceiver;
497 m->message = Message::PLAYER_EVENT;
498 m->parameter = PlayerRadioLive::PREBUFFERING;
499 m->tag = percentDone;
500 messageQueue->postMessage(m);
502 if (preBufferCount == preBufferAmount)
511 if (!instructions.empty()) { ul.unlock(); continue; }
512 playerThreadCond.wait(ul);
516 logger->log("PlayerRadioLive", Log::DEBUG, "End of thread");