]> git.vomp.tv Git - vompclient-marten.git/blob - playerlivetv.cc
FFMPEG decoding in color at 7 fps
[vompclient-marten.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   if (video->supportsh264()) demux_video_size*=5;\r
79 \r
80   int text_fak=video->getTeletextBufferFaktor();\r
81 \r
82  \r
83   if (!demuxer->init(this, audio, video, teletext, demux_video_size,524288, 65536*text_fak,25./*unimportant*/,subtitles))\r
84   {\r
85     logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");\r
86     shutdown();\r
87     return 0;\r
88   }\r
89 \r
90   vfeed.init();\r
91   afeed.init();\r
92   tfeed.init();\r
93 \r
94   video->stop();\r
95   video->blank();\r
96   audio->stop();\r
97 \r
98   initted = true;\r
99   return 1;\r
100 }\r
101 \r
102 int PlayerLiveTV::shutdown()\r
103 {\r
104   if (!initted) return 0;\r
105   logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");\r
106   stop();\r
107   initted = false;\r
108 \r
109   delete demuxer;\r
110   delete subtitles;\r
111   delete teletext;\r
112   teletext = NULL;\r
113   return 1;\r
114 }\r
115 \r
116 bool* PlayerLiveTV::getDemuxerMpegAudioChannels()\r
117 {\r
118   return demuxer->getmpAudioChannels();\r
119 }\r
120 \r
121 bool* PlayerLiveTV::getDemuxerAc3AudioChannels()\r
122 {\r
123   return demuxer->getac3AudioChannels();\r
124 }\r
125 \r
126 int PlayerLiveTV::getCurrentAudioChannel()\r
127 {\r
128   return demuxer->getAID();\r
129 }\r
130 \r
131 void PlayerLiveTV::setAudioChannel(int newChannel, int type)\r
132 {\r
133   demuxer->setAID(newChannel,type);\r
134 }\r
135 \r
136 void PlayerLiveTV::setSubtitleChannel(int newChannel)\r
137 {\r
138    demuxer->setSubID(newChannel);\r
139 }\r
140 \r
141 int *PlayerLiveTV::getTeletxtSubtitlePages()\r
142 {\r
143     return teletext->getSubtitlePages();\r
144 }\r
145 \r
146 int PlayerLiveTV::getCurrentSubtitleChannel(){\r
147     return demuxer->getSubID();\r
148 }\r
149 \r
150 bool PlayerLiveTV::toggleSubtitles()\r
151 {\r
152   if (!subtitlesShowing)\r
153   {\r
154     subtitlesShowing = true;\r
155     subtitles->show();\r
156   }\r
157   else\r
158   {\r
159     subtitlesShowing = false;\r
160     subtitles->hide();\r
161   }\r
162   return subtitlesShowing;\r
163 }\r
164 \r
165 \r
166 void  PlayerLiveTV::turnSubtitlesOn(bool ison) {\r
167  if (ison)\r
168   {\r
169     subtitlesShowing = true;\r
170     subtitles->show();\r
171   }\r
172   else\r
173   {\r
174     subtitlesShowing = false;\r
175     subtitles->hide();\r
176   }\r
177 \r
178 }\r
179 // ----------------------------------- Externally called events\r
180 \r
181 void PlayerLiveTV::go(ULONG index)\r
182 {\r
183   struct PLInstruction i;\r
184   i.instruction = I_SETCHANNEL;\r
185   i.channelIndex = index;\r
186   instructions.push(i);\r
187   threadStart();\r
188 }\r
189 \r
190 void PlayerLiveTV::setChannel(ULONG index)\r
191 {\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
198 }\r
199 \r
200 void PlayerLiveTV::stop()\r
201 {\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
206   threadSignal();\r
207   threadStop();\r
208   logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");\r
209 }\r
210 \r
211 // ----------------------------------- Callback\r
212 \r
213 void PlayerLiveTV::call(void* caller)\r
214 {\r
215   if (caller == demuxer)\r
216   {\r
217     logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");\r
218 \r
219     int dxCurrentAspect = demuxer->getAspectRatio();\r
220     if (dxCurrentAspect == Demuxer::ASPECT_4_3)\r
221     {\r
222       if (video->getTVsize() == Video::ASPECT16X9)\r
223       {\r
224         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");\r
225         video->setAspectRatio(Video::ASPECT4X3);\r
226       }\r
227       else\r
228       {\r
229         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
230       }\r
231 \r
232       Message* m = new Message();\r
233       m->from = this;\r
234       m->to = messageReceiver;\r
235       m->message = Message::PLAYER_EVENT;\r
236       m->parameter = PlayerLiveTV::ASPECT43;\r
237       messageQueue->postMessageFromOuterSpace(m);\r
238     }\r
239     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)\r
240     {\r
241       if (video->getTVsize() == Video::ASPECT16X9)\r
242       {\r
243         logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");\r
244         video->setAspectRatio(Video::ASPECT16X9);\r
245       }\r
246       else\r
247       {\r
248         logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
249       }    \r
250 \r
251       Message* m = new Message();\r
252       m->from = this;\r
253       m->to = messageReceiver;\r
254       m->message = Message::PLAYER_EVENT;\r
255       m->parameter = PlayerLiveTV::ASPECT169;\r
256       messageQueue->postMessageFromOuterSpace(m);\r
257     }\r
258     else\r
259     {\r
260       logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");\r
261     }\r
262   }\r
263   else if (caller == &afeed)\r
264   {\r
265     if (state == S_VIDEOSTARTUP)\r
266     {\r
267       logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");\r
268       videoStartup = true;\r
269       threadSignalNoLock();\r
270     }\r
271   }\r
272 }\r
273 \r
274 // -----------------------------------\r
275 \r
276 void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)\r
277 {\r
278   // Flag:\r
279   // 0 = normal stream packet\r
280   // 1 = stream end\r
281   // 2 = connection lost\r
282 \r
283   //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);\r
284 \r
285   if (flag == 1)\r
286   {\r
287     if (data) abort();\r
288     \r
289     Message* m = new Message();\r
290     m->from = this;\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
295   }\r
296         \r
297   if (streamChunks.size() < 11)\r
298   {\r
299     StreamChunk s;\r
300     s.data = data;\r
301     s.len = len;\r
302     streamChunks.push(s);\r
303     threadSignalNoLock();\r
304   }\r
305   else\r
306   {\r
307     // Too many chunks in streamChunks, drop this chunk\r
308     free(data);\r
309     logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");\r
310   }\r
311 }\r
312 \r
313 void PlayerLiveTV::clearStreamChunks()\r
314 {\r
315   while(streamChunks.size())\r
316   {\r
317     logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");\r
318     struct StreamChunk s = streamChunks.front();\r
319     streamChunks.pop();\r
320     free(s.data);\r
321   }\r
322 }\r
323 \r
324 void PlayerLiveTV::chunkToDemuxer()\r
325 {\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
331   free(s.data);  \r
332   if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed\r
333   {\r
334       video->sync();\r
335       video->play();\r
336       video->pause();\r
337       //audio->setStreamType(Audio::MPEG2_PES);\r
338       audio->sync();\r
339       audio->play();\r
340       audio->pause();\r
341       pendingAudioPlay = false;\r
342   }\r
343 }\r
344 \r
345 void PlayerLiveTV::switchState(UCHAR newState)\r
346 {\r
347   logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);\r
348 \r
349   switch(state)\r
350   {\r
351     case S_STOP:   // FROM S_STOP\r
352     {\r
353       switch(newState)\r
354       {\r
355         case S_VIDEOSTARTUP:\r
356         {\r
357           video->blank();\r
358           video->reset();\r
359           //video->sync();\r
360           //video->play();\r
361           //video->pause();\r
362 \r
363           audio->stop();\r
364           audio->unPause();\r
365           audio->reset();\r
366           //audio->setStreamType(Audio::MPEG2_PES);\r
367           //audio->sync();\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
376 \r
377           //audio->play();\r
378           //audio->pause();\r
379 \r
380           demuxer->reset();\r
381           demuxer->seek();\r
382 \r
383           afeed.start();\r
384           vfeed.start();\r
385           subtitles->start();\r
386           tfeed.start();\r
387           \r
388           state = newState;\r
389           if (!video->independentAVStartUp()){\r
390                   videoStartup = true;\r
391                   threadSignalNoLock();\r
392           }\r
393           return;\r
394         }\r
395         default:\r
396         {\r
397           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
398           abort();\r
399           break;\r
400         }\r
401       }\r
402     }\r
403 \r
404     case S_VIDEOSTARTUP:     // FROM S_VIDEOSTARTUP\r
405     {\r
406       switch(newState)\r
407       {\r
408         case S_PREBUFFERING:\r
409         {\r
410           pendingAudioPlay=false;\r
411           vfeed.release();\r
412           state = newState;\r
413           return;\r
414         }\r
415         \r
416         case S_VIDEOSTARTUP:\r
417         {\r
418 \r
419           vdr->stopStreaming();\r
420           clearStreamChunks(); \r
421           vfeed.stop();\r
422           afeed.stop();\r
423           subtitles->stop();\r
424           tfeed.stop();\r
425                            \r
426           video->blank();\r
427           video->reset();\r
428           //video->sync();\r
429           //video->play();\r
430           //video->pause();\r
431           audio->stop();\r
432           audio->unPause();\r
433           audio->reset();\r
434           //audio->setStreamType(Audio::MPEG2_PES);\r
435           //audio->sync();\r
436           pendingAudioPlay = true;\r
437           //audio->play();\r
438           //audio->pause();\r
439 \r
440           demuxer->reset();\r
441           demuxer->seek();\r
442 \r
443           afeed.start();\r
444           vfeed.start();\r
445           subtitles->start();     \r
446           tfeed.start();\r
447           state = newState;\r
448           if (!video->independentAVStartUp()){\r
449               videoStartup = true;\r
450               threadSignalNoLock();\r
451           }\r
452           return;\r
453         }        \r
454         case S_STOP:\r
455         { \r
456           vdr->stopStreaming();\r
457           pendingAudioPlay=false;\r
458           clearStreamChunks();\r
459           vfeed.stop();\r
460           afeed.stop();\r
461           subtitles->stop();\r
462           tfeed.stop();\r
463           video->stop();\r
464           video->blank();\r
465           audio->stop();\r
466           audio->reset();\r
467           video->reset();\r
468           state = newState;\r
469           return;\r
470         }\r
471         default:\r
472         {\r
473           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
474           abort();\r
475           break;\r
476         }        \r
477       }\r
478     }\r
479     \r
480     case S_PREBUFFERING:    // FROM S_PREBUFFERING\r
481     {\r
482       switch(newState)\r
483       {\r
484         case S_PLAY:\r
485         {\r
486           pendingAudioPlay=false;\r
487           audio->unPause();\r
488           video->unPause();\r
489           state = newState;\r
490           return;\r
491         }\r
492         case S_VIDEOSTARTUP:\r
493         {\r
494           vdr->stopStreaming();\r
495           clearStreamChunks();\r
496           vfeed.stop();\r
497           afeed.stop();\r
498           subtitles->stop();\r
499           tfeed.stop();\r
500           video->stop();\r
501           video->blank();\r
502           audio->stop();\r
503           audio->unPause();\r
504           audio->reset();\r
505 \r
506           video->reset();\r
507           //video->sync();\r
508           //video->play();\r
509           //video->pause();\r
510 \r
511           //audio->setStreamType(Audio::MPEG2_PES);\r
512           //audio->sync();\r
513           pendingAudioPlay = true;\r
514           //audio->play();\r
515           //audio->pause();\r
516 \r
517           demuxer->reset();\r
518           demuxer->seek();\r
519 \r
520           afeed.start();\r
521           vfeed.start();\r
522           subtitles->start();\r
523           tfeed.start();\r
524 \r
525           state = newState;\r
526           return;\r
527         }\r
528         case S_STOP:\r
529         {\r
530           pendingAudioPlay=false;\r
531           vdr->stopStreaming();\r
532           clearStreamChunks();\r
533           vfeed.stop();\r
534           afeed.stop();\r
535           subtitles->stop();\r
536           tfeed.stop();\r
537           video->stop();\r
538           video->blank();\r
539           audio->stop();\r
540           audio->reset();\r
541           video->reset();\r
542           state = newState;\r
543           return;        \r
544         }\r
545         default:\r
546         {\r
547           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
548           abort();\r
549           break;\r
550         }        \r
551       }\r
552     }\r
553     \r
554     case S_PLAY:     // FROM S_PLAY\r
555     {\r
556       switch(newState)\r
557       {\r
558         case S_STOP:\r
559         { \r
560           pendingAudioPlay=false;\r
561           vdr->stopStreaming();\r
562           clearStreamChunks();\r
563           vfeed.stop();\r
564           afeed.stop();\r
565           subtitles->stop();\r
566           tfeed.stop();\r
567           video->stop();\r
568           video->blank();\r
569           audio->stop();\r
570           audio->reset();\r
571           video->reset();\r
572           state = newState;\r
573           return;\r
574         }\r
575         case S_VIDEOSTARTUP:\r
576         {\r
577           vdr->stopStreaming();\r
578           clearStreamChunks();\r
579           vfeed.stop();\r
580           afeed.stop();\r
581           subtitles->stop();\r
582           tfeed.stop();\r
583           video->stop();\r
584           video->blank();\r
585           audio->stop();\r
586           audio->unPause();\r
587           audio->reset();\r
588 \r
589           video->reset();\r
590           \r
591           //video->sync();\r
592          // video->play();\r
593           //video->pause();\r
594 \r
595           //audio->setStreamType(Audio::MPEG2_PES);\r
596           //audio->sync();\r
597           //audio->play();\r
598           //audio->pause();\r
599           pendingAudioPlay = true;\r
600           demuxer->reset();\r
601           demuxer->seek();\r
602 \r
603           afeed.start();\r
604           vfeed.start();\r
605           subtitles->start();\r
606           tfeed.start();\r
607           state = newState;\r
608           if (!video->independentAVStartUp()){\r
609                   videoStartup = true;\r
610               threadSignalNoLock();\r
611           }\r
612           return;\r
613         }\r
614         default:\r
615         {\r
616           logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
617           abort();\r
618           break;\r
619         }        \r
620       }\r
621     }    \r
622   }  \r
623 }\r
624 \r
625 bool PlayerLiveTV::checkError()\r
626 {\r
627   if (!vdr->isConnected())\r
628   {\r
629     if (state != S_STOP) switchState(S_STOP);\r
630     \r
631     Message* m = new Message();\r
632     m->from = this;\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
637     \r
638     return true;\r
639   }   \r
640   return false;\r
641 }\r
642 \r
643 void PlayerLiveTV::optimizeInstructionQueue()\r
644 {\r
645   // Walk the list\r
646   \r
647   // Currently there are only 2 instruction types, so this is a bit overkill...\r
648 \r
649   struct PLInstruction i;\r
650   while(instructions.size() > 1)\r
651   {\r
652     i = instructions.front();\r
653     if (i.instruction == I_SETCHANNEL)\r
654     {\r
655       instructions.pop();  // if this is the first of more than 1 command, currently it cannot possibly be relevant\r
656     }\r
657     else if (i.instruction == I_STOP)\r
658     {\r
659       return; // return here and ensure the next instruction will be stop\r
660     }\r
661   }\r
662 }\r
663 \r
664 void PlayerLiveTV::threadMethod()\r
665 {\r
666   while(1)\r
667   {\r
668 \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
671     {\r
672            logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");\r
673       switchState(S_PREBUFFERING);\r
674       videoStartup = false;\r
675       preBufferCount = 0;\r
676       \r
677       checkError();\r
678     }  \r
679   \r
680     while(!instructions.empty())\r
681     {\r
682       if (instructions.size() > 1) optimizeInstructionQueue();\r
683 \r
684       struct PLInstruction i = instructions.front();\r
685       instructions.pop();\r
686     \r
687       if (i.instruction == I_SETCHANNEL)\r
688       {\r
689         logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");\r
690        \r
691         \r
692         switchState(S_VIDEOSTARTUP);\r
693         \r
694         if (!checkError())\r
695         {\r
696            Channel* chan = (*chanList)[i.channelIndex];\r
697            chan->loadPids();\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
703 \r
704           if (chan->numAPids > 0) \r
705           {\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
709           }\r
710           else \r
711           {\r
712               if (chan->numDPids > 0  && audio->maysupportAc3()) \r
713               {\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
716               } \r
717               else\r
718               {\r
719                   logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);\r
720               }\r
721           }\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
728           {      \r
729             Message* m = new Message();\r
730             m->from = this;\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
735           }\r
736         }\r
737       }\r
738       else if (i.instruction == I_STOP)\r
739       {\r
740         logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");\r
741         switchState(S_STOP);\r
742         checkError();\r
743 \r
744         stopNow = true;\r
745         break;\r
746       }\r
747     }\r
748 \r
749         threadCheckExit();\r
750 \r
751     if (stopNow) break;\r
752 \r
753     while(streamChunks.size())\r
754     {\r
755       chunkToDemuxer();\r
756 \r
757       if (state == S_PREBUFFERING)\r
758       {\r
759         ++preBufferCount;\r
760         ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);\r
761         logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);\r
762         \r
763         Message* m = new Message();\r
764         m->from = this;\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
770 \r
771         if (preBufferCount == preBufferAmount)\r
772         {\r
773           switchState(S_PLAY);\r
774           checkError();\r
775         }\r
776       }\r
777     }\r
778     threadLock();\r
779     threadWaitForSignal(); // unlocks and waits for signal\r
780     threadUnlock();\r
781   }\r
782 \r
783   logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");\r
784 }\r
785 \r