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