]> git.vomp.tv Git - vompclient.git/blob - osdvector.cc
Implement Log trace-only mode
[vompclient.git] / osdvector.cc
1 /*
2     Copyright 2012 Marten Richter, 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 "log.h"
21 #include "surfacevector.h"
22 #include "vdr.h"
23 #include "vdrresponsepacket.h"
24 #include "control.h"
25 #include "message.h"
26
27 #include "osdvector.h"
28
29 // The next section is activated, if the magick++ PictureReader is provided, it should be available for many POSIX platforms
30 #ifdef PICTURE_DECODER_MAGICK
31 #define MAGICKCORE_QUANTUM_DEPTH 16
32 #define MAGICKCORE_HDRI_ENABLE 0
33 #include <Magick++.h>
34
35 using namespace Magick;
36
37 static const char* TAG = "OsdVector";
38
39 class MagickDecoder: public OsdVector::PictureDecoder
40 {
41   public:
42     MagickDecoder(OsdVector::PictureReader* treader): OsdVector::PictureDecoder(treader) {pictInfValid = false;};
43
44     unsigned char* decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem);
45
46     bool getDecodedPicture( struct OsdVector::PictureInfo& pict_inf);
47
48     void freeReference(void* ref);
49
50   protected:
51     OsdVector::PictureInfo pictInf;
52     bool pictInfValid;
53 };
54
55 unsigned char* MagickDecoder::decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem)
56 {
57   if (pictInfValid) return buffer; // does support only one image at a Time;
58
59   Image magicimage;
60   Blob* imageblob = new Blob();
61   Blob myblob;
62
63   try
64   {
65     LogNT::getInstance()->debug("MagickDecoder", "decodePicture");
66
67     if (freemem) myblob.updateNoCopy(buffer, length, Blob::MallocAllocator);
68     else myblob.update(buffer, length);
69
70     magicimage.read(myblob);
71
72     magicimage.write(imageblob, "RGBA");
73
74   }
75   catch ( Exception& error_ )
76   {
77     LogNT::getInstance()->debug("MagickDecoder", "Libmagick: {}", error_.what());
78     delete imageblob;
79     LogNT::getInstance()->debug("MagickDecoder", "Libmagick: error mark2");
80     unsigned char* newbuffer = (unsigned char*) malloc(length);
81     memcpy(newbuffer, myblob.data(), length);
82     return newbuffer;
83   }
84
85   pictInf.reference = (void*) imageblob;
86   pictInf.width = magicimage.columns();
87   pictInf.height = magicimage.rows();
88   pictInf.image = imageblob->data();
89   pictInf.decoder = this;
90   pictInf.type = OsdVector::PictureInfo::RGBAMemBlock;
91   pictInf.lindex = index;
92   pictInfValid = true;
93
94
95
96   // I can handle everything, so the return value is always true
97   return NULL;
98 }
99 void MagickDecoder::freeReference(void* ref)
100 {
101   Blob* todelete = (Blob*) ref;
102   delete todelete;
103 }
104
105 bool MagickDecoder::getDecodedPicture(struct OsdVector::PictureInfo& pict_inf)
106 {
107   if (!pictInfValid) return false;
108
109   pict_inf = pictInf;
110   pictInfValid = false;
111   return true;
112 }
113
114
115 #endif
116
117 OsdVector::OsdVector()
118 {
119   logger = LogNT::getInstance();
120
121   setlocale(LC_CTYPE, "C.UTF-8");
122 #ifdef PICTURE_DECODER_MAGICK
123   reader.addDecoder(new MagickDecoder(&reader));
124 #endif
125   MessageQueue::getInstance()->addReceiver(this);
126 }
127
128 OsdVector::~OsdVector()
129 {
130   MessageQueue::getInstance()->removeReceiver(this);
131 }
132
133 bool OsdVector::screenShot(const char* fileName)
134 {
135   //Do nothing, if no libmagick is there
136 #ifdef PICTURE_DECODER_MAGICK
137   int width, height;
138   getRealScreenSize(width, height);
139   size_t length = width * height * 4;
140   void* mem = malloc(length);
141
142   try
143   {
144     Blob myblob;
145
146     if (!screenShotInternal(mem, width, height, true))
147     {
148       logger->debug(TAG, "Screenshot failed!");
149       free(mem);
150       return false;
151     }
152
153     myblob.updateNoCopy(mem, length, Blob::MallocAllocator);
154     Image image(myblob, Geometry(width, height), 8, "RGBA");
155     image.write(fileName);
156   }
157   catch ( Exception& error_ )
158   {
159     logger->debug("MagickEncoder", "Libmagick: {}", error_.what());
160     return false;
161   }
162
163   return true;
164 #endif
165   return false;
166 }
167
168 Surface* OsdVector::createNewSurface()
169 {
170   return new SurfaceVector(this);
171 }
172
173 void OsdVector::Blank()
174 {
175   // FIXME do nothing? remove this one?
176 }
177
178 int OsdVector::restore()
179 {
180   // First clear the contents of all registered surfaces
181   surfaces_mutex.lock();
182
183   //Now go through all surfaces and clear them
184   for (auto& surface : surfaces)
185   {
186     surface.commands.clear();
187     surface.commands.reserve(2048);
188   }
189
190   //also clear all handles, they are now invalid, no need to release them
191   vhi_refcounts.clear();
192   monobitmaps.clear();
193   //jpegs.clear();
194   drawstyleHandles.clear();
195   drawstyleHandlesRefCounts.clear();
196   drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
197   palettepics.clear();
198
199   tvmedias.clear();
200   loadindex_ref.clear();
201   tvmedias_load.clear();
202   tvmedias_load_inv.clear();
203   tvmedias_loaded.clear();
204
205   surfaces_mutex.unlock();
206   return 1;
207 }
208
209 void OsdVector::drawSurfaces()
210 {
211   surfaces_mutex.lock();
212   std::list<SurfaceInfo*> todraw; //First figure out if a surfaces is below another surface
213   std::list<SurfaceInfo>::iterator itty1 = surfaces.begin();
214
215   while (itty1 != surfaces.end())
216   {
217     std::list<SurfaceInfo>::iterator itty2 = itty1;
218     itty2++;
219     bool hidden = false;
220
221     while (itty2 != surfaces.end())
222     {
223       SurfaceInfo& ref1 = *itty1;
224       SurfaceInfo& ref2 = *itty2;
225
226       if (ref1.x >= ref2.x && ref1.y >= ref2.y
227           && (ref1.x + ref1.w) <= (ref2.x + ref2.w)
228           && (ref1.y + ref1.h) <= (ref2.y + ref2.h) )
229       {
230         hidden = true;
231         break;
232       }
233
234       itty2++;
235     }
236
237     if (!hidden)   // we are not hidden, perfect
238     {
239       todraw.push_back(&(*itty1));
240     }
241
242     itty1++;
243   }
244
245   int swidth, sheight;
246   getScreenSize(swidth, sheight);
247   //Now go through all surfaces and draw them
248   std::list<SurfaceInfo*>::iterator curdraw = todraw.begin();
249
250   while (curdraw != todraw.end())
251   {
252     drawSetTrans(*(*curdraw));
253     std::vector<SVGCommand>::iterator commands = (*(*curdraw)).commands.begin();
254     std::vector<SVGCommand>::iterator end = (*(*curdraw)).commands.end();
255
256     while (commands != end)
257     {
258       // update any images loaded in the mean time
259       if ((*commands).instr == DrawImageLoading)
260       {
261         LoadingIndex loadindex = (*commands).target.loadindex;
262
263         if (tvmedias_loaded.find(loadindex) != tvmedias_loaded.end())
264         {
265           (*commands).instr = DrawImage;
266           (*commands).target.image = tvmedias_loaded[loadindex];;
267           incImageRef((*commands).target.image);
268           removeLoadingIndexRef(loadindex);
269         }
270
271       }
272
273       // Now check if the command is on screen!
274       if  (!(*commands).Outside(0, 0, swidth, sheight))
275       {
276         executeDrawCommand(*commands);
277       }
278
279       commands++;
280     }
281
282     curdraw++;
283   }
284
285   surfaces_mutex.unlock();
286 }
287
288 void OsdVector::updateOrAddSurface(const SurfaceVector* surf, float x, float y, float height, float width, std::vector<SVGCommand>& commands)
289 {
290   std::lock_guard<std::mutex> lg(surfaces_mutex);
291   SurfacesIterator si;
292
293   #if DEV
294   logger->trace(TAG, "updateOrAddSurface, surfaces.length {}", surfaces.size());
295   dumpStyles();
296   #endif
297
298   // First determine it is already in our system
299   for(si = surfaces.begin(); si != surfaces.end(); si++)
300   {
301     if (si->surface == surf)
302     {
303       // If the surface given (surf) is already in our surfaces vector, reset our SurfaceInfo->commands vector
304       decrementAllRefCounts(si->commands);
305       si->commands.clear();
306       break;
307     }
308   }
309
310   // if not found, make a new SurfaceInfo
311   if (si == surfaces.end())
312   {
313     SurfaceInfo new_sc;
314     new_sc.surface = surf;
315     new_sc.x = x;
316     new_sc.y = y;
317     new_sc.w = width;
318     new_sc.h = height;
319     si = surfaces.insert(si, new_sc);
320   }
321
322   // update any images loaded in the mean time
323
324   for (SVGCommand& command : commands)
325   {
326     if (command.instr == DrawImageLoading)
327     {
328       LoadingIndex loadindex = command.target.loadindex;
329
330       if (tvmedias_loaded.find(loadindex) != tvmedias_loaded.end())
331       {
332         command.instr = DrawImage;
333         command.target.image = tvmedias_loaded[loadindex];
334         incImageRef(command.target.image);
335         removeLoadingIndexRef(loadindex);
336       }
337     }
338   }
339
340   si->commands = commands; // Copy surf->commands to our SurfaceInfo->commands
341   incrementAllRefCounts(si->commands);
342
343   cleanupOrphanedRefs();
344
345   #if DEV
346   logger->trace(TAG, "After UOAS:");
347   dumpStyles();
348   #endif
349
350   // OSDOVG-ROD-EXPERIMENT
351   logger->trace(TAG, "EXPERIMENT - call doRender");
352   doRender();
353 }
354
355 void OsdVector::removeSurface(const SurfaceVector* surf)
356 {
357   std::lock_guard<std::mutex> lg(surfaces_mutex);                      // FIXME - Can block here on shutdown if a timer is fired just as the wrong time
358         logger->trace("OsdVector-348", "EXPERIMENT - removeSurface");
359   for (auto i = surfaces.begin(); i != surfaces.end(); i++)
360   {
361     if (i->surface == surf)
362     {
363       decrementAllRefCounts(i->commands);
364       i->commands.clear();
365       surfaces.erase(i);
366       return;
367     }
368   }
369 }
370
371 void OsdVector::decrementAllRefCounts(std::vector<SVGCommand>& commands)
372 {
373   for (SVGCommand& command : commands)
374   {
375     VectorHandle handle = command.getHandle();
376     if (handle != 0) // command might not have a handle
377       decrementDrawStyleHandleRefCount(handle);
378
379     VectorHandleImage imageHandle = command.getImageHandle();
380     if (imageHandle) removeImageRef(imageHandle);
381
382     LoadingIndex li = command.getLoadingIndex();
383     if (li) removeLoadingIndexRef(li);
384   }
385 }
386
387 void OsdVector::incrementAllRefCounts(std::vector<SVGCommand>& commands)
388 {
389   for (SVGCommand& command : commands)
390   {
391     VectorHandle handle = command.getHandle();
392     if (handle != 0) // command might not have a handle
393       incrementDrawStyleHandleRefCount(handle);
394
395     VectorHandleImage imageHandle = command.getImageHandle();
396     if (imageHandle) incImageRef(imageHandle);
397
398     LoadingIndex li = command.getLoadingIndex();
399     if (li) incLoadingIndexRef(li);
400   }
401 }
402
403 void OsdVector::incImageRef(VectorHandleImage handle)
404 {
405   if (vhi_refcounts.find(handle) == vhi_refcounts.end())
406   {
407     vhi_refcounts[handle] = 1;
408   }
409   else
410   {
411     vhi_refcounts[handle]++;
412   }
413 }
414
415 void OsdVector::removeImageRef(const VectorHandleImage handle)
416 {
417   vhi_refcounts[handle]--;
418 }
419
420 int OsdVector::getLoadingIndexRef(LoadingIndex index)
421 {
422   surfaces_mutex.lock();
423
424   if (loadindex_ref.find(index) == loadindex_ref.end())
425   {
426     return -1;
427   }
428   else
429   {
430     return loadindex_ref[index];
431   }
432
433   surfaces_mutex.unlock();
434 }
435
436 void OsdVector::incLoadingIndexRef(LoadingIndex index)
437 {
438   if (loadindex_ref.find(index) == loadindex_ref.end())
439   {
440     loadindex_ref[index] = 1;
441   }
442   else
443   {
444     loadindex_ref[index]++;
445   }
446 }
447
448 void OsdVector::removeLoadingIndexRef(const LoadingIndex ref)
449 {
450   loadindex_ref[ref]--;
451
452   if (loadindex_ref[ref] == 0)
453   {
454     //now check, if it is already loaded
455     std::map<LoadingIndex, VectorHandleImage>::iterator itty = tvmedias_loaded.find(ref);
456
457     if ( itty != tvmedias_loaded.end())
458     {
459       // removeImageRef((*itty).second); // remove lock
460       /*
461        * I'm not sure exactly how all this works but removing this line of code prevents
462        * reference counts in vhi_refcounts from going negative. Therefore I suspect the above
463        * line is wrong. Will test for a while.
464        */
465     }
466
467     tvmedias_loaded.erase(ref);
468     //          logger->debug(TAG, "TVMedia removeLoadIndexRef {} {:#x}",tvmedias_load.size(),ref);
469     tvmedias_load.erase(tvmedias_load_inv[ref]);
470     tvmedias_load_inv.erase(ref);
471
472     reader.invalidateLoadingIndex(ref);
473   }
474 }
475
476 void OsdVector::cleanupOrphanedRefs()
477 {
478   // Do some garbage collection
479
480   std::map<void*, VectorHandleImage>::iterator mitty = monobitmaps.begin();
481
482   while (mitty != monobitmaps.end())
483   {
484     std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*mitty).second);
485     int count = (*curitty).second;
486
487     if (count == 0)
488     {
489       VectorHandleImage handle = (*curitty).first;
490       monobitmaps.erase(mitty++);
491       vhi_refcounts.erase(curitty++);
492       destroyImageRef(handle);
493     }
494     else ++mitty;
495   }
496
497   /*map<string,VectorHandleImage>::iterator jitty=jpegs.begin();
498   while (jitty!=jpegs.end()) {
499         map<VectorHandleImage,int>::iterator curitty=vhi_refcounts.find((*jitty).second);
500         int count=(*curitty).second;
501         if (count==0) {
502                 VectorHandleImage handle=(*curitty).first;
503                 jpegs.erase(jitty++);
504                 vhi_refcounts.erase(curitty++);
505                 destroyImageRef(handle);
506         } else ++jitty;
507   }*/
508
509   std::map<TVMediaInfo, VectorHandleImage>::iterator titty = tvmedias.begin();
510
511   while (titty != tvmedias.end())
512   {
513     std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*titty).second);
514     int count = (*curitty).second;
515
516     if (count == 0)
517     {
518       VectorHandleImage handle = (*curitty).first;
519       tvmedias.erase(titty++);
520       vhi_refcounts.erase(curitty);
521       destroyImageRef(handle);
522     }
523     else ++titty;
524   }
525
526
527   std::map<TVMediaInfo, LoadingIndex>::iterator litty = tvmedias_load.begin();
528
529   while (litty != tvmedias_load.end())
530   {
531     std::map<LoadingIndex, int>::iterator curitty = loadindex_ref.find((*litty).second);
532     int count = (*curitty).second;
533
534     if (count == 0)
535     {
536       tvmedias_load_inv.erase((*litty).second);
537       tvmedias_loaded.erase((*litty).second);
538       tvmedias_load.erase(litty++);
539     }
540     else ++litty;
541   }
542
543   std::list<VectorHandleImage>::iterator pitty = palettepics.begin();
544
545   while (pitty != palettepics.end())
546   {
547     std::map<VectorHandleImage, int>::iterator curitty = vhi_refcounts.find((*pitty));
548     int count = (*curitty).second;
549
550     if (count == 0)
551     {
552       VectorHandleImage handle = (*curitty).first;
553       palettepics.erase(pitty++);
554       vhi_refcounts.erase(curitty++);
555       destroyImageRef(handle);
556     }
557     else ++pitty;
558   }
559
560   std::map<VectorHandleImage, int>::iterator citty = vhi_refcounts.begin();
561
562   while (citty != vhi_refcounts.end())
563   {
564     int count = (*citty).second;
565
566     if (count == 0)
567     {
568       VectorHandleImage handle = (*citty).first;
569       vhi_refcounts.erase(citty++);
570       destroyImageRef(handle);
571     }
572     else ++citty;
573   }
574
575
576   std::map<DrawStyle, VectorHandle>::iterator sitty = drawstyleHandles.begin();
577
578   while (sitty != drawstyleHandles.end())
579   {
580     std::map<VectorHandle, int>::iterator curitty = drawstyleHandlesRefCounts.find((*sitty).second);
581     int count = (*curitty).second;
582
583     if (count == 0)
584     {
585       VectorHandle ref = (*curitty).first;
586       drawstyleHandles.erase(sitty++);
587       drawstyleHandlesRefCounts.erase(curitty++);
588       drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
589       destroyDrawStyleHandle(ref);
590
591     }
592     else ++sitty;
593
594   }
595 }
596
597 //int OsdVector::getImageRef(VectorHandleImage handle)
598 //{
599 //  surfaces_mutex.lock();
600 //
601 //  if (vhi_refcounts.find(handle) == vhi_refcounts.end())
602 //  {
603 //    return -1;
604 //  }
605 //  else
606 //  {
607 //    return vhi_refcounts[handle];
608 //  }
609 //
610 //  surfaces_mutex.unlock();
611 //}
612
613 void OsdVector::incrementDrawStyleHandleRefCount(VectorHandle index)
614 {
615   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
616   {
617     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
618     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
619     {
620       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.insert(std::pair<VectorHandle, int>(index, 0)).first;
621     }
622   }
623
624   drawstyleHandlesRefCounts_lastit->second++;
625   drawstyleHandlesRefCounts_lastit_valid = true;
626 }
627
628 void OsdVector::decrementDrawStyleHandleRefCount(VectorHandle index)
629 {
630   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
631   {
632     drawstyleHandlesRefCounts_lastit_valid = false;
633     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
634   }
635
636   if (drawstyleHandlesRefCounts_lastit != drawstyleHandlesRefCounts.end())
637   {
638     drawstyleHandlesRefCounts_lastit_valid = true;
639     drawstyleHandlesRefCounts_lastit->second--;
640   }
641 }
642
643 VectorHandle OsdVector::getDrawStyleHandle(const DrawStyle& c)
644 {
645   surfaces_mutex.lock();
646   VectorHandle style_handle = 0;
647
648   #if DEV
649   dumpStyles();
650   #endif
651
652   if (!drawstyleHandles_lastit_valid || (drawstyleHandles_lastit->first != c))
653   {
654     drawstyleHandles_lastit_valid = false;
655     drawstyleHandles_lastit = drawstyleHandles.find(c);
656   }
657
658   if (drawstyleHandles_lastit == drawstyleHandles.end())
659   {
660     surfaces_mutex.unlock();
661     style_handle = createDrawStyleHandle(c);
662     surfaces_mutex.lock();
663     drawstyleHandles_lastit = drawstyleHandles.insert(std::pair<DrawStyle, VectorHandle>(c, style_handle)).first;
664   }
665   else
666   {
667     style_handle = drawstyleHandles_lastit->second;
668
669     //Now check if the handle is valid
670     if (!drawstyleHandlesRefCounts_lastit_valid || (*drawstyleHandlesRefCounts_lastit).first != style_handle)
671     {
672       drawstyleHandlesRefCounts_lastit_valid = false;
673       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(style_handle);
674     }
675
676     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
677     {
678       //invalid handle recreate
679       surfaces_mutex.unlock();
680       style_handle = createDrawStyleHandle(c);
681       surfaces_mutex.lock();
682       drawstyleHandles_lastit->second = style_handle;
683     }
684     else drawstyleHandlesRefCounts_lastit_valid = true;
685   }
686
687   drawstyleHandles_lastit_valid = true;
688   incrementDrawStyleHandleRefCount(style_handle);
689   surfaces_mutex.unlock();
690   return style_handle;
691 }
692
693 #if DEV
694 void OsdVector::dumpStyles()
695 {
696   return;
697
698   std::map<DrawStyle, VectorHandle>::iterator i;
699   for(i = drawstyleHandles.begin(); i != drawstyleHandles.end(); i++)
700   {
701     const DrawStyle* test = &(i->first);
702     logger->debug(TAG, "DumpStyles: {} {}", (void*)test , i->second);
703   }
704
705   std::map<VectorHandle, int>::iterator i2;
706   for (i2 = drawstyleHandlesRefCounts.begin(); i2 != drawstyleHandlesRefCounts.end(); i2++)
707   {
708     logger->debug(TAG, "DumpStylesRef: {} {}", i2->first, i2->second);
709   }
710 }
711 #endif
712
713 LoadingIndex OsdVector::getTVMediaRef(TVMediaInfo& tvmedia, VectorHandleImage& handle)
714 {
715   VectorHandleImage localHandle = 0;
716   LoadingIndex loadindex = 0;
717   surfaces_mutex.lock();
718
719   if (tvmedias.find(tvmedia) == tvmedias.end())   // if not found, return a loadIndex from loadTVMedia(tvmedia)
720   {
721     loadindex = loadTVMedia(tvmedia);
722   }
723   else   // if found, return a real imageIndex. incImageRef(localHandle). EXCEPT: If it's not found in vhi_refcounts (???) in which case do same as above.
724   {
725     localHandle = tvmedias[tvmedia];
726
727     if (vhi_refcounts.find(localHandle) == vhi_refcounts.end())
728     {
729       //invalid handle recreate
730       loadindex = loadTVMedia(tvmedia);
731       localHandle = 0;
732     }
733     else
734     {
735       incImageRef(localHandle);
736     }
737   }
738
739   /*tvmedias[tvmedia]=createTVMedia(tvmedia,width,height);
740   incImageRef(localHandle);*/
741   handle = localHandle;
742   surfaces_mutex.unlock();
743   return loadindex;
744 }
745
746 LoadingIndex OsdVector::loadTVMedia(TVMediaInfo& tvmedia)          // insert a tvmedia for loading
747 {
748   LoadingIndex index = 0;
749
750   if (tvmedias_load.find(tvmedia) == tvmedias_load.end())        // if not found in tvmedias_load
751   {
752     switch (tvmedia.getType())
753     {
754       case 3:
755         index = VDR::getInstance()->loadTVMediaRecThumb(tvmedia);
756         break;
757
758       case 4:
759       {
760         index = ((long long) tvmedia.getPrimaryID()) << 32LL;
761         reader.addStaticImage(tvmedia.getPrimaryID());
762       } break;
763
764       case 5:
765         index = VDR::getInstance()->loadTVMediaEventThumb(tvmedia);
766         break;
767
768       case 6:
769         index = VDR::getInstance()->loadChannelLogo(tvmedia);
770         break;
771
772       default:
773         index = VDR::getInstance()->loadTVMedia(tvmedia);
774         break;
775     }
776
777     if (tvmedia.getType() != 4 && tvmedia.getStaticFallback() > -1)
778     {
779       reader.informFallback(index, tvmedia.getStaticFallback());
780     }
781
782     tvmedias_load[tvmedia] = index;
783     tvmedias_load_inv[index] = tvmedia;
784   }
785   else
786   {
787     index = tvmedias_load[tvmedia];
788   }
789
790   incLoadingIndexRef(index);
791
792   return index;
793 }
794
795 void OsdVector::informPicture(LoadingIndex index, VectorHandleImage handle)
796 {
797   //Beware for thread safety
798   VectorHandleImage localHandle = 0;
799
800   logger->debug(TAG, "TVMedia Picture for load-id {:#x} arrived index {:#x}", index, handle);
801   surfaces_mutex.lock();
802   TVMediaInfo tvmedia = tvmedias_load_inv[index];
803
804   if (handle)
805   {
806     std::map<LoadingIndex, int>::iterator itty = loadindex_ref.find(index);
807     localHandle = tvmedias[tvmedia] = handle;
808     tvmedias_loaded[index] = localHandle;
809
810     if (itty == loadindex_ref.end() || (*itty).second == 0)
811     {
812       // we do not want the picture anymore . Really...
813       // fill vhi_refcounts in to not irritate the garbage collector
814       if (vhi_refcounts.find(localHandle) == vhi_refcounts.end())
815       {
816         vhi_refcounts[localHandle] = 0;
817       }
818     }
819     else
820     {
821       incImageRef(localHandle); // hold one index until all loadings refs are gone;
822     }
823   }
824
825   surfaces_mutex.unlock();
826
827   // OSDOVG-ROD-EXPERIMENT
828   logger->trace(TAG, "EXPERIMENT - call doRender");
829   doRender();
830 }
831
832 void OsdVector::processMessage(Message* m)
833 {
834   switch(m->message)
835   {
836     case Message::NEW_PICTURE:
837     {
838       //logger->debug(TAG, "TVMedia NEW_PICTURE");
839       informPicture(m->tag, reinterpret_cast<VectorHandleImage>(m->data));
840       break;
841     }
842     case Message::NEW_PICTURE_STATIC:
843     {
844       //logger->debug(TAG, "TVMedia NEW_PICTURE {:#x} {:#x}", m->tag, m->parameter);
845       informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<VectorHandleImage>(m->data));
846       break;
847     }
848   }
849 }
850
851 /*
852 VectorHandleImage OsdVector::getJpegRef(const char* fileName, int *width,int *height)
853 {
854         VectorHandleImage image_handle=0;
855         if (jpegs.find(fileName)==jpegs.end())
856         {
857                 image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
858         } else {
859                 image_handle=jpegs[fileName];
860                 *width=0;
861                 *height=0;
862                 if (vhi_refcounts.find(image_handle)==vhi_refcounts.end()) {
863                         //invalid handle recreate
864                         image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
865                 }
866         }
867         incImageRef(image_handle);
868         return image_handle;
869 }
870 */
871
872 VectorHandleImage OsdVector::getMonoBitmapRef(void* base, int width, int height)
873 {
874   VectorHandleImage image_handle;
875   surfaces_mutex.lock();
876
877   if (monobitmaps.find(base) == monobitmaps.end())
878   {
879     surfaces_mutex.unlock();
880     image_handle = createMonoBitmap(base, width, height);
881     surfaces_mutex.lock();
882     monobitmaps[base] = image_handle;
883   }
884   else
885   {
886     image_handle = monobitmaps[base];
887
888     if (vhi_refcounts.find(image_handle) == vhi_refcounts.end())
889     {
890       //invalid handle recreate
891       surfaces_mutex.unlock();
892       image_handle = createMonoBitmap(base, width, height);
893       surfaces_mutex.lock();
894       monobitmaps[base] = image_handle;
895     }
896   }
897
898   incImageRef(image_handle);
899   surfaces_mutex.unlock();
900   return image_handle;
901 }
902
903 VectorHandleImage OsdVector::getImagePalette(int width, int height, const unsigned char* image_data, const unsigned int* palette_data)
904 {
905   VectorHandleImage image_handle;
906   image_handle = createImagePalette(width, height, image_data, palette_data);
907   surfaces_mutex.lock();
908   palettepics.push_back(image_handle);
909   incImageRef(image_handle);
910   surfaces_mutex.unlock();
911   return image_handle;
912 }
913
914 /// PictureReader Class
915
916 OsdVector::PictureReader::~PictureReader()
917 {
918   threadMutex.lock();
919   if (readerThread.joinable())
920   {
921     threadReqQuit = true;
922     threadCond.notify_one();
923     threadMutex.unlock();
924     readerThread.join();
925   }
926   else
927     threadMutex.unlock();
928
929
930   decoders_lock.lock();
931   while (decoders.size())
932   {
933     PictureDecoder* dec = decoders.front();
934     decoders.pop_front();
935     delete dec;
936   }
937   decoders_lock.unlock();
938 }
939
940 void OsdVector::PictureReader::init()
941 {
942   threadMutex.lock();
943   readerThread = std::thread( [this]
944   {
945     threadMutex.lock();
946     threadMutex.unlock();
947     threadMethod();
948   });
949   threadMutex.unlock();
950 }
951
952 void OsdVector::PictureReader::shutdown()
953 {
954   std::unique_lock<std::mutex> lg(threadMutex);
955
956   if (readerThread.joinable())
957   {
958     threadReqQuit = true;
959     threadCond.notify_one();
960     lg.unlock();
961     readerThread.join();
962   }
963 }
964
965 void OsdVector::PictureReader::addDecoder(PictureDecoder* decoder)
966 {
967   decoders_lock.lock();
968   decoder->init();
969   decoders.push_front(decoder);
970   decoders_lock.unlock();
971 }
972
973 void OsdVector::PictureReader::removeDecoder(PictureDecoder* decoder)
974 {
975   decoders_lock.lock();
976   std::list<PictureDecoder*>::iterator itty = decoders.begin();
977
978   while (itty != decoders.end())
979   {
980     if ((*itty) == decoder)
981     {
982       decoders.erase(itty);
983       break;
984     }
985
986     itty++;
987   }
988
989   LogNT::getInstance()->debug(TAG, "removeDecoder");
990   decoder->shutdown();
991   delete decoder;
992   decoders_lock.unlock();
993 }
994
995 void OsdVector::PictureReader::threadMethod()
996 {
997   OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance());
998 //   LogNT::getInstance()->debug("PictureReader", "TVMedia Start Picture Reader");
999
1000   std::unique_lock<std::mutex> ul(threadMutex); // locked
1001
1002   while (true)
1003   {
1004     threadCond.wait(ul, [this]{ return threadReqQuit || runLoop; });
1005     // locked
1006     if (threadReqQuit) return; // unlock
1007 //     LogNT::getInstance()->debug("PictureReader", "Running loop");
1008     runLoop = false;
1009     ul.unlock();
1010
1011     // unlocked while we work
1012
1013     bool miniRunAgain;
1014     do
1015     {
1016       miniRunAgain = false;
1017
1018       PictureInfo pictinf;
1019       decoders_lock.lock();
1020       std::list<PictureDecoder*>::iterator itty = decoders.begin();
1021
1022       while (itty != decoders.end())
1023       {
1024         if ((*itty)->getDecodedPicture(pictinf))    // FIXME somewhere around here?, or in getDecodedPicture, try SA optimisation
1025         {
1026           miniRunAgain = true;
1027           osdvector->createPicture(pictinf);
1028         }
1029         itty++;
1030       }
1031
1032       if (processReceivedPictures()) miniRunAgain = true;
1033       decoders_lock.unlock();
1034
1035 //       LogNT::getInstance()->debug("PictureReader", "miniRunAgain = true...");
1036
1037     } while(miniRunAgain);
1038
1039     ul.lock();
1040   }
1041 }
1042
1043 void OsdVector::PictureReader::invalidateLoadingIndex(LoadingIndex index)
1044 {
1045   pict_lock_incoming.lock();
1046   invalid_loadindex.insert(index);
1047   pict_lock_incoming.unlock();
1048
1049   // From conversion to std::thread
1050   // The thread used to spin every 10ms, now it only waits on the cond
1051   // This method does not signal the main thread, does it need to now?
1052   // Doesn't look like it, but if it does get the 4 lines below
1053 }
1054
1055 void OsdVector::PictureReader::informFallback(LoadingIndex index, int fallback)
1056 {
1057   pict_lock_incoming.lock();
1058   inform_fallback[index] = fallback;
1059   pict_lock_incoming.unlock();
1060   // Also here, does this need to signal the thread?
1061 }
1062
1063 void OsdVector::PictureReader::receivePicture(VDR_ResponsePacket* vresp)
1064 {
1065   pict_lock_incoming.lock();
1066   pict_incoming.push(vresp);
1067   pict_lock_incoming.unlock();
1068
1069   // These 4 lines to signal the thread
1070   threadMutex.lock();
1071   runLoop = true;
1072   threadCond.notify_one();
1073   threadMutex.unlock();
1074 }
1075
1076 void OsdVector::PictureReader::addStaticImage(unsigned int id)
1077 {
1078   pict_lock_incoming.lock();
1079   pict_incoming_static.push(id);
1080   invalid_loadindex.erase(((long long) id) << 32LL);
1081   pict_lock_incoming.unlock();
1082
1083   threadMutex.lock();
1084   runLoop = true;
1085   threadCond.notify_one();
1086   threadMutex.unlock();
1087 }
1088
1089 #if WIN32
1090   // FIXME win pragma
1091   #pragma warning(disable : 4703)
1092 #endif
1093
1094 bool OsdVector::PictureReader::processReceivedPictures()
1095 {
1096   bool decoded = false;
1097   bool valid = true;
1098   pict_lock_incoming.lock();
1099
1100   if (pict_incoming.size())
1101   {
1102     VDR_ResponsePacket* vresp = pict_incoming.front();
1103     pict_incoming.pop();
1104     std::set<LoadingIndex>::iterator setpos = invalid_loadindex.find(vresp->getStreamID());
1105
1106     if (setpos != invalid_loadindex.end())
1107     {
1108       valid = false;
1109       invalid_loadindex.erase(setpos);
1110     }
1111
1112     pict_lock_incoming.unlock();
1113
1114     if (!valid)   // we do not want it anymore skip it;
1115     {
1116       delete vresp;
1117       return true;
1118     }
1119
1120     // LogNT::getInstance()->debug(TAG, "TVMedia Pictures arrived VDR {:#x} {} {}",
1121     // vresp->getStreamID(),vresp->getUserDataLength(),vresp->getFlag());
1122     bool decode = false;
1123     bool freed = false;
1124     UCHAR* userdata;
1125     ULONG length;
1126
1127     if (vresp->getFlag() != 2)
1128     {
1129       userdata = vresp->getUserData();
1130       length = vresp->getUserDataLength();
1131       decode = true;
1132       freed = true;
1133     }
1134     else
1135     {
1136       int fallback = -1;
1137       pict_lock_incoming.lock();
1138
1139       if (inform_fallback.find(vresp->getStreamID()) != inform_fallback.end())
1140       {
1141         fallback = inform_fallback[vresp->getStreamID()];
1142       }
1143
1144       pict_lock_incoming.unlock();
1145
1146       if (fallback >= 0 && ((OsdVector*)Osd::getInstance())->getStaticImageData(fallback, &userdata, &length))
1147       {
1148         decode = true;
1149         freed = false;
1150       }
1151     }
1152
1153     if (decode)
1154     {
1155       std::list<PictureDecoder*>::iterator itty = decoders.begin();
1156
1157       while (itty != decoders.end())
1158       {
1159         userdata = (*itty)->decodePicture(vresp->getStreamID(), userdata, length, freed);
1160
1161         if (!userdata)
1162         {
1163           decoded = true;
1164           break;
1165         }
1166
1167         itty++;
1168       }
1169
1170       if (!decoded && userdata && freed)
1171       {
1172         free(userdata);
1173       }
1174     }
1175
1176     pict_lock_incoming.lock();
1177     inform_fallback.erase(vresp->getStreamID());
1178     pict_lock_incoming.unlock();
1179     //else  osd->informPicture(vresp->getStreamID(), 0);
1180     delete vresp;
1181   }
1182   else if (pict_incoming_static.size())
1183   {
1184     unsigned int static_id = pict_incoming_static.front();
1185     pict_incoming_static.pop();
1186     std::set<LoadingIndex>::iterator setpos = invalid_loadindex.find(((long long) static_id) << 32LL);
1187
1188     if (setpos != invalid_loadindex.end())
1189     {
1190       valid = false;
1191       invalid_loadindex.erase(setpos);
1192     }
1193
1194     pict_lock_incoming.unlock();
1195
1196     if (!valid)   // we do not want it anymore skip it;
1197     {
1198       return true;
1199     }
1200
1201     UCHAR* userdata;
1202     ULONG length;
1203
1204     if (((OsdVector*)Osd::getInstance())->getStaticImageData(static_id, &userdata, &length))
1205     {
1206       std::list<PictureDecoder*>::iterator itty = decoders.begin();
1207
1208       while (itty != decoders.end())
1209       {
1210         if (!(*itty)->decodePicture(((long long) static_id) << 32LL, userdata, length, false))
1211         {
1212           decoded = true;
1213           break;
1214         }
1215
1216         itty++;
1217       }
1218     }
1219   }
1220   else
1221   {
1222     pict_lock_incoming.unlock();
1223   }
1224
1225   if (pict_incoming.size() || pict_incoming_static.size()) return true;
1226
1227   return decoded;
1228 }