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