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/>.
24 #include "demuxerts.h"
26 #include "messagequeue.h"
30 #include "dvbsubtitles.h"
31 #include "osdreceiver.h"
34 #include "playervideolive.h"
36 // ----------------------------------- Called from outside, one offs or info funcs
38 PlayerVideoLive::PlayerVideoLive(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
39 : vfeed(this), afeed(this), tfeed(this),
40 messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), osdReceiver(tosdReceiver), chanList(tchanList)
42 audio = Audio::getInstance();
43 video = Video::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
50 PlayerVideoLive::~PlayerVideoLive()
52 if (initted) shutdown();
55 int PlayerVideoLive::init()
57 if (initted) return 0;
59 demuxer = new DemuxerTS();
60 if (!demuxer) return 0;
61 subtitles = new DVBSubtitles(osdReceiver);
62 if (!subtitles) return 0;
64 teletext = new TeletextDecoderVBIEBU();
66 unsigned int demux_video_size = 2097152;
67 unsigned int demux_audio_size = 524288;
69 if (video->supportsh264())
71 demux_video_size *= 5 * 1;//5;
74 if (audio->maysupportAc3())
76 //demux_audio_size*=2;
79 int text_fak = video->getTeletextBufferFaktor();
81 if (!demuxer->init(this, audio, video, teletext, demux_video_size, demux_audio_size, 65536 * text_fak, 25./*unimportant*/, subtitles))
83 logger->log("PlayerVideoLive", Log::ERR, "Demuxer failed to init");
96 int PlayerVideoLive::shutdown()
98 if (!initted) return 0;
99 logger->log("PlayerVideoLive", Log::DEBUG, "Shutdown");
102 logger->log("PlayerVideoLive", Log::DEBUG, "state is not stop, calling");
115 bool* PlayerVideoLive::getDemuxerMpegAudioChannels()
117 return demuxer->getmpAudioChannels();
120 bool* PlayerVideoLive::getDemuxerAc3AudioChannels()
122 return demuxer->getac3AudioChannels();
125 int PlayerVideoLive::getCurrentAudioChannel()
127 return demuxer->getAID();
130 void PlayerVideoLive::setAudioChannel(int newChannel, int type,int streamtype)
132 demuxer->setAID(newChannel, type, streamtype, true);
135 void PlayerVideoLive::setSubtitleChannel(int newChannel)
137 demuxer->setSubID(newChannel);
140 int* PlayerVideoLive::getTeletxtSubtitlePages()
142 return teletext->getSubtitlePages();
145 int PlayerVideoLive::getCurrentSubtitleChannel()
147 return demuxer->getSubID();
150 bool PlayerVideoLive::toggleSubtitles()
152 if (!subtitlesShowing)
154 subtitlesShowing = true;
159 subtitlesShowing = false;
162 return subtitlesShowing;
165 void PlayerVideoLive::turnSubtitlesOn(bool ison)
169 subtitlesShowing = true;
174 subtitlesShowing = false;
179 void PlayerVideoLive::tellSubtitlesOSDVisible(bool visible)
181 subtitles->setOSDMenuVisibility(visible);
184 // ----------------------------------- Externally called events
186 void PlayerVideoLive::go(ULONG index)
188 playerThreadMutex.lock();
190 struct PLInstruction i;
191 i.instruction = I_SETCHANNEL;
192 i.channelIndex = index;
193 instructions.push(i);
195 playerThread = std::thread([this]
197 playerThreadMutex.lock();
198 playerThreadMutex.unlock();
201 playerThreadMutex.unlock();
204 void PlayerVideoLive::setChannel(ULONG index)
206 logger->log("PlayerVideoLive", Log::DEBUG, "setChannel");
207 struct PLInstruction i;
208 i.instruction = I_SETCHANNEL;
209 i.channelIndex = index;
210 instructions.push(i);
211 playerThreadCond.notify_one();
214 void PlayerVideoLive::stop()
216 logger->log("PlayerVideoLive", Log::DEBUG, "stop");
218 playerThreadMutex.lock();
220 struct PLInstruction i;
221 i.instruction = I_STOP;
222 instructions.push(i);
224 playerThreadCond.notify_one();
225 playerThreadMutex.unlock();
228 logger->log("PlayerVideoLive", Log::DEBUG, "stop succesfull");
231 // ----------------------------------- Callback
233 void PlayerVideoLive::call(void* caller)
235 if (caller == demuxer)
237 logger->log("PlayerVideoLive", Log::DEBUG, "Callback from demuxer");
240 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx, &pary);
241 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
243 if (video->getTVsize() == Video::ASPECT16X9)
245 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
246 video->setAspectRatio(Video::ASPECT4X3, parx, pary);
250 logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
253 Message* m = new Message();
255 m->to = messageReceiver;
256 m->message = Message::PLAYER_EVENT;
257 m->parameter = PlayerVideoLive::ASPECT43;
258 messageQueue->postMessage(m);
260 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
262 if (video->getTVsize() == Video::ASPECT16X9)
264 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
265 video->setAspectRatio(Video::ASPECT16X9, parx, pary);
269 logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
272 Message* m = new Message();
274 m->to = messageReceiver;
275 m->message = Message::PLAYER_EVENT;
276 m->parameter = PlayerVideoLive::ASPECT169;
277 messageQueue->postMessage(m);
281 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is something else... switch anyway");
282 video->setAspectRatio(dxCurrentAspect, parx, pary);
285 else if (caller == &afeed)
287 if (state == S_VIDEOSTARTUP)
289 logger->log("PlayerVideoLive", Log::DEBUG, "afeed video startup");
291 playerThreadCond.notify_one();
296 // -----------------------------------
298 void PlayerVideoLive::streamReceive(ULONG flag, void* data, ULONG len)
301 // 0 = normal stream packet
303 // 2 = connection lost
305 //logger->log("PlayerVideoLive", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
311 Message* m = new Message();
313 m->to = messageReceiver;
314 m->message = Message::PLAYER_EVENT;
315 m->parameter = PlayerVideoLive::STREAM_END;
316 messageQueue->postMessage(m);
319 if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)
324 streamChunks.push(s);
325 playerThreadCond.notify_one();
329 // Too many chunks in streamChunks, drop this chunk
331 logger->log("PlayerVideoLive", Log::WARN, "Dropped chunk");
335 void PlayerVideoLive::clearStreamChunks()
337 while(streamChunks.size())
339 logger->log("PlayerVideoLive", Log::DEBUG, "Dropping chunk from old stream");
340 struct StreamChunk s = streamChunks.front();
346 void PlayerVideoLive::chunkToDemuxer()
348 StreamChunk s = streamChunks.front();
350 // logger->log("PlayerVideoLive", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
351 /* int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
352 // logger->log("PlayerVideoLive", Log::DEBUG, "put %i to demuxer", a);
354 if (pendingAudioPlay && (demuxer->getHorizontalSize() || !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
359 //audio->setStreamType(Audio::MPEG2_PES);
363 pendingAudioPlay = false;
367 void PlayerVideoLive::switchState(UCHAR newState)
369 logger->log("PlayerVideoLive", Log::DEBUG, "Switch from state %u to state %u", state, newState);
373 case S_STOP: // FROM S_STOP
388 //audio->setStreamType(Audio::MPEG2_PES);
390 // I make this modification, since the video/audio devices needs to know at least
391 // which kind of video is embedded inside the stream
392 // therefore the demuxer needs to feeded at least with enough data
393 // to have one video header
394 // This is crucial, if we have mixed h264/mpeg2 channels
395 // the information from channels is not enough since some directshow decoders need
396 // width and height information before startup
397 pendingAudioPlay = true;
411 if (!video->independentAVStartUp())
414 playerThreadCond.notify_one();
420 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
427 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
433 pendingAudioPlay=false;
440 vdr->stopStreaming();
455 //audio->setStreamType(Audio::MPEG2_PES);
457 pendingAudioPlay = true;
469 if (!video->independentAVStartUp())
472 playerThreadCond.notify_one();
478 vdr->stopStreaming();
479 pendingAudioPlay=false;
495 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
502 case S_PREBUFFERING: // FROM S_PREBUFFERING
508 pendingAudioPlay=false;
516 vdr->stopStreaming();
533 //audio->setStreamType(Audio::MPEG2_PES);
535 pendingAudioPlay = true;
552 pendingAudioPlay=false;
553 vdr->stopStreaming();
569 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
576 case S_PLAY: // FROM S_PLAY
582 pendingAudioPlay=false;
583 vdr->stopStreaming();
599 vdr->stopStreaming();
617 //audio->setStreamType(Audio::MPEG2_PES);
621 pendingAudioPlay = true;
630 if (!video->independentAVStartUp())
633 playerThreadCond.notify_one();
639 logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
648 bool PlayerVideoLive::checkError()
650 if (!vdr->isConnected())
652 if (state != S_STOP) switchState(S_STOP);
654 Message* m = new Message();
656 m->to = messageReceiver;
657 m->message = Message::PLAYER_EVENT;
658 m->parameter = PlayerVideoLive::CONNECTION_LOST;
659 messageQueue->postMessage(m);
666 void PlayerVideoLive::optimizeInstructionQueue()
670 // Currently there are only 2 instruction types, so this is a bit overkill...
672 struct PLInstruction i;
673 while(instructions.size() > 1)
675 i = instructions.front();
676 if (i.instruction == I_SETCHANNEL)
678 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
680 else if (i.instruction == I_STOP)
682 return; // return here and ensure the next instruction will be stop
687 void PlayerVideoLive::threadMethod()
689 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
693 //logger->log("PlayerVideoLive", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
694 if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
696 logger->log("PlayerVideoLive", Log::DEBUG, "Enter prebuffering");
697 switchState(S_PREBUFFERING);
698 videoStartup = false;
703 while (!instructions.empty())
705 if (instructions.size() > 1) optimizeInstructionQueue();
707 struct PLInstruction i = instructions.front();
710 if (i.instruction == I_SETCHANNEL)
712 logger->log("PlayerVideoLive", Log::DEBUG, "start new stream");
714 bool subsRestore = subtitles->isShowing();
716 switchState(S_VIDEOSTARTUP);
720 Channel* chan = (*chanList)[i.channelIndex];
722 h264 = (chan->vstreamtype == 0x1b);
723 demuxer->seth264(h264);
724 video->seth264mode(h264);
725 demuxer->setVID(chan->vpid);
726 video->seth264mode(h264);
732 Command* command = Command::getInstance();
734 if (chan->numDPids > 0 && audio->maysupportAc3())
737 while (j < chan->numDPids)
739 int newpref = command->getLangPref(false, chan->dpids[j].desc);
740 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type) && (prefered < 0 || newpref < prefered))
750 if (chan->numAPids > 0)
753 while (j < chan->numAPids)
755 int newpref = command->getLangPref(false, chan->apids[j].desc);
756 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type) && (prefered < 0 || newpref < prefered))
770 demuxer->setAID(chan->dpids[selected].pid, 1, chan->dpids[selected].type, true);
771 audio->setStreamType(Audio::MPEG2_PES);
772 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u",
773 chan->vpid, chan->dpids[selected].pid, chan->dpids[selected].type);
777 demuxer->setAID(chan->apids[selected].pid, 0, chan->apids[selected].type, true);
778 audio->setStreamType(Audio::MPEG2_PES);
779 logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[selected].pid,
780 chan->apids[selected].type);
785 logger->log("PlayerVideoLive", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
793 while (j < chan->numSPids)
795 int newpref = command->getLangPref(true, chan->spids[j].desc);
796 if ((prefered < 0 || newpref < prefered))
807 demuxer->setSubID(chan->spids[selected].pid);
813 if (command->getSubDefault())
814 turnSubtitlesOn(true);
816 turnSubtitlesOn(false);
821 turnSubtitlesOn(true);
823 turnSubtitlesOn(false);
827 demuxer->setTID(chan->tpid);
828 teletext->ResetDecoder();
829 int streamSuccess = vdr->streamChannel(chan->number, this);
830 if (!checkError() && !streamSuccess)
832 Message* m = new Message();
834 m->to = messageReceiver;
835 m->message = Message::PLAYER_EVENT;
836 m->parameter = PlayerVideoLive::STREAM_END;
837 messageQueue->postMessage(m);
841 else if (i.instruction == I_STOP)
843 logger->log("PlayerVideoLive", Log::DEBUG, "Stopping by instruction");
850 while(streamChunks.size())
852 //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
854 //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
856 if (state == S_PREBUFFERING)
858 // logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark3");
860 ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
861 logger->log("PlayerVideoLive", Log::DEBUG, "Prebuffering %lu%%", percentDone);
863 Message* m = new Message();
865 m->to = messageReceiver;
866 m->message = Message::PLAYER_EVENT;
867 m->parameter = PlayerVideoLive::PREBUFFERING;
868 m->tag = percentDone;
869 messageQueue->postMessage(m);
871 if (preBufferCount == preBufferAmount)
878 //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal %d", streamChunks.size());
881 if (!instructions.empty()) { ul.unlock(); continue; }
882 playerThreadCond.wait(ul);
885 //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
888 logger->log("PlayerVideoLive", Log::DEBUG, "End of thread");