OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.o \
message.o messagequeue.o wol.o audio.o video.o log.o \
vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \
- directory.o mark.o option.o playervideorec.o playerradio.o vfeed.o afeed.o \
+ directory.o mark.o option.o playerradio.o vfeed.o afeed.o \
demuxer.o demuxervdr.o demuxerts.o stream.o osd.o surface.o \
region.o colour.o boxstack.o boxx.o tbboxx.o vrecording.o \
vinfo.o vquestion.o vrecordinglist.o vrecordinglistclassic.o \
woptionpane.o woptionbox.o wremoteconfig.o wtabbar.o led.o \
inputman.o input.o inputudp.o vpicturebanner.o abstractoption.o \
eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \
- vvideolivetv.o vsleeptimer.o playerlivetv.o playerliveradio.o \
+ vvideolivetv.o vsleeptimer.o playerliveradio.o \
wprogressbar.o bitmap.o dvbsubtitles.o tfeed.o vteletextview.o \
teletextdecodervbiebu.o teletxt/txtfont.o movieinfo.o seriesinfo.o \
wmovieview.o wseriesview.o tvmedia.o wtvmedia.o wpictureview.o \
- osdvector.o surfacevector.o buffer.o
+ osdvector.o surfacevector.o buffer.o \
+ playervideorec.o playervideolive.o
OBJ_RASPBERRY = main.o threadp.o osdopenvg.o \
ledraspberry.o videoomx.o audioomx.o imageomx.o \
+++ /dev/null
-/*
- Copyright 2007 Chris Tallon
-
- This file is part of VOMP.
-
- VOMP is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- VOMP is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with VOMP. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-#include "playerlivetv.h"
-
-#include "defines.h"
-#include "log.h"
-#include "audio.h"
-#include "video.h"
-#include "demuxerts.h"
-#include "vdr.h"
-#include "messagequeue.h"
-#include "input.h"
-#include "message.h"
-#include "channel.h"
-#include "dvbsubtitles.h"
-#include "osdreceiver.h"
-#include "command.h"
-
-// ----------------------------------- Called from outside, one offs or info funcs
-
-PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
-: vfeed(this), afeed(this), tfeed(this)
-{
- messageQueue = tmessageQueue;
- messageReceiver = tmessageReceiver;
- osdReceiver = tosdReceiver;
- chanList = tchanList;
-
- audio = Audio::getInstance();
- video = Video::getInstance();
- logger = Log::getInstance();
- vdr = VDR::getInstance();
- initted = false;
-
- subtitlesShowing = false;
- videoStartup = false;
- pendingAudioPlay = false;
-
- stopNow = false;
- state = S_STOP;
-
- video->turnVideoOn();
-}
-
-PlayerLiveTV::~PlayerLiveTV()
-{
- if (initted) shutdown();
-}
-
-int PlayerLiveTV::init()
-{
- if (initted) return 0;
-
- demuxer = new DemuxerTS();
- if (!demuxer) return 0;
- subtitles = new DVBSubtitles(osdReceiver);
- if (!subtitles) return 0;
-
- teletext = new TeletextDecoderVBIEBU();
-
- unsigned int demux_video_size = 2097152;
- unsigned int demux_audio_size = 524288;
-
- if (video->supportsh264())
- {
- demux_video_size *= 5 * 1;//5;
- }
-
- if (audio->maysupportAc3())
- {
- //demux_audio_size*=2;
- }
-
- int text_fak = video->getTeletextBufferFaktor();
-
- if (!demuxer->init(this, audio, video, teletext, demux_video_size, demux_audio_size, 65536 * text_fak, 25./*unimportant*/, subtitles))
- {
- logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
- shutdown();
- return 0;
- }
-
-
- video->stop();
- video->blank();
- audio->stop();
-
- initted = true;
- return 1;
-}
-
-int PlayerLiveTV::shutdown()
-{
- if (!initted) return 0;
- logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");
- stop();
- initted = false;
-
- delete demuxer;
- delete subtitles;
- delete teletext;
- teletext = NULL;
- return 1;
-}
-
-bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
-{
- return demuxer->getmpAudioChannels();
-}
-
-bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
-{
- return demuxer->getac3AudioChannels();
-}
-
-int PlayerLiveTV::getCurrentAudioChannel()
-{
- return demuxer->getAID();
-}
-
-void PlayerLiveTV::setAudioChannel(int newChannel, int type,int streamtype)
-{
- demuxer->setAID(newChannel, type, streamtype, true);
-}
-
-void PlayerLiveTV::setSubtitleChannel(int newChannel)
-{
- demuxer->setSubID(newChannel);
-}
-
-int* PlayerLiveTV::getTeletxtSubtitlePages()
-{
- return teletext->getSubtitlePages();
-}
-
-int PlayerLiveTV::getCurrentSubtitleChannel()
-{
- return demuxer->getSubID();
-}
-
-bool PlayerLiveTV::toggleSubtitles()
-{
- if (!subtitlesShowing)
- {
- subtitlesShowing = true;
- subtitles->show();
- }
- else
- {
- subtitlesShowing = false;
- subtitles->hide();
- }
- return subtitlesShowing;
-}
-
-void PlayerLiveTV::turnSubtitlesOn(bool ison)
-{
- if (ison)
- {
- subtitlesShowing = true;
- subtitles->show();
- }
- else
- {
- subtitlesShowing = false;
- subtitles->hide();
- }
-}
-
-void PlayerLiveTV::tellSubtitlesOSDVisible(bool visible)
-{
- subtitles->setOSDMenuVisibility(visible);
-}
-
-// ----------------------------------- Externally called events
-
-void PlayerLiveTV::go(ULONG index)
-{
- struct PLInstruction i;
- i.instruction = I_SETCHANNEL;
- i.channelIndex = index;
- instructions.push(i);
- threadStart();
-}
-
-void PlayerLiveTV::setChannel(ULONG index)
-{
- logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
- struct PLInstruction i;
- i.instruction = I_SETCHANNEL;
- i.channelIndex = index;
- instructions.push(i);
- threadSignalNoLock();
-}
-
-void PlayerLiveTV::stop()
-{
- logger->log("PlayerLiveTV", Log::DEBUG, "stop");
- struct PLInstruction i;
- i.instruction = I_STOP;
- instructions.push(i);
- threadSignal();
- threadStop();
- logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");
-}
-
-// ----------------------------------- Callback
-
-void PlayerLiveTV::call(void* caller)
-{
- if (caller == demuxer)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
-
- int parx,pary;
- UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx, &pary);
- if (dxCurrentAspect == Demuxer::ASPECT_4_3)
- {
- if (video->getTVsize() == Video::ASPECT16X9)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
- video->setAspectRatio(Video::ASPECT4X3, parx, pary);
- }
- else
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
- }
-
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::ASPECT43;
- messageQueue->postMessage(m);
- }
- else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
- {
- if (video->getTVsize() == Video::ASPECT16X9)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
- video->setAspectRatio(Video::ASPECT16X9, parx, pary);
- }
- else
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
- }
-
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::ASPECT169;
- messageQueue->postMessage(m);
- }
- else
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... switch anyway");
- video->setAspectRatio(dxCurrentAspect, parx, pary);
- }
- }
- else if (caller == &afeed)
- {
- if (state == S_VIDEOSTARTUP)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
- videoStartup = true;
- threadSignalNoLock();
- }
- }
-}
-
-// -----------------------------------
-
-void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
-{
- // Flag:
- // 0 = normal stream packet
- // 1 = stream end
- // 2 = connection lost
-
- //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
-
- if (flag == 1)
- {
- if (data) abort();
-
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::STREAM_END;
- messageQueue->postMessage(m);
- }
-
- if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)
- {
- StreamChunk s;
- s.data = data;
- s.len = len;
- streamChunks.push(s);
- threadSignalNoLock();
- }
- else
- {
- // Too many chunks in streamChunks, drop this chunk
- free(data);
- logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
- }
-}
-
-void PlayerLiveTV::clearStreamChunks()
-{
- while(streamChunks.size())
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
- struct StreamChunk s = streamChunks.front();
- streamChunks.pop();
- free(s.data);
- }
-}
-
-void PlayerLiveTV::chunkToDemuxer()
-{
- StreamChunk s = streamChunks.front();
- streamChunks.pop();
-// logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
- /* int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
-// logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
- free(s.data);
- if (pendingAudioPlay && (demuxer->getHorizontalSize() || !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
- {
- video->sync();
- video->play();
- video->pause();
- //audio->setStreamType(Audio::MPEG2_PES);
- audio->sync();
- audio->play();
- audio->pause();
- pendingAudioPlay = false;
- }
-}
-
-void PlayerLiveTV::switchState(UCHAR newState)
-{
- logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
-
- switch(state)
- {
- case S_STOP: // FROM S_STOP
- {
- switch(newState)
- {
- case S_VIDEOSTARTUP:
- {
- video->blank();
- video->reset();
- //video->sync();
- //video->play();
- //video->pause();
-
- audio->stop();
- audio->unPause();
- audio->reset();
- //audio->setStreamType(Audio::MPEG2_PES);
- //audio->sync();
- // I make this modification, since the video/audio devices needs to know at least
- // which kind of video is embedded inside the stream
- // therefore the demuxer needs to feeded at least with enough data
- // to have one video header
- // This is crucial, if we have mixed h264/mpeg2 channels
- // the information from channels is not enough since some directshow decoders need
- // width and height information before startup
- pendingAudioPlay = true;
-
- //audio->play();
- //audio->pause();
-
- demuxer->reset();
- demuxer->seek();
-
- afeed.start();
- vfeed.start();
- subtitles->start();
- tfeed.start();
-
- state = newState;
- if (!video->independentAVStartUp())
- {
- videoStartup = true;
- threadSignalNoLock();
- }
- return;
- }
- default:
- {
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
- abort();
- break;
- }
- }
- }
-
- case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
- {
- switch(newState)
- {
- case S_PREBUFFERING:
- {
- pendingAudioPlay=false;
- state = newState;
- return;
- }
-
- case S_VIDEOSTARTUP:
- {
- vdr->stopStreaming();
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
-
- video->blank();
- video->reset();
- //video->sync();
- //video->play();
- //video->pause();
- audio->stop();
- audio->unPause();
- audio->reset();
- //audio->setStreamType(Audio::MPEG2_PES);
- //audio->sync();
- pendingAudioPlay = true;
- //audio->play();
- //audio->pause();
-
- demuxer->reset();
- demuxer->seek();
-
- afeed.start();
- vfeed.start();
- subtitles->start();
- tfeed.start();
- state = newState;
- if (!video->independentAVStartUp())
- {
- videoStartup = true;
- threadSignalNoLock();
- }
- return;
- }
- case S_STOP:
- {
- vdr->stopStreaming();
- pendingAudioPlay=false;
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
- video->stop();
- video->blank();
- audio->stop();
- audio->reset();
- video->reset();
- state = newState;
- return;
- }
- default:
- {
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
- abort();
- break;
- }
- }
- }
-
- case S_PREBUFFERING: // FROM S_PREBUFFERING
- {
- switch(newState)
- {
- case S_PLAY:
- {
- pendingAudioPlay=false;
- audio->unPause();
- video->unPause();
- state = newState;
- return;
- }
- case S_VIDEOSTARTUP:
- {
- vdr->stopStreaming();
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
- video->stop();
- video->blank();
- audio->stop();
- audio->unPause();
- audio->reset();
-
- video->reset();
- //video->sync();
- //video->play();
- //video->pause();
-
- //audio->setStreamType(Audio::MPEG2_PES);
- //audio->sync();
- pendingAudioPlay = true;
- //audio->play();
- //audio->pause();
-
- demuxer->reset();
- demuxer->seek();
-
- afeed.start();
- vfeed.start();
- subtitles->start();
- tfeed.start();
-
- state = newState;
- return;
- }
- case S_STOP:
- {
- pendingAudioPlay=false;
- vdr->stopStreaming();
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
- video->stop();
- video->blank();
- audio->stop();
- audio->reset();
- video->reset();
- state = newState;
- return;
- }
- default:
- {
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
- abort();
- break;
- }
- }
- }
-
- case S_PLAY: // FROM S_PLAY
- {
- switch(newState)
- {
- case S_STOP:
- {
- pendingAudioPlay=false;
- vdr->stopStreaming();
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
- video->stop();
- video->blank();
- audio->stop();
- audio->reset();
- video->reset();
- state = newState;
- return;
- }
- case S_VIDEOSTARTUP:
- {
- vdr->stopStreaming();
- clearStreamChunks();
- vfeed.stop();
- afeed.stop();
- subtitles->stop();
- tfeed.stop();
- video->stop();
- video->blank();
- audio->stop();
- audio->unPause();
- audio->reset();
-
- video->reset();
-
- //video->sync();
- // video->play();
- //video->pause();
-
- //audio->setStreamType(Audio::MPEG2_PES);
- //audio->sync();
- //audio->play();
- //audio->pause();
- pendingAudioPlay = true;
- demuxer->reset();
- demuxer->seek();
-
- afeed.start();
- vfeed.start();
- subtitles->start();
- tfeed.start();
- state = newState;
- if (!video->independentAVStartUp())
- {
- videoStartup = true;
- threadSignalNoLock();
- }
- return;
- }
- default:
- {
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
- abort();
- break;
- }
- }
- }
- }
-}
-
-bool PlayerLiveTV::checkError()
-{
- if (!vdr->isConnected())
- {
- if (state != S_STOP) switchState(S_STOP);
-
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::CONNECTION_LOST;
- messageQueue->postMessage(m);
-
- return true;
- }
- return false;
-}
-
-void PlayerLiveTV::optimizeInstructionQueue()
-{
- // Walk the list
-
- // Currently there are only 2 instruction types, so this is a bit overkill...
-
- struct PLInstruction i;
- while(instructions.size() > 1)
- {
- i = instructions.front();
- if (i.instruction == I_SETCHANNEL)
- {
- instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
- }
- else if (i.instruction == I_STOP)
- {
- return; // return here and ensure the next instruction will be stop
- }
- }
-}
-
-void PlayerLiveTV::threadMethod()
-{
- while(1)
- {
- //logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
- if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");
- switchState(S_PREBUFFERING);
- videoStartup = false;
- preBufferCount = 0;
- checkError();
- }
-
- while(!instructions.empty())
- {
- if (instructions.size() > 1) optimizeInstructionQueue();
-
- struct PLInstruction i = instructions.front();
- instructions.pop();
-
- if (i.instruction == I_SETCHANNEL)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
-
- bool subsRestore = subtitles->isShowing();
-
- switchState(S_VIDEOSTARTUP);
-
- if (!checkError())
- {
- Channel* chan = (*chanList)[i.channelIndex];
- chan->loadPids();
- h264=chan->vstreamtype==0x1b;
- demuxer->seth264(h264);
- video->seth264mode(chan->vstreamtype==0x1b);
- demuxer->setVID(chan->vpid);
- video->seth264mode(chan->vstreamtype==0x1b);
-
- bool dolby=false;
- int selected=-1;
- int prefered=-1;
- Command* command = Command::getInstance();
-
- if (chan->numDPids > 0 && audio->maysupportAc3())
- {
- ULONG j = 0;
- while (j < chan->numDPids)
- {
- int newpref = command->getLangPref(false, chan->dpids[j].desc);
- if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)
- && (prefered < 0 || newpref < prefered))
- {
- selected = j;
- dolby=true;
- prefered = newpref;
- }
- j++;
- }
- }
-
-
-
- if (chan->numAPids > 0)
- {
- ULONG j = 0;
- while (j < chan->numAPids)
- {
- int newpref = command->getLangPref(false, chan->apids[j].desc);
- if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)
- && (prefered < 0 || newpref < prefered))
- {
- selected = j;
- dolby=false;
- prefered = newpref;
- }
- j++;
- }
- }
-
-
- if (selected >= 0) {
- if (dolby) {
- demuxer->setAID(chan->dpids[selected].pid, 1, chan->dpids[selected].type, true);
- audio->setStreamType(Audio::MPEG2_PES);
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u",
- chan->vpid, chan->dpids[selected].pid, chan->dpids[selected].type);
- } else {
- demuxer->setAID(chan->apids[selected].pid, 0, chan->apids[selected].type, true);
- audio->setStreamType(Audio::MPEG2_PES);
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[selected].pid,
- chan->apids[selected].type);
- }
-
- } else {
- logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
- }
-
-
- selected = -1;
- prefered=-1;
- if (chan->numSPids) {
- ULONG j = 0;
- while (j < chan->numSPids)
- {
- int newpref = command->getLangPref(true, chan->spids[j].desc);
- if ( (prefered < 0 || newpref < prefered))
- {
- selected = j;
- prefered = newpref;
- }
- j++;
- }
- }
-
- if (selected >= 0)
- {
- demuxer->setSubID(chan->spids[selected].pid);
-
- if (firstStart)
- {
- firstStart = false;
-
- if (command->getSubDefault())
- turnSubtitlesOn(true);
- else
- turnSubtitlesOn(false);
- }
- else
- {
- if (subsRestore)
- turnSubtitlesOn(true);
- else
- turnSubtitlesOn(false);
- }
- }
-
- demuxer->setTID(chan->tpid);
- teletext->ResetDecoder();
- int streamSuccess = vdr->streamChannel(chan->number, this);
- if (!checkError() && !streamSuccess)
- {
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::STREAM_END;
- messageQueue->postMessage(m);
- }
- }
- }
- else if (i.instruction == I_STOP)
- {
- logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
- switchState(S_STOP);
- checkError();
-
- stopNow = true;
- break;
- }
- }
-
- threadCheckExit();
-
- if (stopNow) break;
-
- while(streamChunks.size())
- {
- //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
- chunkToDemuxer();
- //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
-
- if (state == S_PREBUFFERING)
- {
- // logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark3");
- ++preBufferCount;
- ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
- logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
-
- Message* m = new Message();
- m->from = this;
- m->to = messageReceiver;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerLiveTV::PREBUFFERING;
- m->tag = percentDone;
- messageQueue->postMessage(m);
-
- if (preBufferCount == preBufferAmount)
- {
- switchState(S_PLAY);
- checkError();
- }
- }
- }
- //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal %d", streamChunks.size());
- threadLock();
- threadWaitForSignal(); // unlocks and waits for signal
- threadUnlock();
- //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
- }
-
- logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
-}
+++ /dev/null
-/*
- Copyright 2004-2005 Chris Tallon
-
- This file is part of VOMP.
-
- VOMP is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- VOMP is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with VOMP. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-#ifndef PLAYERLIVETV_H
-#define PLAYERLIVETV_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef WIN32
-#include <sys/time.h>
-#endif
-#include <time.h>
-
-#include <queue>
-
-#include "playerlive.h"
-
-#ifdef WIN32
-#include "threadwin.h"
-#else
-#include "threadp.h"
-#endif
-
-#include "callback.h"
-#include "defines.h"
-#include "vfeed.h"
-#include "afeed.h"
-#include "tfeed.h"
-#include "vdr.h"
-
-#include "teletextdecodervbiebu.h"
-
-class MessageQueue;
-class Audio;
-class Video;
-class Log;
-class DemuxerTS;
-class OSDReceiver;
-class DVBSubtitles;
-
-class PlayerLiveTV : public PlayerLive, public Thread_TYPE, public Callback, public StreamReceiver
-{
- public:
- PlayerLiveTV(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* tosdReceiver, ChannelList* chanList);
- virtual ~PlayerLiveTV();
-
- virtual int init();
- virtual int shutdown();
-
- virtual void go(ULONG index);
- virtual void setChannel(ULONG index);
- virtual void stop();
- virtual void setAudioChannel(int newChannel,int type,int streamtype);
- virtual void setSubtitleChannel(int newChannel);
- virtual bool toggleSubtitles();
- virtual void turnSubtitlesOn(bool ison);
- virtual bool isSubtitlesOn() {return subtitlesShowing;};
- virtual void tellSubtitlesOSDVisible(bool visible);
-
- virtual bool* getDemuxerMpegAudioChannels();
- virtual bool* getDemuxerAc3AudioChannels();
- virtual int getCurrentAudioChannel();
- virtual int getCurrentSubtitleChannel();
- virtual int *getTeletxtSubtitlePages();
-
- TeletextDecoderVBIEBU * getTeletextDecoder(){return teletext;};
-
- void call(void*); // for callback interface
-
- virtual void streamReceive(ULONG, void*, ULONG); // stream receiver interface
-
- // Player events
-
- // FIXME so far this just duplicates the old system + the wss
-
- const static UCHAR CONNECTION_LOST = 1;
- const static UCHAR STOP_PLAYBACK = 2;
- const static UCHAR STREAM_END = 3;
- const static UCHAR ASPECT43 = 4;
- const static UCHAR ASPECT169 = 5;
- const static UCHAR PREBUFFERING = 6;
-
- protected:
- void threadMethod();
-
- private:
- bool subtitlesShowing;
- MessageQueue* messageQueue;
- void* messageReceiver;
- OSDReceiver* osdReceiver;
- Log* logger;
- Audio* audio;
- Video* video;
- DemuxerTS* demuxer;
- DVBSubtitles* subtitles;
- TeletextDecoderVBIEBU *teletext;
- VDR* vdr;
- VFeed vfeed;
- AFeed afeed;
- TFeed tfeed;
- ChannelList* chanList;
- bool firstStart{true};
-
- std::queue<PLInstruction> instructions;
- const static UCHAR I_SETCHANNEL = 1;
- const static UCHAR I_STOP = 2;
-
- std::queue<StreamChunk> streamChunks;
-
- bool initted;
-
- UCHAR state;
- const static UCHAR S_STOP = 1;
- const static UCHAR S_VIDEOSTARTUP = 2;
- const static UCHAR S_PREBUFFERING = 3;
- const static UCHAR S_PLAY = 4;
- void switchState(UCHAR newState);
- bool checkError();
-
- bool videoStartup;
- bool pendingAudioPlay;
- bool stopNow;
- bool h264;
- int preBufferCount;
- const static int preBufferAmount = 3;
-
- void clearStreamChunks();
- void chunkToDemuxer();
- void optimizeInstructionQueue();
-};
-
-#endif
-
--- /dev/null
+/*
+ Copyright 2007 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "defines.h"
+#include "log.h"
+#include "audio.h"
+#include "video.h"
+#include "demuxerts.h"
+#include "vdr.h"
+#include "messagequeue.h"
+#include "input.h"
+#include "message.h"
+#include "channel.h"
+#include "dvbsubtitles.h"
+#include "osdreceiver.h"
+#include "command.h"
+
+#include "playervideolive.h"
+
+// ----------------------------------- Called from outside, one offs or info funcs
+
+PlayerVideoLive::PlayerVideoLive(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
+: vfeed(this), afeed(this), tfeed(this)
+{
+ messageQueue = tmessageQueue;
+ messageReceiver = tmessageReceiver;
+ osdReceiver = tosdReceiver;
+ chanList = tchanList;
+
+ audio = Audio::getInstance();
+ video = Video::getInstance();
+ logger = Log::getInstance();
+ vdr = VDR::getInstance();
+ initted = false;
+
+ subtitlesShowing = false;
+ videoStartup = false;
+ pendingAudioPlay = false;
+
+ stopNow = false;
+ state = S_STOP;
+
+ video->turnVideoOn();
+}
+
+PlayerVideoLive::~PlayerVideoLive()
+{
+ if (initted) shutdown();
+}
+
+int PlayerVideoLive::init()
+{
+ if (initted) return 0;
+
+ demuxer = new DemuxerTS();
+ if (!demuxer) return 0;
+ subtitles = new DVBSubtitles(osdReceiver);
+ if (!subtitles) return 0;
+
+ teletext = new TeletextDecoderVBIEBU();
+
+ unsigned int demux_video_size = 2097152;
+ unsigned int demux_audio_size = 524288;
+
+ if (video->supportsh264())
+ {
+ demux_video_size *= 5 * 1;//5;
+ }
+
+ if (audio->maysupportAc3())
+ {
+ //demux_audio_size*=2;
+ }
+
+ int text_fak = video->getTeletextBufferFaktor();
+
+ if (!demuxer->init(this, audio, video, teletext, demux_video_size, demux_audio_size, 65536 * text_fak, 25./*unimportant*/, subtitles))
+ {
+ logger->log("PlayerVideoLive", Log::ERR, "Demuxer failed to init");
+ shutdown();
+ return 0;
+ }
+
+
+ video->stop();
+ video->blank();
+ audio->stop();
+
+ initted = true;
+ return 1;
+}
+
+int PlayerVideoLive::shutdown()
+{
+ if (!initted) return 0;
+ logger->log("PlayerVideoLive", Log::DEBUG, "Shutdown");
+ stop();
+ initted = false;
+
+ delete demuxer;
+ delete subtitles;
+ delete teletext;
+ teletext = NULL;
+ return 1;
+}
+
+bool* PlayerVideoLive::getDemuxerMpegAudioChannels()
+{
+ return demuxer->getmpAudioChannels();
+}
+
+bool* PlayerVideoLive::getDemuxerAc3AudioChannels()
+{
+ return demuxer->getac3AudioChannels();
+}
+
+int PlayerVideoLive::getCurrentAudioChannel()
+{
+ return demuxer->getAID();
+}
+
+void PlayerVideoLive::setAudioChannel(int newChannel, int type,int streamtype)
+{
+ demuxer->setAID(newChannel, type, streamtype, true);
+}
+
+void PlayerVideoLive::setSubtitleChannel(int newChannel)
+{
+ demuxer->setSubID(newChannel);
+}
+
+int* PlayerVideoLive::getTeletxtSubtitlePages()
+{
+ return teletext->getSubtitlePages();
+}
+
+int PlayerVideoLive::getCurrentSubtitleChannel()
+{
+ return demuxer->getSubID();
+}
+
+bool PlayerVideoLive::toggleSubtitles()
+{
+ if (!subtitlesShowing)
+ {
+ subtitlesShowing = true;
+ subtitles->show();
+ }
+ else
+ {
+ subtitlesShowing = false;
+ subtitles->hide();
+ }
+ return subtitlesShowing;
+}
+
+void PlayerVideoLive::turnSubtitlesOn(bool ison)
+{
+ if (ison)
+ {
+ subtitlesShowing = true;
+ subtitles->show();
+ }
+ else
+ {
+ subtitlesShowing = false;
+ subtitles->hide();
+ }
+}
+
+void PlayerVideoLive::tellSubtitlesOSDVisible(bool visible)
+{
+ subtitles->setOSDMenuVisibility(visible);
+}
+
+// ----------------------------------- Externally called events
+
+void PlayerVideoLive::go(ULONG index)
+{
+ struct PLInstruction i;
+ i.instruction = I_SETCHANNEL;
+ i.channelIndex = index;
+ instructions.push(i);
+ threadStart();
+}
+
+void PlayerVideoLive::setChannel(ULONG index)
+{
+ logger->log("PlayerVideoLive", Log::DEBUG, "setChannel");
+ struct PLInstruction i;
+ i.instruction = I_SETCHANNEL;
+ i.channelIndex = index;
+ instructions.push(i);
+ threadSignalNoLock();
+}
+
+void PlayerVideoLive::stop()
+{
+ logger->log("PlayerVideoLive", Log::DEBUG, "stop");
+ struct PLInstruction i;
+ i.instruction = I_STOP;
+ instructions.push(i);
+ threadSignal();
+ threadStop();
+ logger->log("PlayerVideoLive", Log::DEBUG, "stop succesfull");
+}
+
+// ----------------------------------- Callback
+
+void PlayerVideoLive::call(void* caller)
+{
+ if (caller == demuxer)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Callback from demuxer");
+
+ int parx,pary;
+ UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx, &pary);
+ if (dxCurrentAspect == Demuxer::ASPECT_4_3)
+ {
+ if (video->getTVsize() == Video::ASPECT16X9)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
+ video->setAspectRatio(Video::ASPECT4X3, parx, pary);
+ }
+ else
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
+ }
+
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::ASPECT43;
+ messageQueue->postMessage(m);
+ }
+ else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
+ {
+ if (video->getTVsize() == Video::ASPECT16X9)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
+ video->setAspectRatio(Video::ASPECT16X9, parx, pary);
+ }
+ else
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
+ }
+
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::ASPECT169;
+ messageQueue->postMessage(m);
+ }
+ else
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer said video is something else... switch anyway");
+ video->setAspectRatio(dxCurrentAspect, parx, pary);
+ }
+ }
+ else if (caller == &afeed)
+ {
+ if (state == S_VIDEOSTARTUP)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "afeed video startup");
+ videoStartup = true;
+ threadSignalNoLock();
+ }
+ }
+}
+
+// -----------------------------------
+
+void PlayerVideoLive::streamReceive(ULONG flag, void* data, ULONG len)
+{
+ // Flag:
+ // 0 = normal stream packet
+ // 1 = stream end
+ // 2 = connection lost
+
+ //logger->log("PlayerVideoLive", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
+
+ if (flag == 1)
+ {
+ if (data) abort();
+
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::STREAM_END;
+ messageQueue->postMessage(m);
+ }
+
+ if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)
+ {
+ StreamChunk s;
+ s.data = data;
+ s.len = len;
+ streamChunks.push(s);
+ threadSignalNoLock();
+ }
+ else
+ {
+ // Too many chunks in streamChunks, drop this chunk
+ free(data);
+ logger->log("PlayerVideoLive", Log::WARN, "Dropped chunk");
+ }
+}
+
+void PlayerVideoLive::clearStreamChunks()
+{
+ while(streamChunks.size())
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Dropping chunk from old stream");
+ struct StreamChunk s = streamChunks.front();
+ streamChunks.pop();
+ free(s.data);
+ }
+}
+
+void PlayerVideoLive::chunkToDemuxer()
+{
+ StreamChunk s = streamChunks.front();
+ streamChunks.pop();
+// logger->log("PlayerVideoLive", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
+ /* int a =*/ demuxer->put(static_cast<UCHAR*>(s.data), s.len);
+// logger->log("PlayerVideoLive", Log::DEBUG, "put %i to demuxer", a);
+ free(s.data);
+ if (pendingAudioPlay && (demuxer->getHorizontalSize() || !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
+ {
+ video->sync();
+ video->play();
+ video->pause();
+ //audio->setStreamType(Audio::MPEG2_PES);
+ audio->sync();
+ audio->play();
+ audio->pause();
+ pendingAudioPlay = false;
+ }
+}
+
+void PlayerVideoLive::switchState(UCHAR newState)
+{
+ logger->log("PlayerVideoLive", Log::DEBUG, "Switch from state %u to state %u", state, newState);
+
+ switch(state)
+ {
+ case S_STOP: // FROM S_STOP
+ {
+ switch(newState)
+ {
+ case S_VIDEOSTARTUP:
+ {
+ video->blank();
+ video->reset();
+ //video->sync();
+ //video->play();
+ //video->pause();
+
+ audio->stop();
+ audio->unPause();
+ audio->reset();
+ //audio->setStreamType(Audio::MPEG2_PES);
+ //audio->sync();
+ // I make this modification, since the video/audio devices needs to know at least
+ // which kind of video is embedded inside the stream
+ // therefore the demuxer needs to feeded at least with enough data
+ // to have one video header
+ // This is crucial, if we have mixed h264/mpeg2 channels
+ // the information from channels is not enough since some directshow decoders need
+ // width and height information before startup
+ pendingAudioPlay = true;
+
+ //audio->play();
+ //audio->pause();
+
+ demuxer->reset();
+ demuxer->seek();
+
+ afeed.start();
+ vfeed.start();
+ subtitles->start();
+ tfeed.start();
+
+ state = newState;
+ if (!video->independentAVStartUp())
+ {
+ videoStartup = true;
+ threadSignalNoLock();
+ }
+ return;
+ }
+ default:
+ {
+ logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
+ abort();
+ break;
+ }
+ }
+ }
+
+ case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
+ {
+ switch(newState)
+ {
+ case S_PREBUFFERING:
+ {
+ pendingAudioPlay=false;
+ state = newState;
+ return;
+ }
+
+ case S_VIDEOSTARTUP:
+ {
+ vdr->stopStreaming();
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+
+ video->blank();
+ video->reset();
+ //video->sync();
+ //video->play();
+ //video->pause();
+ audio->stop();
+ audio->unPause();
+ audio->reset();
+ //audio->setStreamType(Audio::MPEG2_PES);
+ //audio->sync();
+ pendingAudioPlay = true;
+ //audio->play();
+ //audio->pause();
+
+ demuxer->reset();
+ demuxer->seek();
+
+ afeed.start();
+ vfeed.start();
+ subtitles->start();
+ tfeed.start();
+ state = newState;
+ if (!video->independentAVStartUp())
+ {
+ videoStartup = true;
+ threadSignalNoLock();
+ }
+ return;
+ }
+ case S_STOP:
+ {
+ vdr->stopStreaming();
+ pendingAudioPlay=false;
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+ video->stop();
+ video->blank();
+ audio->stop();
+ audio->reset();
+ video->reset();
+ state = newState;
+ return;
+ }
+ default:
+ {
+ logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
+ abort();
+ break;
+ }
+ }
+ }
+
+ case S_PREBUFFERING: // FROM S_PREBUFFERING
+ {
+ switch(newState)
+ {
+ case S_PLAY:
+ {
+ pendingAudioPlay=false;
+ audio->unPause();
+ video->unPause();
+ state = newState;
+ return;
+ }
+ case S_VIDEOSTARTUP:
+ {
+ vdr->stopStreaming();
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+ video->stop();
+ video->blank();
+ audio->stop();
+ audio->unPause();
+ audio->reset();
+
+ video->reset();
+ //video->sync();
+ //video->play();
+ //video->pause();
+
+ //audio->setStreamType(Audio::MPEG2_PES);
+ //audio->sync();
+ pendingAudioPlay = true;
+ //audio->play();
+ //audio->pause();
+
+ demuxer->reset();
+ demuxer->seek();
+
+ afeed.start();
+ vfeed.start();
+ subtitles->start();
+ tfeed.start();
+
+ state = newState;
+ return;
+ }
+ case S_STOP:
+ {
+ pendingAudioPlay=false;
+ vdr->stopStreaming();
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+ video->stop();
+ video->blank();
+ audio->stop();
+ audio->reset();
+ video->reset();
+ state = newState;
+ return;
+ }
+ default:
+ {
+ logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
+ abort();
+ break;
+ }
+ }
+ }
+
+ case S_PLAY: // FROM S_PLAY
+ {
+ switch(newState)
+ {
+ case S_STOP:
+ {
+ pendingAudioPlay=false;
+ vdr->stopStreaming();
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+ video->stop();
+ video->blank();
+ audio->stop();
+ audio->reset();
+ video->reset();
+ state = newState;
+ return;
+ }
+ case S_VIDEOSTARTUP:
+ {
+ vdr->stopStreaming();
+ clearStreamChunks();
+ vfeed.stop();
+ afeed.stop();
+ subtitles->stop();
+ tfeed.stop();
+ video->stop();
+ video->blank();
+ audio->stop();
+ audio->unPause();
+ audio->reset();
+
+ video->reset();
+
+ //video->sync();
+ // video->play();
+ //video->pause();
+
+ //audio->setStreamType(Audio::MPEG2_PES);
+ //audio->sync();
+ //audio->play();
+ //audio->pause();
+ pendingAudioPlay = true;
+ demuxer->reset();
+ demuxer->seek();
+
+ afeed.start();
+ vfeed.start();
+ subtitles->start();
+ tfeed.start();
+ state = newState;
+ if (!video->independentAVStartUp())
+ {
+ videoStartup = true;
+ threadSignalNoLock();
+ }
+ return;
+ }
+ default:
+ {
+ logger->log("PlayerVideoLive", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
+ abort();
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool PlayerVideoLive::checkError()
+{
+ if (!vdr->isConnected())
+ {
+ if (state != S_STOP) switchState(S_STOP);
+
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::CONNECTION_LOST;
+ messageQueue->postMessage(m);
+
+ return true;
+ }
+ return false;
+}
+
+void PlayerVideoLive::optimizeInstructionQueue()
+{
+ // Walk the list
+
+ // Currently there are only 2 instruction types, so this is a bit overkill...
+
+ struct PLInstruction i;
+ while(instructions.size() > 1)
+ {
+ i = instructions.front();
+ if (i.instruction == I_SETCHANNEL)
+ {
+ instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
+ }
+ else if (i.instruction == I_STOP)
+ {
+ return; // return here and ensure the next instruction will be stop
+ }
+ }
+}
+
+void PlayerVideoLive::threadMethod()
+{
+ while(1)
+ {
+ //logger->log("PlayerVideoLive", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
+ if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Enter prebuffering");
+ switchState(S_PREBUFFERING);
+ videoStartup = false;
+ preBufferCount = 0;
+ checkError();
+ }
+
+ while(!instructions.empty())
+ {
+ if (instructions.size() > 1) optimizeInstructionQueue();
+
+ struct PLInstruction i = instructions.front();
+ instructions.pop();
+
+ if (i.instruction == I_SETCHANNEL)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "start new stream");
+
+ bool subsRestore = subtitles->isShowing();
+
+ switchState(S_VIDEOSTARTUP);
+
+ if (!checkError())
+ {
+ Channel* chan = (*chanList)[i.channelIndex];
+ chan->loadPids();
+ h264=chan->vstreamtype==0x1b;
+ demuxer->seth264(h264);
+ video->seth264mode(chan->vstreamtype==0x1b);
+ demuxer->setVID(chan->vpid);
+ video->seth264mode(chan->vstreamtype==0x1b);
+
+ bool dolby=false;
+ int selected=-1;
+ int prefered=-1;
+ Command* command = Command::getInstance();
+
+ if (chan->numDPids > 0 && audio->maysupportAc3())
+ {
+ ULONG j = 0;
+ while (j < chan->numDPids)
+ {
+ int newpref = command->getLangPref(false, chan->dpids[j].desc);
+ if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)
+ && (prefered < 0 || newpref < prefered))
+ {
+ selected = j;
+ dolby=true;
+ prefered = newpref;
+ }
+ j++;
+ }
+ }
+
+
+
+ if (chan->numAPids > 0)
+ {
+ ULONG j = 0;
+ while (j < chan->numAPids)
+ {
+ int newpref = command->getLangPref(false, chan->apids[j].desc);
+ if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)
+ && (prefered < 0 || newpref < prefered))
+ {
+ selected = j;
+ dolby=false;
+ prefered = newpref;
+ }
+ j++;
+ }
+ }
+
+
+ if (selected >= 0) {
+ if (dolby) {
+ demuxer->setAID(chan->dpids[selected].pid, 1, chan->dpids[selected].type, true);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u",
+ chan->vpid, chan->dpids[selected].pid, chan->dpids[selected].type);
+ } else {
+ demuxer->setAID(chan->apids[selected].pid, 0, chan->apids[selected].type, true);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerVideoLive", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[selected].pid,
+ chan->apids[selected].type);
+ }
+
+ } else {
+ logger->log("PlayerVideoLive", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
+ }
+
+
+ selected = -1;
+ prefered=-1;
+ if (chan->numSPids) {
+ ULONG j = 0;
+ while (j < chan->numSPids)
+ {
+ int newpref = command->getLangPref(true, chan->spids[j].desc);
+ if ( (prefered < 0 || newpref < prefered))
+ {
+ selected = j;
+ prefered = newpref;
+ }
+ j++;
+ }
+ }
+
+ if (selected >= 0)
+ {
+ demuxer->setSubID(chan->spids[selected].pid);
+
+ if (firstStart)
+ {
+ firstStart = false;
+
+ if (command->getSubDefault())
+ turnSubtitlesOn(true);
+ else
+ turnSubtitlesOn(false);
+ }
+ else
+ {
+ if (subsRestore)
+ turnSubtitlesOn(true);
+ else
+ turnSubtitlesOn(false);
+ }
+ }
+
+ demuxer->setTID(chan->tpid);
+ teletext->ResetDecoder();
+ int streamSuccess = vdr->streamChannel(chan->number, this);
+ if (!checkError() && !streamSuccess)
+ {
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::STREAM_END;
+ messageQueue->postMessage(m);
+ }
+ }
+ }
+ else if (i.instruction == I_STOP)
+ {
+ logger->log("PlayerVideoLive", Log::DEBUG, "Stopping");
+ switchState(S_STOP);
+ checkError();
+
+ stopNow = true;
+ break;
+ }
+ }
+
+ threadCheckExit();
+
+ if (stopNow) break;
+
+ while(streamChunks.size())
+ {
+ //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
+ chunkToDemuxer();
+ //logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
+
+ if (state == S_PREBUFFERING)
+ {
+ // logger->log("PlayerVideoLive", Log::DEBUG, "chunk mark3");
+ ++preBufferCount;
+ ULONG percentDone = (preBufferCount * 100) / preBufferAmount;
+ logger->log("PlayerVideoLive", Log::DEBUG, "Prebuffering %lu%%", percentDone);
+
+ Message* m = new Message();
+ m->from = this;
+ m->to = messageReceiver;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerVideoLive::PREBUFFERING;
+ m->tag = percentDone;
+ messageQueue->postMessage(m);
+
+ if (preBufferCount == preBufferAmount)
+ {
+ switchState(S_PLAY);
+ checkError();
+ }
+ }
+ }
+ //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal %d", streamChunks.size());
+ threadLock();
+ threadWaitForSignal(); // unlocks and waits for signal
+ threadUnlock();
+ //logger->log("PlayerVideoLive", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
+ }
+
+ logger->log("PlayerVideoLive", Log::DEBUG, "End of thread");
+}
--- /dev/null
+/*
+ Copyright 2004-2005 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLAYERVIDEOLIVE_H
+#define PLAYERVIDEOLIVE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include <queue>
+
+#include "playerlive.h"
+
+#ifdef WIN32
+#include "threadwin.h"
+#else
+#include "threadp.h"
+#endif
+
+#include "callback.h"
+#include "defines.h"
+#include "vfeed.h"
+#include "afeed.h"
+#include "tfeed.h"
+#include "vdr.h"
+
+#include "teletextdecodervbiebu.h"
+
+class MessageQueue;
+class Audio;
+class Video;
+class Log;
+class DemuxerTS;
+class OSDReceiver;
+class DVBSubtitles;
+
+class PlayerVideoLive : public PlayerLive, public Thread_TYPE, public Callback, public StreamReceiver
+{
+ public:
+ PlayerVideoLive(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* tosdReceiver, ChannelList* chanList);
+ virtual ~PlayerVideoLive();
+
+ virtual int init();
+ virtual int shutdown();
+
+ virtual void go(ULONG index);
+ virtual void setChannel(ULONG index);
+ virtual void stop();
+ virtual void setAudioChannel(int newChannel,int type,int streamtype);
+ virtual void setSubtitleChannel(int newChannel);
+ virtual bool toggleSubtitles();
+ virtual void turnSubtitlesOn(bool ison);
+ virtual bool isSubtitlesOn() {return subtitlesShowing;};
+ virtual void tellSubtitlesOSDVisible(bool visible);
+
+ virtual bool* getDemuxerMpegAudioChannels();
+ virtual bool* getDemuxerAc3AudioChannels();
+ virtual int getCurrentAudioChannel();
+ virtual int getCurrentSubtitleChannel();
+ virtual int *getTeletxtSubtitlePages();
+
+ TeletextDecoderVBIEBU * getTeletextDecoder(){return teletext;};
+
+ void call(void*); // for callback interface
+
+ virtual void streamReceive(ULONG, void*, ULONG); // stream receiver interface
+
+ // Player events
+
+ // FIXME so far this just duplicates the old system + the wss
+
+ const static UCHAR CONNECTION_LOST = 1;
+ const static UCHAR STOP_PLAYBACK = 2;
+ const static UCHAR STREAM_END = 3;
+ const static UCHAR ASPECT43 = 4;
+ const static UCHAR ASPECT169 = 5;
+ const static UCHAR PREBUFFERING = 6;
+
+ protected:
+ void threadMethod();
+
+ private:
+ bool subtitlesShowing;
+ MessageQueue* messageQueue;
+ void* messageReceiver;
+ OSDReceiver* osdReceiver;
+ Log* logger;
+ Audio* audio;
+ Video* video;
+ DemuxerTS* demuxer;
+ DVBSubtitles* subtitles;
+ TeletextDecoderVBIEBU *teletext;
+ VDR* vdr;
+ VFeed vfeed;
+ AFeed afeed;
+ TFeed tfeed;
+ ChannelList* chanList;
+ bool firstStart{true};
+
+ std::queue<PLInstruction> instructions;
+ const static UCHAR I_SETCHANNEL = 1;
+ const static UCHAR I_STOP = 2;
+
+ std::queue<StreamChunk> streamChunks;
+
+ bool initted;
+
+ UCHAR state;
+ const static UCHAR S_STOP = 1;
+ const static UCHAR S_VIDEOSTARTUP = 2;
+ const static UCHAR S_PREBUFFERING = 3;
+ const static UCHAR S_PLAY = 4;
+ void switchState(UCHAR newState);
+ bool checkError();
+
+ bool videoStartup;
+ bool pendingAudioPlay;
+ bool stopNow;
+ bool h264;
+ int preBufferCount;
+ const static int preBufferAmount = 3;
+
+ void clearStreamChunks();
+ void chunkToDemuxer();
+ void optimizeInstructionQueue();
+};
+
+#endif
+
#include "video.h"
#include "boxstack.h"
#include "input.h"
-#include "playerlivetv.h"
+#include "playervideolive.h"
#include "vteletextview.h"
-VTeletextView::VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerLiveTV* playerObj)
+VTeletextView::VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerVideoLive* playerObj)
{
ttdecoder = TTdecoder;
pv = playerview;
#include "teletextdecodervbiebu.h"
-class PlayerLiveTV;
+class PlayerVideoLive;
class VTeletextView : public Boxx
{
public:
- VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerLiveTV* palyerObj);
+ VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerVideoLive* palyerObj);
~VTeletextView();
void draw(bool completedraw, bool onlyfirstline);
void draw() { draw(true, false); }
int page;
bool subtitlemode{};
Boxx* pv;
- PlayerLiveTV* player;
+ PlayerVideoLive* player;
};
#endif
#include "video.h"
#include "audio.h"
#include "playerlive.h"
-#include "playerlivetv.h"
+#include "playervideolive.h"
#include "playerliveradio.h"
#include "channel.h"
#include "boxstack.h"
if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
{
streamType = VDR::VIDEO;
- player = new PlayerLiveTV(Command::getInstance(), this, this, chanList);
+ player = new PlayerVideoLive(Command::getInstance(), this, this, chanList);
}
else
{
}
case Input::RECORD:
if (streamType == VDR::VIDEO)
- (static_cast<PlayerLiveTV*>(player))->toggleSubtitles();
+ (static_cast<PlayerVideoLive*>(player))->toggleSubtitles();
return 2;
}
void VVideoLiveTV::doTeletext(bool subtitlemode)
{
if (streamType !=VDR::VIDEO) return;
- (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
+ (static_cast<PlayerVideoLive*>(player))->tellSubtitlesOSDVisible(true);
bool exists = true;
// Cancel keying
osdChanNum.setText(formatChanNum);
osdChanName.setText((*chanList)[osdChannelIndex]->name);
}
- (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
+ (static_cast<PlayerVideoLive*>(player))->tellSubtitlesOSDVisible(true);
if (osd.getVisible()) clearScreen();
// Draw the teletxt
- VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
+ VTeletextView *vtxv=((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView();
if (vtxv==NULL)
{
- vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this, (PlayerLiveTV*)player);
- ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv);
+ vtxv= new VTeletextView(((PlayerVideoLive*)player)->getTeletextDecoder(),this, (PlayerVideoLive*)player);
+ ((PlayerVideoLive*)player)->getTeletextDecoder()->registerTeletextView(vtxv);
exists=false;
}
osdChanNum.setText(formatChanNum);
osdChanName.setText((*chanList)[osdChannelIndex]->name);
}
- int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel();
+ int subtitleChannel=((PlayerVideoLive*)player)->getCurrentSubtitleChannel();
int subtitleType=0x10;
- if (!(static_cast<PlayerLiveTV*>(player))->isSubtitlesOn()) {
- if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() &&
- ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode()
+ if (!(static_cast<PlayerVideoLive*>(player))->isSubtitlesOn()) {
+ if (((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView() &&
+ ((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode()
) {
- subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage();
+ subtitleChannel=((PlayerVideoLive*)player)->getTeletextDecoder()->getPage();
subtitleType=0x11;
} else {
}
// Draw the selector
- vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(),
+ vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerVideoLive*)player)->getCurrentAudioChannel(),
subtitleType,subtitleChannel,NULL);
vas->setBackgroundColour(DrawStyle::OSDBACKGROUND);
void VVideoLiveTV::displayOSD(bool newNowNextData)
{
- PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
+ PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(true);
osd.setVisible(true);
if (newNowNextData)
draw();
boxstack->update(this);
- PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
+ PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
}
else boxstack->update(this, osd.getRegion());
Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3.");
- PlayerLiveTV* playerlivetv = dynamic_cast<PlayerLiveTV*>(player);
+ PlayerVideoLive* playerlivetv = dynamic_cast<PlayerVideoLive*>(player);
if (playerlivetv) playerlivetv->tellSubtitlesOSDVisible(false);
Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end.");
}
UINT newChannel = 0;
if (streamType == VDR::VIDEO)
{
- VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
+ VTeletextView *vtxt=((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView();
if (vtxt)
{
BoxStack::getInstance()->remove(vtxt);
if (streamType == VDR::VIDEO)
{
player->setSubtitleChannel((m->parameter & 0xFFFF));
- (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(true);
- VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
+ (static_cast<PlayerVideoLive*>(player))->turnSubtitlesOn(true);
+ VTeletextView *vtxt=((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView();
if (vtxt && vtxt->isInSubtitleMode())
{
BoxStack::getInstance()->remove(vtxt);
case 0xFF:
{ //nosubtitles
if (streamType == VDR::VIDEO){
- (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
- VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
+ (static_cast<PlayerVideoLive*>(player))->turnSubtitlesOn(false);
+ VTeletextView *vtxt=((PlayerVideoLive*)player)->getTeletextDecoder()->getTeletxtView();
if (vtxt && vtxt->isInSubtitleMode()) {
BoxStack::getInstance()->remove(vtxt);
}
break;
case 0x11:
{ //videotext
- (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
+ (static_cast<PlayerVideoLive*>(player))->turnSubtitlesOn(false);
doTeletext(true);
- ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
+ ((PlayerVideoLive*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
} break;
};
{
switch (m->parameter)
{
- case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
+ case PlayerVideoLive::CONNECTION_LOST: // connection lost detected
{
Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
Command::getInstance()->connectionLost();
break;
}
- case PlayerLiveTV::STREAM_END:
+ case PlayerVideoLive::STREAM_END:
{
// Message comes from playerlivetv through master mutex, so can do anything here
showUnavailable();
break;
}
- case PlayerLiveTV::ASPECT43:
+ case PlayerVideoLive::ASPECT43:
{
OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
if (osdv)
break;
}
- case PlayerLiveTV::ASPECT169:
+ case PlayerVideoLive::ASPECT169:
{
sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
sAspectRatio.setVisible(true);
break;
}
- case PlayerLiveTV::PREBUFFERING:
+ case PlayerVideoLive::PREBUFFERING:
{
preBuffering = m->tag;
Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);