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