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