]> git.vomp.tv Git - vompclient.git/blob - playerlivetv.cc
Preparations for dynamic mode switching
[vompclient.git] / playerlivetv.cc
1 /*\r
2     Copyright 2007 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \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
10 \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
15 \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
19 */\r
20 \r
21 #include "playerlivetv.h"\r
22 \r
23 #include "log.h"\r
24 #include "audio.h"\r
25 #include "video.h"\r
26 #include "demuxerts.h"\r
27 #include "vdr.h"\r
28 #include "messagequeue.h"\r
29 #include "remote.h"\r
30 #include "message.h"\r
31 #include "channel.h"\r
32 #include "dvbsubtitles.h"\r
33 #include "osdreceiver.h"\r
34 \r
35 // ----------------------------------- Called from outside, one offs or info funcs\r
36 \r
37 PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)\r
38 : vfeed(this), afeed(this), tfeed(this)\r
39 {\r
40   messageQueue = tmessageQueue;\r
41   messageReceiver = tmessageReceiver;\r
42   osdReceiver = tosdReceiver;\r
43   chanList = tchanList;\r
44   \r
45   audio = Audio::getInstance();\r
46   video = Video::getInstance();\r
47   logger = Log::getInstance();\r
48   vdr = VDR::getInstance();\r
49   initted = false;\r
50 \r
51   subtitlesShowing = false;\r
52   videoStartup = false;\r
53   pendingAudioPlay = false;\r
54 \r
55   stopNow = false;\r
56   state = S_STOP;\r
57 \r
58   video->turnVideoOn();\r
59 }\r
60 \r
61 PlayerLiveTV::~PlayerLiveTV()\r
62 {\r
63   if (initted) shutdown();\r
64 }\r
65 \r
66 int PlayerLiveTV::init()\r
67 {\r
68   if (initted) return 0;\r
69 \r
70   demuxer = new DemuxerTS();\r
71   if (!demuxer) return 0;\r
72   subtitles = new DVBSubtitles(osdReceiver);\r
73   if (!subtitles) return 0;\r
74 \r
75   teletext = new TeletextDecoderVBIEBU();\r
76   \r
77   unsigned int demux_video_size=2097152;\r
78    unsigned int demux_audio_size=524288;\r
79    if (video->supportsh264()) {\r
80           demux_video_size*=5*1;//5;\r
81 \r
82    }\r
83    if (audio->maysupportAc3()) {\r
84           //demux_audio_size*=2;\r
85    }\r
86 \r
87   int text_fak=video->getTeletextBufferFaktor();\r
88 \r
89  \r
90   if (!demuxer->init(this, audio, video, teletext, demux_video_size,demux_audio_size, 65536*text_fak,25./*unimportant*/,subtitles))\r
91   {\r
92     logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");\r
93     shutdown();\r
94     return 0;\r
95   }\r
96 \r
97   vfeed.init();\r
98   afeed.init();\r
99   tfeed.init();\r
100 \r
101   video->stop();\r
102   video->blank();\r
103   audio->stop();\r
104 \r
105   initted = true;\r
106   return 1;\r
107 }\r
108 \r
109 int PlayerLiveTV::shutdown()\r
110 {\r
111   if (!initted) return 0;\r
112   logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");\r
113   stop();\r
114   initted = false;\r
115 \r
116   delete demuxer;\r
117   delete subtitles;\r
118   delete teletext;\r
119   teletext = NULL;\r
120   return 1;\r
121 }\r
122 \r
123 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()\r
124 {\r
125   return demuxer->getmpAudioChannels();\r
126 }\r
127 \r
128 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()\r
129 {\r
130   return demuxer->getac3AudioChannels();\r
131 }\r
132 \r
133 int PlayerLiveTV::getCurrentAudioChannel()\r
134 {\r
135   return demuxer->getAID();\r
136 }\r
137 \r
138 void PlayerLiveTV::setAudioChannel(int newChannel, int type,int streamtype)\r
139 {\r
140   demuxer->setAID(newChannel,type,streamtype);\r
141 }\r
142 \r
143 void PlayerLiveTV::setSubtitleChannel(int newChannel)\r
144 {\r
145    demuxer->setSubID(newChannel);\r
146 }\r
147 \r
148 int *PlayerLiveTV::getTeletxtSubtitlePages()\r
149 {\r
150     return teletext->getSubtitlePages();\r
151 }\r
152 \r
153 int PlayerLiveTV::getCurrentSubtitleChannel(){\r
154     return demuxer->getSubID();\r
155 }\r
156 \r
157 bool PlayerLiveTV::toggleSubtitles()\r
158 {\r
159   if (!subtitlesShowing)\r
160   {\r
161     subtitlesShowing = true;\r
162     subtitles->show();\r
163   }\r
164   else\r
165   {\r
166     subtitlesShowing = false;\r
167     subtitles->hide();\r
168   }\r
169   return subtitlesShowing;\r
170 }\r
171 \r
172 \r
173 void  PlayerLiveTV::turnSubtitlesOn(bool ison) {\r
174  if (ison)\r
175   {\r
176     subtitlesShowing = true;\r
177     subtitles->show();\r
178   }\r
179   else\r
180   {\r
181     subtitlesShowing = false;\r
182     subtitles->hide();\r
183   }\r
184 \r
185 }\r
186 // ----------------------------------- Externally called events\r
187 \r
188 void PlayerLiveTV::go(ULONG index)\r
189 {\r
190   struct PLInstruction i;\r
191   i.instruction = I_SETCHANNEL;\r
192   i.channelIndex = index;\r
193   instructions.push(i);\r
194   threadStart();\r
195 }\r
196 \r
197 void PlayerLiveTV::setChannel(ULONG index)\r
198 {\r
199   logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");\r
200   struct PLInstruction i;\r
201   i.instruction = I_SETCHANNEL;\r
202   i.channelIndex = index;\r
203   instructions.push(i);  \r
204   threadSignalNoLock();\r
205 }\r
206 \r
207 void PlayerLiveTV::stop()\r
208 {\r
209   logger->log("PlayerLiveTV", Log::DEBUG, "stop");\r
210   struct PLInstruction i;\r
211   i.instruction = I_STOP;\r
212   instructions.push(i);\r
213   threadSignal();\r
214   threadStop();\r
215   logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");\r
216 }\r
217 \r
218 // ----------------------------------- Callback\r
219 \r
220 void PlayerLiveTV::call(void* caller)\r
221 {\r
222   if (caller == demuxer)\r
223   {\r
224     logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");\r
225 \r
226     int dxCurrentAspect = demuxer->getAspectRatio();\r
227     if (dxCurrentAspect == Demuxer::ASPECT_4_3)\r
228     {\r
229       if (video->getTVsize() == Video::ASPECT16X9)\r
230       {\r
231         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");\r
232         video->setAspectRatio(Video::ASPECT4X3);\r
233       }\r
234       else\r
235       {\r
236         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
237       }\r
238 \r
239       Message* m = new Message();\r
240       m->from = this;\r
241       m->to = messageReceiver;\r
242       m->message = Message::PLAYER_EVENT;\r
243       m->parameter = PlayerLiveTV::ASPECT43;\r
244       messageQueue->postMessageFromOuterSpace(m);\r
245     }\r
246     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)\r
247     {\r
248       if (video->getTVsize() == Video::ASPECT16X9)\r
249       {\r
250         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");\r
251         video->setAspectRatio(Video::ASPECT16X9);\r
252       }\r
253       else\r
254       {\r
255         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
256       }    \r
257 \r
258       Message* m = new Message();\r
259       m->from = this;\r
260       m->to = messageReceiver;\r
261       m->message = Message::PLAYER_EVENT;\r
262       m->parameter = PlayerLiveTV::ASPECT169;\r
263       messageQueue->postMessageFromOuterSpace(m);\r
264     }\r
265     else\r
266     {\r
267       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");\r
268     }\r
269   }\r
270   else if (caller == &afeed)\r
271   {\r
272     if (state == S_VIDEOSTARTUP)\r
273     {\r
274       logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");\r
275       videoStartup = true;\r
276       threadSignalNoLock();\r
277     }\r
278   }\r
279 }\r
280 \r
281 // -----------------------------------\r
282 \r
283 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)\r
284 {\r
285   // Flag:\r
286   // 0 = normal stream packet\r
287   // 1 = stream end\r
288   // 2 = connection lost\r
289 \r
290   //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);\r
291 \r
292   if (flag == 1)\r
293   {\r
294     if (data) abort();\r
295     \r
296     Message* m = new Message();\r
297     m->from = this;\r
298     m->to = messageReceiver;\r
299     m->message = Message::PLAYER_EVENT;\r
300     m->parameter = PlayerLiveTV::STREAM_END;\r
301     messageQueue->postMessageFromOuterSpace(m);\r
302   }\r
303         \r
304   if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)\r
305   {\r
306     StreamChunk s;\r
307     s.data = data;\r
308     s.len = len;\r
309     streamChunks.push(s);\r
310     threadSignalNoLock();\r
311   }\r
312   else\r
313   {\r
314     // Too many chunks in streamChunks, drop this chunk\r
315     free(data);\r
316     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");\r
317   }\r
318 }\r
319 \r
320 void PlayerLiveTV::clearStreamChunks()\r
321 {\r
322   while(streamChunks.size())\r
323   {\r
324     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");\r
325     struct StreamChunk s = streamChunks.front();\r
326     streamChunks.pop();\r
327     free(s.data);\r
328   }\r
329 }\r
330 \r
331 void PlayerLiveTV::chunkToDemuxer()\r
332 {\r
333   StreamChunk s = streamChunks.front();\r
334   streamChunks.pop();\r
335 //  logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);\r
336  /* int a =*/ demuxer->put((UCHAR*)s.data, s.len);\r
337 //  logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);\r
338   free(s.data);  \r
339   if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed\r
340   {\r
341       video->sync();\r
342       video->play();\r
343       video->pause();\r
344       //audio->setStreamType(Audio::MPEG2_PES);\r
345       audio->sync();\r
346       audio->play();\r
347       audio->pause();\r
348       pendingAudioPlay = false;\r
349   }\r
350 }\r
351 \r
352 void PlayerLiveTV::switchState(UCHAR newState)\r
353 {\r
354   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);\r
355 \r
356   switch(state)\r
357   {\r
358     case S_STOP:   // FROM S_STOP\r
359     {\r
360       switch(newState)\r
361       {\r
362         case S_VIDEOSTARTUP:\r
363         {\r
364           video->blank();\r
365           video->reset();\r
366           //video->sync();\r
367           //video->play();\r
368           //video->pause();\r
369 \r
370           audio->stop();\r
371           audio->unPause();\r
372           audio->reset();\r
373           //audio->setStreamType(Audio::MPEG2_PES);\r
374           //audio->sync();\r
375           // I make this modification, since the video/audio devices needs to know at least\r
376           // which kind of video is embedded inside the stream\r
377           // therefore the demuxer needs to feeded at least with enough data\r
378           // to have one video header\r
379           // This is crucial, if we have mixed h264/mpeg2 channels\r
380           // the information from channels is not enough since some directshow decoders need\r
381           // width and height information before startup\r
382           pendingAudioPlay = true;\r
383 \r
384           //audio->play();\r
385           //audio->pause();\r
386 \r
387           demuxer->reset();\r
388           demuxer->seek();\r
389 \r
390           afeed.start();\r
391           vfeed.start();\r
392           subtitles->start();\r
393           tfeed.start();\r
394           \r
395           state = newState;\r
396           if (!video->independentAVStartUp()){\r
397                   videoStartup = true;\r
398                   threadSignalNoLock();\r
399           }\r
400           return;\r
401         }\r
402         default:\r
403         {\r
404           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
405           abort();\r
406           break;\r
407         }\r
408       }\r
409     }\r
410 \r
411     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP\r
412     {\r
413       switch(newState)\r
414       {\r
415         case S_PREBUFFERING:\r
416         {\r
417           pendingAudioPlay=false;\r
418           vfeed.release();\r
419           state = newState;\r
420           return;\r
421         }\r
422         \r
423         case S_VIDEOSTARTUP:\r
424         {\r
425 \r
426           vdr->stopStreaming();\r
427           clearStreamChunks(); \r
428           vfeed.stop();\r
429           afeed.stop();\r
430           subtitles->stop();\r
431           tfeed.stop();\r
432                            \r
433           video->blank();\r
434           video->reset();\r
435           //video->sync();\r
436           //video->play();\r
437           //video->pause();\r
438           audio->stop();\r
439           audio->unPause();\r
440           audio->reset();\r
441           //audio->setStreamType(Audio::MPEG2_PES);\r
442           //audio->sync();\r
443           pendingAudioPlay = true;\r
444           //audio->play();\r
445           //audio->pause();\r
446 \r
447           demuxer->reset();\r
448           demuxer->seek();\r
449 \r
450           afeed.start();\r
451           vfeed.start();\r
452           subtitles->start();     \r
453           tfeed.start();\r
454           state = newState;\r
455           if (!video->independentAVStartUp()){\r
456               videoStartup = true;\r
457               threadSignalNoLock();\r
458           }\r
459           return;\r
460         }        \r
461         case S_STOP:\r
462         { \r
463           vdr->stopStreaming();\r
464           pendingAudioPlay=false;\r
465           clearStreamChunks();\r
466           vfeed.stop();\r
467           afeed.stop();\r
468           subtitles->stop();\r
469           tfeed.stop();\r
470           video->stop();\r
471           video->blank();\r
472           audio->stop();\r
473           audio->reset();\r
474           video->reset();\r
475           state = newState;\r
476           return;\r
477         }\r
478         default:\r
479         {\r
480           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
481           abort();\r
482           break;\r
483         }        \r
484       }\r
485     }\r
486     \r
487     case S_PREBUFFERING:    // FROM S_PREBUFFERING\r
488     {\r
489       switch(newState)\r
490       {\r
491         case S_PLAY:\r
492         {\r
493           pendingAudioPlay=false;\r
494           audio->unPause();\r
495           video->unPause();\r
496           state = newState;\r
497           return;\r
498         }\r
499         case S_VIDEOSTARTUP:\r
500         {\r
501           vdr->stopStreaming();\r
502           clearStreamChunks();\r
503           vfeed.stop();\r
504           afeed.stop();\r
505           subtitles->stop();\r
506           tfeed.stop();\r
507           video->stop();\r
508           video->blank();\r
509           audio->stop();\r
510           audio->unPause();\r
511           audio->reset();\r
512 \r
513           video->reset();\r
514           //video->sync();\r
515           //video->play();\r
516           //video->pause();\r
517 \r
518           //audio->setStreamType(Audio::MPEG2_PES);\r
519           //audio->sync();\r
520           pendingAudioPlay = true;\r
521           //audio->play();\r
522           //audio->pause();\r
523 \r
524           demuxer->reset();\r
525           demuxer->seek();\r
526 \r
527           afeed.start();\r
528           vfeed.start();\r
529           subtitles->start();\r
530           tfeed.start();\r
531 \r
532           state = newState;\r
533           return;\r
534         }\r
535         case S_STOP:\r
536         {\r
537           pendingAudioPlay=false;\r
538           vdr->stopStreaming();\r
539           clearStreamChunks();\r
540           vfeed.stop();\r
541           afeed.stop();\r
542           subtitles->stop();\r
543           tfeed.stop();\r
544           video->stop();\r
545           video->blank();\r
546           audio->stop();\r
547           audio->reset();\r
548           video->reset();\r
549           state = newState;\r
550           return;        \r
551         }\r
552         default:\r
553         {\r
554           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
555           abort();\r
556           break;\r
557         }        \r
558       }\r
559     }\r
560     \r
561     case S_PLAY:     // FROM S_PLAY\r
562     {\r
563       switch(newState)\r
564       {\r
565         case S_STOP:\r
566         { \r
567           pendingAudioPlay=false;\r
568           vdr->stopStreaming();\r
569           clearStreamChunks();\r
570           vfeed.stop();\r
571           afeed.stop();\r
572           subtitles->stop();\r
573           tfeed.stop();\r
574           video->stop();\r
575           video->blank();\r
576           audio->stop();\r
577           audio->reset();\r
578           video->reset();\r
579           state = newState;\r
580           return;\r
581         }\r
582         case S_VIDEOSTARTUP:\r
583         {\r
584           vdr->stopStreaming();\r
585           clearStreamChunks();\r
586           vfeed.stop();\r
587           afeed.stop();\r
588           subtitles->stop();\r
589           tfeed.stop();\r
590           video->stop();\r
591           video->blank();\r
592           audio->stop();\r
593           audio->unPause();\r
594           audio->reset();\r
595 \r
596           video->reset();\r
597           \r
598           //video->sync();\r
599          // video->play();\r
600           //video->pause();\r
601 \r
602           //audio->setStreamType(Audio::MPEG2_PES);\r
603           //audio->sync();\r
604           //audio->play();\r
605           //audio->pause();\r
606           pendingAudioPlay = true;\r
607           demuxer->reset();\r
608           demuxer->seek();\r
609 \r
610           afeed.start();\r
611           vfeed.start();\r
612           subtitles->start();\r
613           tfeed.start();\r
614           state = newState;\r
615           if (!video->independentAVStartUp()){\r
616                   videoStartup = true;\r
617               threadSignalNoLock();\r
618           }\r
619           return;\r
620         }\r
621         default:\r
622         {\r
623           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
624           abort();\r
625           break;\r
626         }        \r
627       }\r
628     }    \r
629   }  \r
630 }\r
631 \r
632 bool PlayerLiveTV::checkError()\r
633 {\r
634   if (!vdr->isConnected())\r
635   {\r
636     if (state != S_STOP) switchState(S_STOP);\r
637     \r
638     Message* m = new Message();\r
639     m->from = this;\r
640     m->to = messageReceiver;\r
641     m->message = Message::PLAYER_EVENT;\r
642     m->parameter = PlayerLiveTV::CONNECTION_LOST;\r
643     messageQueue->postMessageFromOuterSpace(m);\r
644     \r
645     return true;\r
646   }   \r
647   return false;\r
648 }\r
649 \r
650 void PlayerLiveTV::optimizeInstructionQueue()\r
651 {\r
652   // Walk the list\r
653   \r
654   // Currently there are only 2 instruction types, so this is a bit overkill...\r
655 \r
656   struct PLInstruction i;\r
657   while(instructions.size() > 1)\r
658   {\r
659     i = instructions.front();\r
660     if (i.instruction == I_SETCHANNEL)\r
661     {\r
662       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant\r
663     }\r
664     else if (i.instruction == I_STOP)\r
665     {\r
666       return; // return here and ensure the next instruction will be stop\r
667     }\r
668   }\r
669 }\r
670 \r
671 void PlayerLiveTV::threadMethod()\r
672 {\r
673   while(1)\r
674   {\r
675 \r
676         //logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);\r
677     if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data\r
678     {\r
679            logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");\r
680       switchState(S_PREBUFFERING);\r
681       videoStartup = false;\r
682       preBufferCount = 0;\r
683       \r
684       checkError();\r
685     }  \r
686   \r
687     while(!instructions.empty())\r
688     {\r
689       if (instructions.size() > 1) optimizeInstructionQueue();\r
690 \r
691       struct PLInstruction i = instructions.front();\r
692       instructions.pop();\r
693     \r
694       if (i.instruction == I_SETCHANNEL)\r
695       {\r
696         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");\r
697        \r
698         \r
699         switchState(S_VIDEOSTARTUP);\r
700         \r
701         if (!checkError())\r
702         {\r
703            Channel* chan = (*chanList)[i.channelIndex];\r
704            chan->loadPids();\r
705            h264=chan->vstreamtype==0x1b;\r
706            demuxer->seth264(h264);\r
707            video->seth264mode(chan->vstreamtype==0x1b);\r
708            demuxer->setVID(chan->vpid);\r
709            video->seth264mode(chan->vstreamtype==0x1b);\r
710 \r
711            bool found=false;\r
712 \r
713           if (chan->numAPids > 0) \r
714           {\r
715                   int j=0;\r
716                   while (j<chan->numAPids && !found) {\r
717                           if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {\r
718                                   demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);\r
719                                   audio->setStreamType(Audio::MPEG2_PES);\r
720                                   logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);\r
721                                   found=true;\r
722                           }\r
723                           j++;\r
724                   }\r
725           }\r
726 \r
727           if (!found)\r
728           {\r
729               if (chan->numDPids > 0  && audio->maysupportAc3()) \r
730               {\r
731                   int j=0;\r
732                   while (j<chan->numDPids && !found) {\r
733                           if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {\r
734                                   demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);\r
735                                   audio->setStreamType(Audio::MPEG2_PES);\r
736                                   logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);\r
737                                   found=true;\r
738                           }\r
739                           j++;\r
740                   }\r
741               } \r
742               else\r
743               {\r
744                   logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);\r
745               }\r
746           }\r
747           if (chan->numSPids > 0)\r
748             demuxer->setSubID(chan->spids[0].pid);\r
749           demuxer->setTID(chan->tpid);\r
750           teletext->ResetDecoder();\r
751           int streamSuccess = vdr->streamChannel(chan->number, this);\r
752           if (!checkError() && !streamSuccess)\r
753           {      \r
754             Message* m = new Message();\r
755             m->from = this;\r
756             m->to = messageReceiver;\r
757             m->message = Message::PLAYER_EVENT;\r
758             m->parameter = PlayerLiveTV::STREAM_END;\r
759             messageQueue->postMessageFromOuterSpace(m);\r
760           }\r
761         }\r
762       }\r
763       else if (i.instruction == I_STOP)\r
764       {\r
765         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");\r
766         switchState(S_STOP);\r
767         checkError();\r
768 \r
769         stopNow = true;\r
770         break;\r
771       }\r
772     }\r
773 \r
774         threadCheckExit();\r
775 \r
776     if (stopNow) break;\r
777 \r
778     while(streamChunks.size())\r
779     {\r
780         //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark1 %d", streamChunks.size());\r
781       chunkToDemuxer();\r
782       //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark2 %d", streamChunks.size());\r
783 \r
784       if (state == S_PREBUFFERING)\r
785       {\r
786          // logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark3");\r
787         ++preBufferCount;\r
788         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);\r
789         logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);\r
790         \r
791         Message* m = new Message();\r
792         m->from = this;\r
793         m->to = messageReceiver;\r
794         m->message = Message::PLAYER_EVENT;\r
795         m->parameter = PlayerLiveTV::PREBUFFERING;\r
796         m->tag = percentDone;\r
797         messageQueue->postMessageFromOuterSpace(m);\r
798 \r
799         if (preBufferCount == preBufferAmount)\r
800         {\r
801           switchState(S_PLAY);\r
802           checkError();\r
803         }\r
804       }\r
805     }\r
806   //  logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal %d", streamChunks.size());\r
807     threadLock();\r
808     threadWaitForSignal(); // unlocks and waits for signal\r
809     threadUnlock();\r
810     //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal2 %d",streamChunks.size());\r
811   }\r
812 \r
813   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");\r
814 }\r
815 \r