2 Copyright 2007 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
21 #include "playerlivetv.h"
\r
26 #include "demuxerts.h"
\r
28 #include "messagequeue.h"
\r
30 #include "message.h"
\r
31 #include "channel.h"
\r
32 #include "dvbsubtitles.h"
\r
33 #include "osdreceiver.h"
\r
35 // ----------------------------------- Called from outside, one offs or info funcs
\r
37 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)
\r
38 : vfeed(this), afeed(this), tfeed(this)
\r
40 messageQueue = tmessageQueue;
\r
41 messageReceiver = tmessageReceiver;
\r
42 osdReceiver = tosdReceiver;
\r
43 chanList = tchanList;
\r
45 audio = Audio::getInstance();
\r
46 video = Video::getInstance();
\r
47 logger = Log::getInstance();
\r
48 vdr = VDR::getInstance();
\r
51 subtitlesShowing = false;
\r
52 videoStartup = false;
\r
53 pendingAudioPlay = false;
\r
58 video->turnVideoOn();
\r
61 PlayerLiveTV::~PlayerLiveTV()
\r
63 if (initted) shutdown();
\r
66 int PlayerLiveTV::init()
\r
68 if (initted) return 0;
\r
70 demuxer = new DemuxerTS();
\r
71 if (!demuxer) return 0;
\r
72 subtitles = new DVBSubtitles(osdReceiver);
\r
73 if (!subtitles) return 0;
\r
75 teletext = new TeletextDecoderVBIEBU();
\r
77 unsigned int demux_video_size=2097152;
\r
78 if (video->supportsh264()) demux_video_size*=5;
\r
80 int text_fak=video->getTeletextBufferFaktor();
\r
83 if (!demuxer->init(this, audio, video, teletext, demux_video_size,524288, 65536*text_fak,25./*unimportant*/,subtitles))
\r
85 logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");
\r
102 int PlayerLiveTV::shutdown()
\r
104 if (!initted) return 0;
\r
105 logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");
\r
116 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()
\r
118 return demuxer->getmpAudioChannels();
\r
121 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()
\r
123 return demuxer->getac3AudioChannels();
\r
126 int PlayerLiveTV::getCurrentAudioChannel()
\r
128 return demuxer->getAID();
\r
131 void PlayerLiveTV::setAudioChannel(int newChannel, int type)
\r
133 demuxer->setAID(newChannel,type);
\r
136 void PlayerLiveTV::setSubtitleChannel(int newChannel)
\r
138 demuxer->setSubID(newChannel);
\r
141 int *PlayerLiveTV::getTeletxtSubtitlePages()
\r
143 return teletext->getSubtitlePages();
\r
146 int PlayerLiveTV::getCurrentSubtitleChannel(){
\r
147 return demuxer->getSubID();
\r
150 bool PlayerLiveTV::toggleSubtitles()
\r
152 if (!subtitlesShowing)
\r
154 subtitlesShowing = true;
\r
159 subtitlesShowing = false;
\r
162 return subtitlesShowing;
\r
166 void PlayerLiveTV::turnSubtitlesOn(bool ison) {
\r
169 subtitlesShowing = true;
\r
174 subtitlesShowing = false;
\r
179 // ----------------------------------- Externally called events
\r
181 void PlayerLiveTV::go(ULONG index)
\r
183 struct PLInstruction i;
\r
184 i.instruction = I_SETCHANNEL;
\r
185 i.channelIndex = index;
\r
186 instructions.push(i);
\r
190 void PlayerLiveTV::setChannel(ULONG index)
\r
192 logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");
\r
193 struct PLInstruction i;
\r
194 i.instruction = I_SETCHANNEL;
\r
195 i.channelIndex = index;
\r
196 instructions.push(i);
\r
197 threadSignalNoLock();
\r
200 void PlayerLiveTV::stop()
\r
202 logger->log("PlayerLiveTV", Log::DEBUG, "stop");
\r
203 struct PLInstruction i;
\r
204 i.instruction = I_STOP;
\r
205 instructions.push(i);
\r
208 logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");
\r
211 // ----------------------------------- Callback
\r
213 void PlayerLiveTV::call(void* caller)
\r
215 if (caller == demuxer)
\r
217 logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");
\r
219 int dxCurrentAspect = demuxer->getAspectRatio();
\r
220 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
\r
222 if (video->getTVsize() == Video::ASPECT16X9)
\r
224 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
\r
225 video->setAspectRatio(Video::ASPECT4X3);
\r
229 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
\r
232 Message* m = new Message();
\r
234 m->to = messageReceiver;
\r
235 m->message = Message::PLAYER_EVENT;
\r
236 m->parameter = PlayerLiveTV::ASPECT43;
\r
237 messageQueue->postMessageFromOuterSpace(m);
\r
239 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
\r
241 if (video->getTVsize() == Video::ASPECT16X9)
\r
243 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
\r
244 video->setAspectRatio(Video::ASPECT16X9);
\r
248 logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
\r
251 Message* m = new Message();
\r
253 m->to = messageReceiver;
\r
254 m->message = Message::PLAYER_EVENT;
\r
255 m->parameter = PlayerLiveTV::ASPECT169;
\r
256 messageQueue->postMessageFromOuterSpace(m);
\r
260 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");
\r
263 else if (caller == &afeed)
\r
265 if (state == S_VIDEOSTARTUP)
\r
267 logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");
\r
268 videoStartup = true;
\r
269 threadSignalNoLock();
\r
274 // -----------------------------------
\r
276 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)
\r
279 // 0 = normal stream packet
\r
281 // 2 = connection lost
\r
283 //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);
\r
289 Message* m = new Message();
\r
291 m->to = messageReceiver;
\r
292 m->message = Message::PLAYER_EVENT;
\r
293 m->parameter = PlayerLiveTV::STREAM_END;
\r
294 messageQueue->postMessageFromOuterSpace(m);
\r
297 if (streamChunks.size() < 11)
\r
302 streamChunks.push(s);
\r
303 threadSignalNoLock();
\r
307 // Too many chunks in streamChunks, drop this chunk
\r
309 logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");
\r
313 void PlayerLiveTV::clearStreamChunks()
\r
315 while(streamChunks.size())
\r
317 logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");
\r
318 struct StreamChunk s = streamChunks.front();
\r
319 streamChunks.pop();
\r
324 void PlayerLiveTV::chunkToDemuxer()
\r
326 StreamChunk s = streamChunks.front();
\r
327 streamChunks.pop();
\r
328 // logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);
\r
329 /* int a =*/ demuxer->put((UCHAR*)s.data, s.len);
\r
330 // logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);
\r
332 if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed
\r
337 //audio->setStreamType(Audio::MPEG2_PES);
\r
341 pendingAudioPlay = false;
\r
345 void PlayerLiveTV::switchState(UCHAR newState)
\r
347 logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);
\r
351 case S_STOP: // FROM S_STOP
\r
355 case S_VIDEOSTARTUP:
\r
366 //audio->setStreamType(Audio::MPEG2_PES);
\r
368 // I make this modification, since the video/audio devices needs to know at least
\r
369 // which kind of video is embedded inside the stream
\r
370 // therefore the demuxer needs to feeded at least with enough data
\r
371 // to have one video header
\r
372 // This is crucial, if we have mixed h264/mpeg2 channels
\r
373 // the information from channels is not enough since some directshow decoders need
\r
374 // width and height information before startup
\r
375 pendingAudioPlay = true;
\r
385 subtitles->start();
\r
389 if (!video->independentAVStartUp()){
\r
390 videoStartup = true;
\r
391 threadSignalNoLock();
\r
397 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
404 case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP
\r
408 case S_PREBUFFERING:
\r
410 pendingAudioPlay=false;
\r
416 case S_VIDEOSTARTUP:
\r
419 vdr->stopStreaming();
\r
420 clearStreamChunks();
\r
434 //audio->setStreamType(Audio::MPEG2_PES);
\r
436 pendingAudioPlay = true;
\r
445 subtitles->start();
\r
448 if (!video->independentAVStartUp()){
\r
449 videoStartup = true;
\r
450 threadSignalNoLock();
\r
456 vdr->stopStreaming();
\r
457 pendingAudioPlay=false;
\r
458 clearStreamChunks();
\r
473 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
480 case S_PREBUFFERING: // FROM S_PREBUFFERING
\r
486 pendingAudioPlay=false;
\r
492 case S_VIDEOSTARTUP:
\r
494 vdr->stopStreaming();
\r
495 clearStreamChunks();
\r
511 //audio->setStreamType(Audio::MPEG2_PES);
\r
513 pendingAudioPlay = true;
\r
522 subtitles->start();
\r
530 pendingAudioPlay=false;
\r
531 vdr->stopStreaming();
\r
532 clearStreamChunks();
\r
547 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
554 case S_PLAY: // FROM S_PLAY
\r
560 pendingAudioPlay=false;
\r
561 vdr->stopStreaming();
\r
562 clearStreamChunks();
\r
575 case S_VIDEOSTARTUP:
\r
577 vdr->stopStreaming();
\r
578 clearStreamChunks();
\r
595 //audio->setStreamType(Audio::MPEG2_PES);
\r
599 pendingAudioPlay = true;
\r
605 subtitles->start();
\r
608 if (!video->independentAVStartUp()){
\r
609 videoStartup = true;
\r
610 threadSignalNoLock();
\r
616 logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);
\r
625 bool PlayerLiveTV::checkError()
\r
627 if (!vdr->isConnected())
\r
629 if (state != S_STOP) switchState(S_STOP);
\r
631 Message* m = new Message();
\r
633 m->to = messageReceiver;
\r
634 m->message = Message::PLAYER_EVENT;
\r
635 m->parameter = PlayerLiveTV::CONNECTION_LOST;
\r
636 messageQueue->postMessageFromOuterSpace(m);
\r
643 void PlayerLiveTV::optimizeInstructionQueue()
\r
647 // Currently there are only 2 instruction types, so this is a bit overkill...
\r
649 struct PLInstruction i;
\r
650 while(instructions.size() > 1)
\r
652 i = instructions.front();
\r
653 if (i.instruction == I_SETCHANNEL)
\r
655 instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant
\r
657 else if (i.instruction == I_STOP)
\r
659 return; // return here and ensure the next instruction will be stop
\r
664 void PlayerLiveTV::threadMethod()
\r
669 // logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);
\r
670 if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data
\r
672 logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");
\r
673 switchState(S_PREBUFFERING);
\r
674 videoStartup = false;
\r
675 preBufferCount = 0;
\r
680 while(!instructions.empty())
\r
682 if (instructions.size() > 1) optimizeInstructionQueue();
\r
684 struct PLInstruction i = instructions.front();
\r
685 instructions.pop();
\r
687 if (i.instruction == I_SETCHANNEL)
\r
689 logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");
\r
692 switchState(S_VIDEOSTARTUP);
\r
696 Channel* chan = (*chanList)[i.channelIndex];
\r
698 h264=chan->vstreamtype==0x1b;
\r
699 demuxer->seth264(h264);
\r
700 video->seth264mode(chan->vstreamtype==0x1b);
\r
701 demuxer->setVID(chan->vpid);
\r
702 video->seth264mode(chan->vstreamtype==0x1b);
\r
704 if (chan->numAPids > 0)
\r
706 demuxer->setAID(chan->apids[0].pid,0);
\r
707 audio->setStreamType(Audio::MPEG2_PES);
\r
708 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid);
\r
712 if (chan->numDPids > 0 && audio->maysupportAc3())
\r
714 demuxer->setAID(chan->dpids[0].pid,1);
\r
715 logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3)", chan->vpid, chan->dpids[0].pid);
\r
719 logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);
\r
722 if (chan->numSPids > 0)
\r
723 demuxer->setSubID(chan->spids[0].pid);
\r
724 demuxer->setTID(chan->tpid);
\r
725 teletext->ResetDecoder();
\r
726 int streamSuccess = vdr->streamChannel(chan->number, this);
\r
727 if (!checkError() && !streamSuccess)
\r
729 Message* m = new Message();
\r
731 m->to = messageReceiver;
\r
732 m->message = Message::PLAYER_EVENT;
\r
733 m->parameter = PlayerLiveTV::STREAM_END;
\r
734 messageQueue->postMessageFromOuterSpace(m);
\r
738 else if (i.instruction == I_STOP)
\r
740 logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");
\r
741 switchState(S_STOP);
\r
751 if (stopNow) break;
\r
753 while(streamChunks.size())
\r
757 if (state == S_PREBUFFERING)
\r
760 ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);
\r
761 logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);
\r
763 Message* m = new Message();
\r
765 m->to = messageReceiver;
\r
766 m->message = Message::PLAYER_EVENT;
\r
767 m->parameter = PlayerLiveTV::PREBUFFERING;
\r
768 m->tag = percentDone;
\r
769 messageQueue->postMessageFromOuterSpace(m);
\r
771 if (preBufferCount == preBufferAmount)
\r
773 switchState(S_PLAY);
\r
779 threadWaitForSignal(); // unlocks and waits for signal
\r
783 logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
\r