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