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 static const char* TAG = "PlayerRadioLive";
41 // ----------------------------------- Called from outside, one offs or info funcs
43 PlayerRadioLive::PlayerRadioLive(MessageQueue* tmessageQueue, MessageReceiver* tmessageReceiver, ChannelList* tchanList)
44 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this), chanList(tchanList)
46 audio = Audio::getInstance();
47 logger = LogNT::getInstance();
48 vdr = VDR::getInstance();
50 Video::getInstance()->turnVideoOff();
53 PlayerRadioLive::~PlayerRadioLive()
55 if (initted) shutdown();
58 int PlayerRadioLive::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->error(TAG, "Demuxer failed to init");
78 int PlayerRadioLive::shutdown()
80 if (!initted) return 0;
81 if (state != S_STOP) // FIXME check when this is called and how. This is not thread-sync bullet proof as-is.
83 logger->debug(TAG, "state is not stop, calling");
91 bool* PlayerRadioLive::getDemuxerMpegAudioChannels()
93 return demuxer->getmpAudioChannels();
96 bool* PlayerRadioLive::getDemuxerAc3AudioChannels()
98 return demuxer->getac3AudioChannels();
101 int PlayerRadioLive::getCurrentAudioChannel()
103 return demuxer->getAID();
106 int* PlayerRadioLive::getTeletxtSubtitlePages()
111 int PlayerRadioLive::getCurrentSubtitleChannel()
113 return demuxer->getSubID();
116 void PlayerRadioLive::setAudioChannel(int newChannel, int type, int streamtype)
118 demuxer->setAID(newChannel, type, streamtype, true);
121 void PlayerRadioLive::setSubtitleChannel(int newChannel)
123 demuxer->setSubID(newChannel);
126 // ----------------------------------- Externally called events
128 void PlayerRadioLive::go(ULONG index)
130 playerThreadMutex.lock();
132 struct PLInstruction i;
133 i.instruction = I_SETCHANNEL;
134 i.channelIndex = index;
135 instructions.push(i);
137 playerThread = std::thread([this]
139 playerThreadMutex.lock();
140 playerThreadMutex.unlock();
143 playerThreadMutex.unlock();
146 void PlayerRadioLive::setChannel(ULONG index)
148 logger->debug(TAG, "setChannel");
149 struct PLInstruction i;
150 i.instruction = I_SETCHANNEL;
151 i.channelIndex = index;
152 instructions.push(i);
153 logger->debug(TAG, "posted setChannel instruction, now {} in queue", instructions.size());
154 playerThreadCond.notify_one();
157 void PlayerRadioLive::stop()
159 logger->debug(TAG, "stop");
160 playerThreadMutex.lock();
161 struct PLInstruction i;
162 i.instruction = I_STOP;
163 instructions.push(i);
164 playerThreadCond.notify_one();
165 playerThreadMutex.unlock();
167 logger->debug(TAG, "stop successful");
170 // ----------------------------------- Callback
172 void PlayerRadioLive::call(void* /*caller*/)
176 // -----------------------------------
178 void PlayerRadioLive::streamReceive(ULONG flag, void* data, ULONG len)
181 // 0 = normal stream packet
183 // 2 = connection lost
189 Message* m = new Message();
191 m->to = messageReceiver;
192 m->message = Message::PLAYER_EVENT;
193 m->parameter = PlayerRadioLive::STREAM_END;
194 messageQueue->postMessage(m);
197 if (streamChunks.size() < 11)
202 streamChunks.push(s);
203 playerThreadCond.notify_one();
207 // Too many chunks in streamChunks, drop this chunk
209 logger->warn(TAG, "Dropped chunk");
213 void PlayerRadioLive::clearStreamChunks()
215 while(streamChunks.size())
217 logger->debug(TAG, "Dropping chunk from old stream");
218 struct StreamChunk s = streamChunks.front();
224 void PlayerRadioLive::chunkToDemuxer()
226 StreamChunk s = streamChunks.front();
228 //logger->debug(TAG, "About to call demuxer with {} {}", (void*)s.data, s.len);
229 /*int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
230 //logger->debug(TAG, "put {} to demuxer", a);
234 void PlayerRadioLive::switchState(UCHAR newState)
236 logger->debug(TAG, "Switch from state {} to state {}", state, newState);
240 case S_STOP: // FROM S_STOP
249 audio->setStreamType(Audio::MPEG2_PES);
250 audio->systemMuteOff();
263 logger->crit(TAG, "Thread called state {} to state {} which is not supported", state, newState);
270 case S_PREBUFFERING: // FROM S_PREBUFFERING
282 vdr->stopStreaming();
292 vdr->stopStreaming();
308 logger->crit(TAG, "Thread called state {} to state {} which is not supported", state, newState);
315 case S_PLAY: // FROM S_PLAY
321 vdr->stopStreaming();
329 case S_PREBUFFERING: // IS THIS HOW IT WORKS?
331 vdr->stopStreaming();
347 logger->crit(TAG, "Thread called state {} to state {} which is not supported", state, newState);
356 bool PlayerRadioLive::checkError()
358 if (!vdr->isConnected())
362 Message* m = new Message();
364 m->to = messageReceiver;
365 m->message = Message::PLAYER_EVENT;
366 m->parameter = PlayerRadioLive::CONNECTION_LOST;
367 messageQueue->postMessage(m);
374 void PlayerRadioLive::optimizeInstructionQueue()
378 // Currently there are only 2 instruction types, so this is a bit overkill...
380 struct PLInstruction i;
381 while(instructions.size() > 1)
383 i = instructions.front();
384 if (i.instruction == I_SETCHANNEL)
386 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
388 else if (i.instruction == I_STOP)
390 return; // return here and ensure the next instruction will be stop
395 void PlayerRadioLive::threadMethod()
397 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
401 while(!instructions.empty())
403 if (instructions.size() > 1)
405 logger->debug(TAG, "Should optimise");
406 optimizeInstructionQueue();
409 struct PLInstruction i = instructions.front();
412 if (i.instruction == I_SETCHANNEL)
414 logger->debug(TAG, "start new stream");
416 switchState(S_PREBUFFERING);
420 Channel* chan = (*chanList)[i.channelIndex];
425 if (chan->numAPids > 0)
428 while (j < chan->numAPids && !found)
430 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type))
432 demuxer->setAID(chan->apids[j].pid, 0, chan->apids[j].type, true);
433 audio->setStreamType(Audio::MPEG2_PES);
434 logger->debug(TAG, "Demuxer pids: {} {} {}", chan->vpid, chan->apids[j].pid, chan->apids[j].type);
443 if (chan->numDPids > 0 && audio->maysupportAc3())
446 while (j < chan->numDPids && !found)
448 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type))
450 demuxer->setAID(chan->dpids[j].pid, 1, chan->dpids[j].type, true);
451 audio->setStreamType(Audio::MPEG2_PES);
452 logger->debug(TAG, "Demuxer pids: {} {} (ac3) {}", chan->vpid, chan->dpids[j].pid, chan->dpids[j].type);
460 logger->warn(TAG, "Demuxer no pids!");
464 int streamSuccess = vdr->streamChannel(chan->number, this);
465 if (!checkError() && !streamSuccess)
467 Message* m = new Message();
469 m->to = messageReceiver;
470 m->message = Message::PLAYER_EVENT;
471 m->parameter = PlayerRadioLive::STREAM_END;
472 messageQueue->postMessage(m);
476 else if (i.instruction == I_STOP)
478 logger->debug(TAG, "Stopping");
485 while(streamChunks.size())
489 if (state == S_PREBUFFERING)
492 ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
494 logger->debug(TAG, "Prebuffering {}%", percentDone);
496 Message* m = new Message();
498 m->to = messageReceiver;
499 m->message = Message::PLAYER_EVENT;
500 m->parameter = PlayerRadioLive::PREBUFFERING;
501 m->tag = percentDone;
502 messageQueue->postMessage(m);
504 if (preBufferCount == preBufferAmount)
513 if (!instructions.empty()) { ul.unlock(); continue; }
514 playerThreadCond.wait(ul);
518 logger->debug(TAG, "End of thread");