2 Copyright 2007 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, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "playerlivetv.h"
26 #include "demuxerts.h"
28 #include "messagequeue.h"
32 #include "dvbsubtitles.h"
33 #include "osdreceiver.h"
35 // ----------------------------------- Called from outside, one offs or info funcs
37 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
38 : vfeed(this), afeed(this), tfeed(this)
40 messageQueue = tmessageQueue;
41 messageReceiver = tmessageReceiver;
42 osdReceiver = tosdReceiver;
45 audio = Audio::getInstance();
46 video = Video::getInstance();
47 logger = Log::getInstance();
48 vdr = VDR::getInstance();
51 subtitlesShowing = false;
53 pendingAudioPlay = false;
61 PlayerLiveTV::~PlayerLiveTV()
63 if (initted) shutdown();
66 int PlayerLiveTV::init()
68 if (initted) return 0;
70 demuxer = new DemuxerTS();
71 if (!demuxer) return 0;
72 subtitles = new DVBSubtitles(osdReceiver);
73 if (!subtitles) return 0;
75 teletext = new TeletextDecoderVBIEBU();
77 unsigned int demux_video_size=2097152;
78 unsigned int demux_audio_size=524288;
79 if (video->supportsh264()) {
80 demux_video_size*=5*1;//5;
83 if (audio->maysupportAc3()) {
84 //demux_audio_size*=2;
87 int text_fak=video->getTeletextBufferFaktor();
90 if (!demuxer->init(this, audio, video, teletext, demux_video_size,demux_audio_size, 65536*text_fak,25./*unimportant*/,subtitles))
92 logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
109 int PlayerLiveTV::shutdown()
111 if (!initted) return 0;
112 logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");
123 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
125 return demuxer->getmpAudioChannels();
128 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
130 return demuxer->getac3AudioChannels();
133 int PlayerLiveTV::getCurrentAudioChannel()
135 return demuxer->getAID();
138 void PlayerLiveTV::setAudioChannel(int newChannel, int type,int streamtype)
140 demuxer->setAID(newChannel,type,streamtype);
143 void PlayerLiveTV::setSubtitleChannel(int newChannel)
145 demuxer->setSubID(newChannel);
148 int *PlayerLiveTV::getTeletxtSubtitlePages()
150 return teletext->getSubtitlePages();
153 int PlayerLiveTV::getCurrentSubtitleChannel(){
154 return demuxer->getSubID();
157 bool PlayerLiveTV::toggleSubtitles()
159 if (!subtitlesShowing)
161 subtitlesShowing = true;
166 subtitlesShowing = false;
169 return subtitlesShowing;
173 void PlayerLiveTV::turnSubtitlesOn(bool ison) {
176 subtitlesShowing = true;
181 subtitlesShowing = false;
186 // ----------------------------------- Externally called events
188 void PlayerLiveTV::go(ULONG index)
190 struct PLInstruction i;
191 i.instruction = I_SETCHANNEL;
192 i.channelIndex = index;
193 instructions.push(i);
197 void PlayerLiveTV::setChannel(ULONG index)
199 logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
200 struct PLInstruction i;
201 i.instruction = I_SETCHANNEL;
202 i.channelIndex = index;
203 instructions.push(i);
204 threadSignalNoLock();
207 void PlayerLiveTV::stop()
209 logger->log("PlayerLiveTV", Log::DEBUG, "stop");
210 struct PLInstruction i;
211 i.instruction = I_STOP;
212 instructions.push(i);
215 logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");
218 // ----------------------------------- Callback
220 void PlayerLiveTV::call(void* caller)
222 if (caller == demuxer)
224 logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
227 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
228 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
230 if (video->getTVsize() == Video::ASPECT16X9)
232 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
233 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
237 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
240 Message* m = new Message();
242 m->to = messageReceiver;
243 m->message = Message::PLAYER_EVENT;
244 m->parameter = PlayerLiveTV::ASPECT43;
245 messageQueue->postMessageFromOuterSpace(m);
247 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
249 if (video->getTVsize() == Video::ASPECT16X9)
251 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
252 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
256 logger->log("PlayerLiveTV", 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 = PlayerLiveTV::ASPECT169;
264 messageQueue->postMessageFromOuterSpace(m);
268 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... switch anyway");
269 video->setAspectRatio( dxCurrentAspect,parx,pary);
272 else if (caller == &afeed)
274 if (state == S_VIDEOSTARTUP)
276 logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
278 threadSignalNoLock();
283 // -----------------------------------
285 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
288 // 0 = normal stream packet
290 // 2 = connection lost
292 //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
298 Message* m = new Message();
300 m->to = messageReceiver;
301 m->message = Message::PLAYER_EVENT;
302 m->parameter = PlayerLiveTV::STREAM_END;
303 messageQueue->postMessageFromOuterSpace(m);
306 if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)
311 streamChunks.push(s);
312 threadSignalNoLock();
316 // Too many chunks in streamChunks, drop this chunk
318 logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
322 void PlayerLiveTV::clearStreamChunks()
324 while(streamChunks.size())
326 logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
327 struct StreamChunk s = streamChunks.front();
333 void PlayerLiveTV::chunkToDemuxer()
335 StreamChunk s = streamChunks.front();
337 // logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
338 /* int a =*/ demuxer->put((UCHAR*)s.data, s.len);
339 // logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
341 if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
346 //audio->setStreamType(Audio::MPEG2_PES);
350 pendingAudioPlay = false;
354 void PlayerLiveTV::switchState(UCHAR newState)
356 logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
360 case S_STOP: // FROM S_STOP
375 //audio->setStreamType(Audio::MPEG2_PES);
377 // I make this modification, since the video/audio devices needs to know at least
378 // which kind of video is embedded inside the stream
379 // therefore the demuxer needs to feeded at least with enough data
380 // to have one video header
381 // This is crucial, if we have mixed h264/mpeg2 channels
382 // the information from channels is not enough since some directshow decoders need
383 // width and height information before startup
384 pendingAudioPlay = true;
398 if (!video->independentAVStartUp()){
400 threadSignalNoLock();
406 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
413 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
419 pendingAudioPlay=false;
428 vdr->stopStreaming();
443 //audio->setStreamType(Audio::MPEG2_PES);
445 pendingAudioPlay = true;
457 if (!video->independentAVStartUp()){
459 threadSignalNoLock();
465 vdr->stopStreaming();
466 pendingAudioPlay=false;
482 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
489 case S_PREBUFFERING: // FROM S_PREBUFFERING
495 pendingAudioPlay=false;
503 vdr->stopStreaming();
520 //audio->setStreamType(Audio::MPEG2_PES);
522 pendingAudioPlay = true;
539 pendingAudioPlay=false;
540 vdr->stopStreaming();
556 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
563 case S_PLAY: // FROM S_PLAY
569 pendingAudioPlay=false;
570 vdr->stopStreaming();
586 vdr->stopStreaming();
604 //audio->setStreamType(Audio::MPEG2_PES);
608 pendingAudioPlay = true;
617 if (!video->independentAVStartUp()){
619 threadSignalNoLock();
625 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
634 bool PlayerLiveTV::checkError()
636 if (!vdr->isConnected())
638 if (state != S_STOP) switchState(S_STOP);
640 Message* m = new Message();
642 m->to = messageReceiver;
643 m->message = Message::PLAYER_EVENT;
644 m->parameter = PlayerLiveTV::CONNECTION_LOST;
645 messageQueue->postMessageFromOuterSpace(m);
652 void PlayerLiveTV::optimizeInstructionQueue()
656 // Currently there are only 2 instruction types, so this is a bit overkill...
658 struct PLInstruction i;
659 while(instructions.size() > 1)
661 i = instructions.front();
662 if (i.instruction == I_SETCHANNEL)
664 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
666 else if (i.instruction == I_STOP)
668 return; // return here and ensure the next instruction will be stop
673 void PlayerLiveTV::threadMethod()
678 //logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
679 if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
681 logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");
682 switchState(S_PREBUFFERING);
683 videoStartup = false;
689 while(!instructions.empty())
691 if (instructions.size() > 1) optimizeInstructionQueue();
693 struct PLInstruction i = instructions.front();
696 if (i.instruction == I_SETCHANNEL)
698 logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
701 switchState(S_VIDEOSTARTUP);
705 Channel* chan = (*chanList)[i.channelIndex];
707 h264=chan->vstreamtype==0x1b;
708 demuxer->seth264(h264);
709 video->seth264mode(chan->vstreamtype==0x1b);
710 demuxer->setVID(chan->vpid);
711 video->seth264mode(chan->vstreamtype==0x1b);
715 if (chan->numAPids > 0)
718 while (j<chan->numAPids && !found) {
719 if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {
720 demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);
721 audio->setStreamType(Audio::MPEG2_PES);
722 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);
731 if (chan->numDPids > 0 && audio->maysupportAc3())
734 while (j<chan->numDPids && !found) {
735 if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {
736 demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);
737 audio->setStreamType(Audio::MPEG2_PES);
738 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);
746 logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
749 if (chan->numSPids > 0)
750 demuxer->setSubID(chan->spids[0].pid);
751 demuxer->setTID(chan->tpid);
752 teletext->ResetDecoder();
753 int streamSuccess = vdr->streamChannel(chan->number, this);
754 if (!checkError() && !streamSuccess)
756 Message* m = new Message();
758 m->to = messageReceiver;
759 m->message = Message::PLAYER_EVENT;
760 m->parameter = PlayerLiveTV::STREAM_END;
761 messageQueue->postMessageFromOuterSpace(m);
765 else if (i.instruction == I_STOP)
767 logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
780 while(streamChunks.size())
782 //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
784 //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
786 if (state == S_PREBUFFERING)
788 // logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark3");
790 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
791 logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
793 Message* m = new Message();
795 m->to = messageReceiver;
796 m->message = Message::PLAYER_EVENT;
797 m->parameter = PlayerLiveTV::PREBUFFERING;
798 m->tag = percentDone;
799 messageQueue->postMessageFromOuterSpace(m);
801 if (preBufferCount == preBufferAmount)
808 // logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal %d", streamChunks.size());
810 threadWaitForSignal(); // unlocks and waits for signal
812 //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
815 logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");