]> git.vomp.tv Git - vompclient.git/blob - osdvector.cc
Log lines, comments
[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   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   // 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   images_ref.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         LoadIndex 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           removeLoadIndexRef(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       LoadIndex 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         removeLoadIndexRef(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     ImageIndex ii = command.getImageIndex();
380     if (ii) removeImageRef(ii);
381
382     LoadIndex li = command.getLoadIndex();
383     if (li) removeLoadIndexRef(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     ImageIndex ii = command.getImageIndex();
396     if (ii) incImageRef(ii);
397
398     LoadIndex li = command.getLoadIndex();
399     if (li) incLoadIndexRef(li);
400   }
401 }
402
403 void OsdVector::incImageRef(ImageIndex index)
404 {
405   if (images_ref.find(index) == images_ref.end())
406   {
407     images_ref[index] = 1;
408   }
409   else
410   {
411     images_ref[index]++;
412   }
413 }
414
415 void OsdVector::removeImageRef(const ImageIndex ref)
416 {
417   images_ref[ref]--;
418 }
419
420 int OsdVector::getLoadIndexRef(LoadIndex 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::incLoadIndexRef(LoadIndex 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::removeLoadIndexRef(const LoadIndex 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<LoadIndex, ImageIndex>::iterator itty = tvmedias_loaded.find(ref);
456
457     if ( itty != tvmedias_loaded.end())
458     {
459       removeImageRef((*itty).second); // remove lock
460     }
461
462     tvmedias_loaded.erase(ref);
463     //          logger->debug(TAG, "TVMedia removeLoadIndexRef {} {:#x}",tvmedias_load.size(),ref);
464     tvmedias_load.erase(tvmedias_load_inv[ref]);
465     tvmedias_load_inv.erase(ref);
466
467     reader.invalidateLoadIndex(ref);
468   }
469 }
470
471 void OsdVector::cleanupOrphanedRefs()
472 {
473   // Do some garbage collection
474
475   std::map<void*, ImageIndex>::iterator mitty = monobitmaps.begin();
476
477   while (mitty != monobitmaps.end())
478   {
479     std::map<ImageIndex, int>::iterator curitty = images_ref.find((*mitty).second);
480     int count = (*curitty).second;
481
482     if (count == 0)
483     {
484       ImageIndex ref = (*curitty).first;
485       monobitmaps.erase(mitty++);
486       images_ref.erase(curitty++);
487       destroyImageRef(ref);
488     }
489     else ++mitty;
490   }
491
492   /*map<string,ImageIndex>::iterator jitty=jpegs.begin();
493   while (jitty!=jpegs.end()) {
494         map<ImageIndex,int>::iterator curitty=images_ref.find((*jitty).second);
495         int count=(*curitty).second;
496         if (count==0) {
497                 ImageIndex ref=(*curitty).first;
498                 jpegs.erase(jitty++);
499                 images_ref.erase(curitty++);
500                 destroyImageRef(ref);
501         } else ++jitty;
502   }*/
503
504   std::map<TVMediaInfo, ImageIndex>::iterator titty = tvmedias.begin();
505
506   while (titty != tvmedias.end())
507   {
508     std::map<ImageIndex, int>::iterator curitty = images_ref.find((*titty).second);
509     int count = (*curitty).second;
510
511     if (count == 0)
512     {
513       ImageIndex ref = (*curitty).first;
514       tvmedias.erase(titty++);
515       images_ref.erase(curitty);
516       destroyImageRef(ref);
517     }
518     else ++titty;
519   }
520
521
522   std::map<TVMediaInfo, LoadIndex>::iterator litty = tvmedias_load.begin();
523
524   while (litty != tvmedias_load.end())
525   {
526     std::map<LoadIndex, int>::iterator curitty = loadindex_ref.find((*litty).second);
527     int count = (*curitty).second;
528
529     if (count == 0)
530     {
531       tvmedias_load_inv.erase((*litty).second);
532       tvmedias_loaded.erase((*litty).second);
533       tvmedias_load.erase(litty++);
534     }
535     else ++litty;
536   }
537
538   std::list<ImageIndex>::iterator pitty = palettepics.begin();
539
540   while (pitty != palettepics.end())
541   {
542     std::map<ImageIndex, int>::iterator curitty = images_ref.find((*pitty));
543     int count = (*curitty).second;
544
545     if (count == 0)
546     {
547       ImageIndex ref = (*curitty).first;
548       palettepics.erase(pitty++);
549       images_ref.erase(curitty++);
550       destroyImageRef(ref);
551     }
552     else ++pitty;
553   }
554
555   std::map<ImageIndex, int>::iterator citty = images_ref.begin();
556
557   while (citty != images_ref.end())
558   {
559     int count = (*citty).second;
560
561     if (count == 0)
562     {
563       ImageIndex ref = (*citty).first;
564       images_ref.erase(citty++);
565       destroyImageRef(ref);
566     }
567     else ++citty;
568   }
569
570
571   std::map<DrawStyle, VectorHandle>::iterator sitty = drawstyleHandles.begin();
572
573   while (sitty != drawstyleHandles.end())
574   {
575     std::map<VectorHandle, int>::iterator curitty = drawstyleHandlesRefCounts.find((*sitty).second);
576     int count = (*curitty).second;
577
578     if (count == 0)
579     {
580       VectorHandle ref = (*curitty).first;
581       drawstyleHandles.erase(sitty++);
582       drawstyleHandlesRefCounts.erase(curitty++);
583       drawstyleHandles_lastit_valid = drawstyleHandlesRefCounts_lastit_valid = false;
584       destroyDrawStyleHandle(ref);
585
586     }
587     else ++sitty;
588
589   }
590 }
591
592 int OsdVector::getImageRef(ImageIndex index)
593 {
594   surfaces_mutex.lock();
595
596   if (images_ref.find(index) == images_ref.end())
597   {
598     return -1;
599   }
600   else
601   {
602     return images_ref[index];
603   }
604
605   surfaces_mutex.unlock();
606 }
607
608 void OsdVector::incrementDrawStyleHandleRefCount(VectorHandle index)
609 {
610   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
611   {
612     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
613     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
614     {
615       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.insert(std::pair<VectorHandle, int>(index, 0)).first;
616     }
617   }
618
619   drawstyleHandlesRefCounts_lastit->second++;
620   drawstyleHandlesRefCounts_lastit_valid = true;
621 }
622
623 void OsdVector::decrementDrawStyleHandleRefCount(VectorHandle index)
624 {
625   if (!drawstyleHandlesRefCounts_lastit_valid || (drawstyleHandlesRefCounts_lastit->first != index))
626   {
627     drawstyleHandlesRefCounts_lastit_valid = false;
628     drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(index);
629   }
630
631   if (drawstyleHandlesRefCounts_lastit != drawstyleHandlesRefCounts.end())
632   {
633     drawstyleHandlesRefCounts_lastit_valid = true;
634     drawstyleHandlesRefCounts_lastit->second--;
635   }
636 }
637
638 VectorHandle OsdVector::getDrawStyleHandle(const DrawStyle& c)
639 {
640   surfaces_mutex.lock();
641   VectorHandle style_handle = 0;
642
643   #if DEV
644   dumpStyles();
645   #endif
646
647   if (!drawstyleHandles_lastit_valid || (drawstyleHandles_lastit->first != c))
648   {
649     drawstyleHandles_lastit_valid = false;
650     drawstyleHandles_lastit = drawstyleHandles.find(c);
651   }
652
653   if (drawstyleHandles_lastit == drawstyleHandles.end())
654   {
655     surfaces_mutex.unlock();
656     style_handle = createDrawStyleHandle(c);
657     surfaces_mutex.lock();
658     drawstyleHandles_lastit = drawstyleHandles.insert(std::pair<DrawStyle, VectorHandle>(c, style_handle)).first;
659   }
660   else
661   {
662     style_handle = drawstyleHandles_lastit->second;
663
664     //Now check if the handle is valid
665     if (!drawstyleHandlesRefCounts_lastit_valid || (*drawstyleHandlesRefCounts_lastit).first != style_handle)
666     {
667       drawstyleHandlesRefCounts_lastit_valid = false;
668       drawstyleHandlesRefCounts_lastit = drawstyleHandlesRefCounts.find(style_handle);
669     }
670
671     if (drawstyleHandlesRefCounts_lastit == drawstyleHandlesRefCounts.end())
672     {
673       //invalid handle recreate
674       surfaces_mutex.unlock();
675       style_handle = createDrawStyleHandle(c);
676       surfaces_mutex.lock();
677       drawstyleHandles_lastit->second = style_handle;
678     }
679     else drawstyleHandlesRefCounts_lastit_valid = true;
680   }
681
682   drawstyleHandles_lastit_valid = true;
683   incrementDrawStyleHandleRefCount(style_handle);
684   surfaces_mutex.unlock();
685   return style_handle;
686 }
687
688 #if DEV
689 void OsdVector::dumpStyles()
690 {
691   return;
692
693   std::map<DrawStyle, VectorHandle>::iterator i;
694   for(i = drawstyleHandles.begin(); i != drawstyleHandles.end(); i++)
695   {
696     const DrawStyle* test = &(i->first);
697     logger->debug(TAG, "DumpStyles: {} {}", (void*)test , i->second);
698   }
699
700   std::map<VectorHandle, int>::iterator i2;
701   for (i2 = drawstyleHandlesRefCounts.begin(); i2 != drawstyleHandlesRefCounts.end(); i2++)
702   {
703     logger->debug(TAG, "DumpStylesRef: {} {}", i2->first, i2->second);
704   }
705 }
706 #endif
707
708 LoadIndex OsdVector::getTVMediaRef(TVMediaInfo& tvmedia, ImageIndex& image)
709 {
710   ImageIndex image_handle = 0;
711   LoadIndex loadindex = 0;
712   surfaces_mutex.lock();
713
714   if (tvmedias.find(tvmedia) == tvmedias.end())   // if not found, return a loadIndex from loadTVMedia(tvmedia)
715   {
716     loadindex = loadTVMedia(tvmedia);
717   }
718   else   // if found, return a real imageIndex. incImageRef(image_handle). EXCEPT: If it's not found in images_ref (???) in which case do same as above.
719   {
720     image_handle = tvmedias[tvmedia];
721
722     if (images_ref.find(image_handle) == images_ref.end())
723     {
724       //invalid handle recreate
725       loadindex = loadTVMedia(tvmedia);
726       image_handle = 0;
727     }
728     else
729     {
730       incImageRef(image_handle);
731     }
732   }
733
734   /*tvmedias[tvmedia]=createTVMedia(tvmedia,width,height);
735   incImageRef(image_handle);*/
736   image = image_handle;
737   surfaces_mutex.unlock();
738   return loadindex;
739 }
740
741 LoadIndex OsdVector::loadTVMedia(TVMediaInfo& tvmedia)          // insert a tvmedia for loading
742 {
743   LoadIndex index = 0;
744
745   if (tvmedias_load.find(tvmedia) == tvmedias_load.end())        // if not found in tvmedias_load
746   {
747     switch (tvmedia.getType())
748     {
749       case 3:
750         index = VDR::getInstance()->loadTVMediaRecThumb(tvmedia);
751         break;
752
753       case 4:
754       {
755         index = ((long long) tvmedia.getPrimaryID()) << 32LL;
756         reader.addStaticImage(tvmedia.getPrimaryID());
757       } break;
758
759       case 5:
760         index = VDR::getInstance()->loadTVMediaEventThumb(tvmedia);
761         break;
762
763       case 6:
764         index = VDR::getInstance()->loadChannelLogo(tvmedia);
765         break;
766
767       default:
768         index = VDR::getInstance()->loadTVMedia(tvmedia);
769         break;
770     }
771
772     if (tvmedia.getType() != 4 && tvmedia.getStaticFallback() > -1)
773     {
774       reader.informFallback(index, tvmedia.getStaticFallback());
775     }
776
777     tvmedias_load[tvmedia] = index;
778     tvmedias_load_inv[index] = tvmedia;
779   }
780   else
781   {
782     index = tvmedias_load[tvmedia];
783   }
784
785   incLoadIndexRef(index);
786
787   return index;
788 }
789
790 void OsdVector::informPicture(LoadIndex index, ImageIndex imageIndex)
791 {
792   //Beware for thread safety
793   ImageIndex image_index = 0;
794
795   logger->debug(TAG, "TVMedia Picture for load-id {:#x} arrived index {:#x}", index, imageIndex);
796   surfaces_mutex.lock();
797   TVMediaInfo tvmedia = tvmedias_load_inv[index];
798
799   if (imageIndex)
800   {
801     std::map<LoadIndex, int>::iterator itty = loadindex_ref.find(index);
802     image_index = tvmedias[tvmedia] = imageIndex;
803     tvmedias_loaded[index] = image_index;
804
805     if (itty == loadindex_ref.end() || (*itty).second == 0)
806     {
807       // we do not want the picture anymore . Really...
808       // fill images_ref in to not irritate the garbage collector
809       if (images_ref.find(image_index) == images_ref.end())
810       {
811         images_ref[image_index] = 0;
812       }
813     }
814     else
815     {
816       incImageRef(image_index); // hold one index until all loadings refs are gone;
817     }
818   }
819
820   surfaces_mutex.unlock();
821 }
822
823 void OsdVector::processMessage(Message* m)
824 {
825   switch(m->message)
826   {
827     case Message::NEW_PICTURE:
828     {
829       //logger->debug(TAG, "TVMedia NEW_PICTURE");
830       informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
831       break;
832     }
833     case Message::NEW_PICTURE_STATIC:
834     {
835       //logger->debug(TAG, "TVMedia NEW_PICTURE {:#x} {:#x}", m->tag, m->parameter);
836       informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
837       break;
838     }
839   }
840 }
841
842 /*
843 ImageIndex OsdVector::getJpegRef(const char* fileName, int *width,int *height)
844 {
845         ImageIndex image_handle=0;
846         if (jpegs.find(fileName)==jpegs.end())
847         {
848                 image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
849         } else {
850                 image_handle=jpegs[fileName];
851                 *width=0;
852                 *height=0;
853                 if (images_ref.find(image_handle)==images_ref.end()) {
854                         //invalid handle recreate
855                         image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
856                 }
857         }
858         incImageRef(image_handle);
859         return image_handle;
860 }
861 */
862
863 ImageIndex OsdVector::getMonoBitmapRef(void* base, int width, int height)
864 {
865   ImageIndex image_handle;
866   surfaces_mutex.lock();
867
868   if (monobitmaps.find(base) == monobitmaps.end())
869   {
870     surfaces_mutex.unlock();
871     image_handle = createMonoBitmap(base, width, height);
872     surfaces_mutex.lock();
873     monobitmaps[base] = image_handle;
874   }
875   else
876   {
877     image_handle = monobitmaps[base];
878
879     if (images_ref.find(image_handle) == images_ref.end())
880     {
881       //invalid handle recreate
882       surfaces_mutex.unlock();
883       image_handle = createMonoBitmap(base, width, height);
884       surfaces_mutex.lock();
885       monobitmaps[base] = image_handle;
886     }
887   }
888
889   incImageRef(image_handle);
890   surfaces_mutex.unlock();
891   return image_handle;
892 }
893
894 ImageIndex OsdVector::getImagePalette(int width, int height, const unsigned char* image_data, const unsigned int* palette_data)
895 {
896   ImageIndex image_handle;
897   image_handle = createImagePalette(width, height, image_data, palette_data);
898   surfaces_mutex.lock();
899   palettepics.push_back(image_handle);
900   incImageRef(image_handle);
901   surfaces_mutex.unlock();
902   return image_handle;
903 }
904
905 /// PictureReader Class
906
907 OsdVector::PictureReader::~PictureReader()
908 {
909   decoders_lock.lock();
910
911   while (decoders.size())
912   {
913     PictureDecoder* dec = decoders.front();
914     decoders.pop_front();
915     delete dec;
916   }
917
918   decoders_lock.unlock();
919 }
920
921 void OsdVector::PictureReader::init()
922 {
923   threadStart();
924 }
925
926 void OsdVector::PictureReader::shutdown()
927 {
928   threadStop();
929 }
930
931 void OsdVector::PictureReader::addDecoder(PictureDecoder* decoder)
932 {
933   decoders_lock.lock();
934   decoder->init();
935   decoders.push_front(decoder);
936   decoders_lock.unlock();
937 }
938
939 void OsdVector::PictureReader::removeDecoder(PictureDecoder* decoder)
940 {
941   decoders_lock.lock();
942   std::list<PictureDecoder*>::iterator itty = decoders.begin();
943
944   while (itty != decoders.end())
945   {
946     if ((*itty) == decoder)
947     {
948       decoders.erase(itty);
949       break;
950     }
951
952     itty++;
953   }
954
955   LogNT::getInstance()->debug(TAG, "removeDecoder");
956   decoder->shutdown();
957   delete decoder;
958   decoders_lock.unlock();
959 }
960
961 void OsdVector::PictureReader::threadMethod()
962 {
963   OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance());
964   LogNT::getInstance()->debug(TAG, "TVMedia Start Picture Reader");
965
966   while (true)
967   {
968     if (!threadIsActive())
969     {
970       LogNT::getInstance()->debug(TAG, "TVMedia End Picture Reader");
971       threadCheckExit();
972     }
973
974     bool todos = true;
975
976     while (todos)
977     {
978       todos = false;
979       PictureInfo pictinf;
980       decoders_lock.lock();
981       std::list<PictureDecoder*>::iterator itty = decoders.begin();
982
983       while (itty != decoders.end())
984       {
985         if ((*itty)->getDecodedPicture(pictinf))    // FIXME somewhere around here?, or in getDecodedPicture, try SA optimisation
986         {
987           todos = true;
988           osdvector->createPicture(pictinf);
989         }
990
991         itty++;
992       }
993
994       if (processReceivedPictures())
995       {
996         todos = true;
997       }
998
999       decoders_lock.unlock();
1000     }
1001
1002     //LogNT::getInstance()->debug(TAG, "TVMedia Sleep Picture Reader");
1003
1004     struct timespec target_time;
1005     getClockRealTime(&target_time);
1006
1007     target_time.tv_nsec += 1000000LL * 10;
1008
1009     if (target_time.tv_nsec > 999999999)
1010     {
1011       target_time.tv_nsec -= 1000000000L;
1012       target_time.tv_sec += 1;
1013     }
1014
1015     threadLock();
1016     threadWaitForSignalTimed(&target_time);
1017     threadUnlock();
1018     //LogNT::getInstance()->debug(TAG, "TVMedia Sleep end Picture Reader");
1019   }
1020 }
1021
1022 void OsdVector::PictureReader::invalidateLoadIndex(LoadIndex index)
1023 {
1024   pict_lock_incoming.lock();
1025   invalid_loadindex.insert(index);
1026   pict_lock_incoming.unlock();
1027 }
1028
1029 void OsdVector::PictureReader::informFallback(LoadIndex index, int fallback)
1030 {
1031   pict_lock_incoming.lock();
1032   inform_fallback[index] = fallback;
1033   pict_lock_incoming.unlock();
1034 }
1035
1036 void OsdVector::PictureReader::receivePicture(VDR_ResponsePacket* vresp)
1037 {
1038   pict_lock_incoming.lock();
1039   pict_incoming.push(vresp);
1040   pict_lock_incoming.unlock();
1041   threadSignal();
1042 }
1043
1044
1045 void OsdVector::PictureReader::addStaticImage(unsigned int id)
1046 {
1047   pict_lock_incoming.lock();
1048   pict_incoming_static.push(id);
1049   invalid_loadindex.erase(((long long) id) << 32LL);
1050   pict_lock_incoming.unlock();
1051   threadSignal();
1052 }
1053
1054 #if WIN32
1055   // FIXME win pragma
1056   #pragma warning(disable : 4703)
1057 #endif
1058
1059 bool OsdVector::PictureReader::processReceivedPictures()
1060 {
1061   bool decoded = false;
1062   bool valid = true;
1063   pict_lock_incoming.lock();
1064
1065   if (pict_incoming.size())
1066   {
1067     VDR_ResponsePacket* vresp = pict_incoming.front();
1068     pict_incoming.pop();
1069     std::set<LoadIndex>::iterator setpos = invalid_loadindex.find(vresp->getStreamID());
1070
1071     if (setpos != invalid_loadindex.end())
1072     {
1073       valid = false;
1074       invalid_loadindex.erase(setpos);
1075     }
1076
1077     pict_lock_incoming.unlock();
1078
1079     if (!valid)   // we do not want it anymore skip it;
1080     {
1081       delete vresp;
1082       return true;
1083     }
1084
1085     // LogNT::getInstance()->debug(TAG, "TVMedia Pictures arrived VDR {:#x} {} {}",
1086     // vresp->getStreamID(),vresp->getUserDataLength(),vresp->getFlag());
1087     bool decode = false;
1088     bool freed = false;
1089     UCHAR* userdata;
1090     ULONG length;
1091
1092     if (vresp->getFlag() != 2)
1093     {
1094       userdata = vresp->getUserData();
1095       length = vresp->getUserDataLength();
1096       decode = true;
1097       freed = true;
1098     }
1099     else
1100     {
1101       int fallback = -1;
1102       pict_lock_incoming.lock();
1103
1104       if (inform_fallback.find(vresp->getStreamID()) != inform_fallback.end())
1105       {
1106         fallback = inform_fallback[vresp->getStreamID()];
1107       }
1108
1109       pict_lock_incoming.unlock();
1110
1111       if (fallback >= 0 && ((OsdVector*)Osd::getInstance())->getStaticImageData(fallback, &userdata, &length))
1112       {
1113         decode = true;
1114         freed = false;
1115       }
1116     }
1117
1118     if (decode)
1119     {
1120       std::list<PictureDecoder*>::iterator itty = decoders.begin();
1121
1122       while (itty != decoders.end())
1123       {
1124         userdata = (*itty)->decodePicture(vresp->getStreamID(), userdata, length, freed);
1125
1126         if (!userdata)
1127         {
1128           decoded = true;
1129           break;
1130         }
1131
1132         itty++;
1133       }
1134
1135       if (!decoded && userdata && freed)
1136       {
1137         free(userdata);
1138       }
1139     }
1140
1141     pict_lock_incoming.lock();
1142     inform_fallback.erase(vresp->getStreamID());
1143     pict_lock_incoming.unlock();
1144     //else  osd->informPicture(vresp->getStreamID(), 0);
1145     delete vresp;
1146   }
1147   else if (pict_incoming_static.size())
1148   {
1149     unsigned int static_id = pict_incoming_static.front();
1150     pict_incoming_static.pop();
1151     std::set<LoadIndex>::iterator setpos = invalid_loadindex.find(((long long) static_id) << 32LL);
1152
1153     if (setpos != invalid_loadindex.end())
1154     {
1155       valid = false;
1156       invalid_loadindex.erase(setpos);
1157     }
1158
1159     pict_lock_incoming.unlock();
1160
1161     if (!valid)   // we do not want it anymore skip it;
1162     {
1163       return true;
1164     }
1165
1166     UCHAR* userdata;
1167     ULONG length;
1168
1169     if (((OsdVector*)Osd::getInstance())->getStaticImageData(static_id, &userdata, &length))
1170     {
1171       std::list<PictureDecoder*>::iterator itty = decoders.begin();
1172
1173       while (itty != decoders.end())
1174       {
1175         if (!(*itty)->decodePicture(((long long) static_id) << 32LL, userdata, length, false))
1176         {
1177           decoded = true;
1178           break;
1179         }
1180
1181         itty++;
1182       }
1183     }
1184   }
1185   else
1186   {
1187     pict_lock_incoming.unlock();
1188   }
1189
1190   if (pict_incoming.size() || pict_incoming_static.size()) return true;
1191
1192   return decoded;
1193 }