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