2 Copyright 2007-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/>.
30 #include "demuxerts.h"
32 #include "messagequeue.h"
36 #include "dvbsubtitles.h"
37 #include "osdreceiver.h"
40 #include "playervideolive.h"
42 // ----------------------------------- Called from outside, one offs or info funcs
44 PlayerVideoLive::PlayerVideoLive(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
45 : vfeed(this), afeed(this), tfeed(this),
46 messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), osdReceiver(tosdReceiver), chanList(tchanList)
48 audio = Audio::getInstance();
49 video = Video::getInstance();
50 logger = Log::getInstance();
51 vdr = VDR::getInstance();
56 PlayerVideoLive::~PlayerVideoLive()
58 if (initted) shutdown();
61 int PlayerVideoLive::init()
63 if (initted) return 0;
65 demuxer = new DemuxerTS();
66 if (!demuxer) return 0;
67 subtitles = new DVBSubtitles(osdReceiver);
68 if (!subtitles) return 0;
70 teletext = new TeletextDecoderVBIEBU();
72 unsigned int demux_video_size = 2097152;
73 unsigned int demux_audio_size = 524288;
75 if (video->supportsh264())
77 demux_video_size *= 5 * 1;//5;
80 if (audio->maysupportAc3())
82 //demux_audio_size*=2;
85 int text_fak = video->getTeletextBufferFaktor();
87 if (!demuxer->init(this, audio, video, teletext, demux_video_size, demux_audio_size, 65536 * text_fak, 25./*unimportant*/, subtitles))
89 logger->log("PlayerVideoLive", Log::ERR, "Demuxer failed to init");
102 int PlayerVideoLive::shutdown()
104 if (!initted) return 0;
105 logger->log("PlayerVideoLive", Log::DEBUG, "Shutdown");
108 logger->log("PlayerVideoLive", Log::DEBUG, "state is not stop, calling");
121 bool* PlayerVideoLive::getDemuxerMpegAudioChannels()
123 return demuxer->getmpAudioChannels();
126 bool* PlayerVideoLive::getDemuxerAc3AudioChannels()
128 return demuxer->getac3AudioChannels();
131 int PlayerVideoLive::getCurrentAudioChannel()
133 return demuxer->getAID();
136 void PlayerVideoLive::setAudioChannel(int newChannel, int type,int streamtype)
138 demuxer->setAID(newChannel, type, streamtype, true);
141 void PlayerVideoLive::setSubtitleChannel(int newChannel)
143 demuxer->setSubID(newChannel);
146 int* PlayerVideoLive::getTeletxtSubtitlePages()
148 return teletext->getSubtitlePages();
151 int PlayerVideoLive::getCurrentSubtitleChannel()
153 return demuxer->getSubID();
156 bool PlayerVideoLive::toggleSubtitles()
158 if (!subtitlesShowing)
160 subtitlesShowing = true;
165 subtitlesShowing = false;
168 return subtitlesShowing;
171 void PlayerVideoLive::turnSubtitlesOn(bool ison)
175 subtitlesShowing = true;
180 subtitlesShowing = false;
185 void PlayerVideoLive::tellSubtitlesOSDVisible(bool visible)
187 subtitles->setOSDMenuVisibility(visible);
190 // ----------------------------------- Externally called events
192 void PlayerVideoLive::go(ULONG index)
194 playerThreadMutex.lock();
196 struct PLInstruction i;
197 i.instruction = I_SETCHANNEL;
198 i.channelIndex = index;
199 instructions.push(i);
201 playerThread = std::thread([this]
203 playerThreadMutex.lock();
204 playerThreadMutex.unlock();
207 playerThreadMutex.unlock();
210 void PlayerVideoLive::setChannel(ULONG index)
212 logger->log("PlayerVideoLive", Log::DEBUG, "setChannel");
213 struct PLInstruction i;
214 i.instruction = I_SETCHANNEL;
215 i.channelIndex = index;
216 instructions.push(i);
217 playerThreadCond.notify_one();
220 void PlayerVideoLive::stop()
222 logger->log("PlayerVideoLive", Log::DEBUG, "stop");
224 playerThreadMutex.lock();
226 struct PLInstruction i;
227 i.instruction = I_STOP;
228 instructions.push(i);
230 playerThreadCond.notify_one();
231 playerThreadMutex.unlock();
234 logger->log("PlayerVideoLive", Log::DEBUG, "stop succesfull");
237 // ----------------------------------- Callback
239 void PlayerVideoLive::call(void* caller)
241 if (caller == demuxer)
243 logger->log("PlayerVideoLive", Log::DEBUG, "Callback from demuxer");
246 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx, &pary);
247 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
249 if (video->getTVsize() == Video::ASPECT16X9)
251 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
252 video->setAspectRatio(Video::ASPECT4X3, parx, pary);
256 logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
259 Message* m = new Message();
261 m->to = messageReceiver;
262 m->message = Message::PLAYER_EVENT;
263 m->parameter = PlayerVideoLive::ASPECT43;
264 messageQueue->postMessage(m);
266 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
268 if (video->getTVsize() == Video::ASPECT16X9)
270 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
271 video->setAspectRatio(Video::ASPECT16X9, parx, pary);
275 logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
278 Message* m = new Message();
280 m->to = messageReceiver;
281 m->message = Message::PLAYER_EVENT;
282 m->parameter = PlayerVideoLive::ASPECT169;
283 messageQueue->postMessage(m);
287 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is something else... switch anyway");
288 video->setAspectRatio(dxCurrentAspect, parx, pary);
291 else if (caller == &afeed)
293 if (state == S_VIDEOSTARTUP)
295 logger->log("PlayerVideoLive", Log::DEBUG, "afeed video startup");
297 playerThreadCond.notify_one();
302 // -----------------------------------
304 void PlayerVideoLive::streamReceive(ULONG flag, void* data, ULONG len)
307 // 0 = normal stream packet
309 // 2 = connection lost
311 //logger->log("PlayerVideoLive", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
317 Message* m = new Message();
319 m->to = messageReceiver;
320 m->message = Message::PLAYER_EVENT;
321 m->parameter = PlayerVideoLive::STREAM_END;
322 messageQueue->postMessage(m);
325 if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)
330 streamChunks.push(s);
331 playerThreadCond.notify_one();
335 // Too many chunks in streamChunks, drop this chunk
337 logger->log("PlayerVideoLive", Log::WARN, "Dropped chunk");
341 void PlayerVideoLive::clearStreamChunks()
343 while(streamChunks.size())
345 logger->log("PlayerVideoLive", Log::DEBUG, "Dropping chunk from old stream");
346 struct StreamChunk s = streamChunks.front();
352 void PlayerVideoLive::chunkToDemuxer()
354 StreamChunk s = streamChunks.front();
356 // logger->log("PlayerVideoLive", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
357 /* int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
358 // logger->log("PlayerVideoLive", Log::DEBUG, "put %i to demuxer", a);
360 if (pendingAudioPlay && (demuxer->getHorizontalSize() || !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
365 //audio->setStreamType(Audio::MPEG2_PES);
369 pendingAudioPlay = false;
373 void PlayerVideoLive::switchState(UCHAR newState)
375 logger->log("PlayerVideoLive", Log::DEBUG, "Switch from state %u to state %u", state, newState);
379 case S_STOP: // FROM S_STOP
394 //audio->setStreamType(Audio::MPEG2_PES);
396 // I make this modification, since the video/audio devices needs to know at least
397 // which kind of video is embedded inside the stream
398 // therefore the demuxer needs to feeded at least with enough data
399 // to have one video header
400 // This is crucial, if we have mixed h264/mpeg2 channels
401 // the information from channels is not enough since some directshow decoders need
402 // width and height information before startup
403 pendingAudioPlay = true;
417 if (!video->independentAVStartUp())
420 playerThreadCond.notify_one();
426 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
433 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
439 pendingAudioPlay=false;
446 vdr->stopStreaming();
461 //audio->setStreamType(Audio::MPEG2_PES);
463 pendingAudioPlay = true;
475 if (!video->independentAVStartUp())
478 playerThreadCond.notify_one();
484 vdr->stopStreaming();
485 pendingAudioPlay=false;
501 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
508 case S_PREBUFFERING: // FROM S_PREBUFFERING
514 pendingAudioPlay=false;
522 vdr->stopStreaming();
539 //audio->setStreamType(Audio::MPEG2_PES);
541 pendingAudioPlay = true;
558 pendingAudioPlay=false;
559 vdr->stopStreaming();
575 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
582 case S_PLAY: // FROM S_PLAY
588 pendingAudioPlay=false;
589 vdr->stopStreaming();
605 vdr->stopStreaming();
623 //audio->setStreamType(Audio::MPEG2_PES);
627 pendingAudioPlay = true;
636 if (!video->independentAVStartUp())
639 playerThreadCond.notify_one();
645 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
654 bool PlayerVideoLive::checkError()
656 if (!vdr->isConnected())
658 if (state != S_STOP) switchState(S_STOP);
660 Message* m = new Message();
662 m->to = messageReceiver;
663 m->message = Message::PLAYER_EVENT;
664 m->parameter = PlayerVideoLive::CONNECTION_LOST;
665 messageQueue->postMessage(m);
672 void PlayerVideoLive::optimizeInstructionQueue()
676 // Currently there are only 2 instruction types, so this is a bit overkill...
678 struct PLInstruction i;
679 while(instructions.size() > 1)
681 i = instructions.front();
682 if (i.instruction == I_SETCHANNEL)
684 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
686 else if (i.instruction == I_STOP)
688 return; // return here and ensure the next instruction will be stop
693 void PlayerVideoLive::threadMethod()
695 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
699 //logger->log("PlayerVideoLive", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
700 if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
702 logger->log("PlayerVideoLive", Log::DEBUG, "Enter prebuffering");
703 switchState(S_PREBUFFERING);
704 videoStartup = false;
709 while (!instructions.empty())
711 if (instructions.size() > 1) optimizeInstructionQueue();
713 struct PLInstruction i = instructions.front();
716 if (i.instruction == I_SETCHANNEL)
718 logger->log("PlayerVideoLive", Log::DEBUG, "start new stream");
720 bool subsRestore = subtitles->isShowing();
722 switchState(S_VIDEOSTARTUP);
726 Channel* chan = (*chanList)[i.channelIndex];
728 h264 = (chan->vstreamtype == 0x1b);
729 demuxer->seth264(h264);
730 video->seth264mode(h264);
731 demuxer->setVID(chan->vpid);
732 video->seth264mode(h264);
738 Control* control = Control::getInstance();
740 if (chan->numDPids > 0 && audio->maysupportAc3())
743 while (j < chan->numDPids)
745 int newpref = control->getLangPref(false, chan->dpids[j].desc);
746 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type) && (prefered < 0 || newpref < prefered))
756 if (chan->numAPids > 0)
759 while (j < chan->numAPids)
761 int newpref = control->getLangPref(false, chan->apids[j].desc);
762 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type) && (prefered < 0 || newpref < prefered))
776 demuxer->setAID(chan->dpids[selected].pid, 1, chan->dpids[selected].type, true);
777 audio->setStreamType(Audio::MPEG2_PES);
778 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u",
779 chan->vpid, chan->dpids[selected].pid, chan->dpids[selected].type);
783 demuxer->setAID(chan->apids[selected].pid, 0, chan->apids[selected].type, true);
784 audio->setStreamType(Audio::MPEG2_PES);
785 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[selected].pid,
786 chan->apids[selected].type);
791 logger->log("PlayerVideoLive", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
799 while (j < chan->numSPids)
801 int newpref = control->getLangPref(true, chan->spids[j].desc);
802 if ((prefered < 0 || newpref < prefered))
813 demuxer->setSubID(chan->spids[selected].pid);
819 if (control->getSubDefault())
820 turnSubtitlesOn(true);
822 turnSubtitlesOn(false);
827 turnSubtitlesOn(true);
829 turnSubtitlesOn(false);
833 demuxer->setTID(chan->tpid);
834 teletext->ResetDecoder();
835 int streamSuccess = vdr->streamChannel(chan->number, this);
836 if (!checkError() && !streamSuccess)
838 Message* m = new Message();
840 m->to = messageReceiver;
841 m->message = Message::PLAYER_EVENT;
842 m->parameter = PlayerVideoLive::STREAM_END;
843 messageQueue->postMessage(m);
847 else if (i.instruction == I_STOP)
849 logger->log("PlayerVideoLive", Log::DEBUG, "Stopping by instruction");
856 while(streamChunks.size())
858 //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
860 //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
862 if (state == S_PREBUFFERING)
864 // logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark3");
866 ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
867 logger->log("PlayerVideoLive", Log::DEBUG, "Prebuffering %lu%%", percentDone);
869 Message* m = new Message();
871 m->to = messageReceiver;
872 m->message = Message::PLAYER_EVENT;
873 m->parameter = PlayerVideoLive::PREBUFFERING;
874 m->tag = percentDone;
875 messageQueue->postMessage(m);
877 if (preBufferCount == preBufferAmount)
884 //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal %d", streamChunks.size());
887 if (!instructions.empty()) { ul.unlock(); continue; }
888 playerThreadCond.wait(ul);
891 //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
894 logger->log("PlayerVideoLive", Log::DEBUG, "End of thread");