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