]> git.vomp.tv Git - vompclient.git/blob - src/imageloader.cc
Bug fix, but was caught by other code. More efficient now.
[vompclient.git] / src / imageloader.cc
1 /*
2     Copyright 2021 Chris Tallon
3     Portions Copyright 2012 Marten Richter
4
5     This file is part of VOMP.
6
7     VOMP is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     VOMP is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "log.h"
22 #include "vdr.h"
23 #include "vdrresponsepacket.h"
24 #include "staticartwork.h"
25
26 #include "imageloader.h"
27
28 const static char* TAG = "ImageLoader";
29
30 ImageLoader* ImageLoader::instance = NULL;
31
32 ImageLoader::ImageLoader()
33 {
34   instance = this;
35   logger = LogNT::getInstance();
36 }
37
38 ImageLoader::~ImageLoader()
39 {
40   instance = NULL;
41
42   decoders_lock.lock();
43   while (decoders.size())
44   {
45     PictureDecoder* dec = decoders.front();
46     decoders.pop_front();
47     delete dec;
48   }
49   decoders_lock.unlock();
50 }
51
52 ImageLoader* ImageLoader::getInstance()
53 {
54   return instance;
55 }
56
57 bool ImageLoader::init()
58 {
59   // Start picture reader thread
60   pictureReaderCondSigtex.lock(); // misuse condsigtex here
61   pictureReaderThread = std::thread( [this]
62   {
63     pictureReaderCondSigtex.lock();
64     pictureReaderCondSigtex.unlock();
65     pictureReaderThreadMethod();
66   });
67   pictureReaderCondSigtex.unlock();
68
69   return true;
70 }
71
72 void ImageLoader::shutdown()
73 {
74   std::unique_lock<std::mutex> lg(pictureReaderCondSigtex);
75
76   if (pictureReaderThread.joinable())
77   {
78     pictureReaderThreadQuit = true;
79     pictureReaderThreadCond.notify_one();
80     lg.unlock();
81     pictureReaderThread.join();
82   }
83 }
84
85 void ImageLoader::addDecoder(PictureDecoder* decoder)
86 {
87   decoders_lock.lock();
88   logger->debug(TAG, "addDecoder {}", static_cast<void*>(decoder));
89   decoder->init();
90   decoders.push_front(decoder);
91   decoders_lock.unlock();
92 }
93
94 void ImageLoader::removeDecoder(PictureDecoder* decoder)
95 {
96   decoders_lock.lock();
97   std::list<PictureDecoder*>::iterator itty = decoders.begin();
98
99   while (itty != decoders.end())
100   {
101     if ((*itty) == decoder)
102     {
103       decoders.erase(itty);
104       break;
105     }
106
107     itty++;
108   }
109
110   logger->debug(TAG, "removeDecoder");
111   decoder->shutdown();
112   delete decoder;
113   decoders_lock.unlock();
114 }
115
116 void ImageLoader::garbageCollect()
117 {
118   mImagesLock.lock();
119   auto it = std::remove_if(mImages.begin(), mImages.end(), [](Image& i) { return i.use_count() == 1; });
120   mImages.erase(it, mImages.end());
121   mImagesLock.unlock();
122 }
123
124 void ImageLoader::dumpImages()
125 {
126   logger->trace(TAG, "Num Images: {}", mImages.size());
127 }
128
129 ImageStatic ImageLoader::createStatic(int sa_id)
130 {
131   ImageStatic staticImage = std::make_shared<CImageStatic>();
132   staticImage->type = CImage::STATIC;
133   staticImage->staticArtworkID = sa_id;
134   Image image = staticImage;
135
136   mImagesLock.lock();
137   mImages.push_back(image);
138   mImagesLock.unlock();
139
140   ensureLoaded(image);
141   return staticImage;
142 }
143
144 // For the old TVMedia types 0,1,2, though I suspect one of these doesn't exist
145 // I think there's only SeriesInfo and MovieInfo that uses this now
146 ImageGeneric ImageLoader::createGeneric()
147 {
148   ImageGeneric genericImage = std::make_shared<CImageGeneric>();
149   genericImage->type = CImage::GENERIC;
150   Image image = genericImage;
151
152   mImagesLock.lock();
153   mImages.push_back(image);
154   mImagesLock.unlock();
155
156   // Generic Images do not have ensureLoaded called on them
157   // It is the callers responsibility to fill the generic with
158   // extra information and then call ImageLoader::ensureLoaded(image)
159   return genericImage;
160 }
161
162 // FIXME use unique_lock, lock around add as well, unlock before ensureLoaded
163
164 ImageRecFolder ImageLoader::createRecFolder(const char* recName)
165 {
166   // Does this image already exist?
167   {
168     std::lock_guard<std::mutex> lg(mImagesLock);
169     for(Image& mImage : mImages)
170     {
171       if (mImage->type == CImage::RECFOLDER)
172       {
173         ImageRecFolder i = std::static_pointer_cast<CImageRecFolder>(mImage);
174         if (i->fileName == recName) return i;
175       }
176     }
177   }
178
179   ImageRecFolder recFolderImage = std::make_shared<CImageRecFolder>();
180   recFolderImage->type = CImage::RECFOLDER;
181   recFolderImage->fileName = recName;
182   recFolderImage->staticArtworkID = sa_recfolder; // For fallback
183   Image image = recFolderImage;
184
185   mImagesLock.lock();
186   mImages.push_back(image);
187   mImagesLock.unlock();
188
189   ensureLoaded(image);
190   return recFolderImage;
191 }
192
193 ImageRecThumb ImageLoader::createRecThumb(const char* recName)
194 {
195   // Does this image already exist?
196   {
197     std::lock_guard<std::mutex> lg(mImagesLock);
198     for(Image& mImage : mImages)
199     {
200       if (mImage->type == CImage::RECTHUMB)
201       {
202         ImageRecThumb i = std::static_pointer_cast<CImageRecThumb>(mImage);
203         if (i->fileName == recName) return i;
204       }
205     }
206   }
207
208   ImageRecThumb recThumbImage = std::make_shared<CImageRecThumb>();
209   recThumbImage->type = CImage::RECTHUMB;
210   recThumbImage->fileName = recName;
211   recThumbImage->staticArtworkID = sa_recording; // For fallback
212   Image image = recThumbImage;
213
214   mImagesLock.lock();
215   mImages.push_back(image);
216   mImagesLock.unlock();
217
218   ensureLoaded(image);
219   return recThumbImage;
220 }
221
222 ImageEventThumb ImageLoader::createEventThumb(u4 channel, u4 event)
223 {
224   // Does this image already exist?
225   {
226     std::lock_guard<std::mutex> lg(mImagesLock);
227     for(Image& mImage : mImages)
228     {
229       if (mImage->type == CImage::EVENTTHUMB)
230       {
231         ImageEventThumb i = std::static_pointer_cast<CImageEventThumb>(mImage);
232         if ((i->channel == channel) && (i->event == event)) return i;
233       }
234     }
235   }
236
237   ImageEventThumb eventThumbImage = std::make_shared<CImageEventThumb>();
238   eventThumbImage->type = CImage::EVENTTHUMB;
239   eventThumbImage->channel = channel;
240   eventThumbImage->event = event;
241   eventThumbImage->staticArtworkID = sa_defposter; // For fallback
242   Image image = eventThumbImage;
243
244   mImagesLock.lock();
245   mImages.push_back(image);
246   mImagesLock.unlock();
247
248   ensureLoaded(image);
249   return eventThumbImage;
250 }
251
252 ImageChannelLogo ImageLoader::createChannelLogo(u4 channel, int sa_id)
253 {
254   // Does this image already exist?
255   {
256     std::lock_guard<std::mutex> lg(mImagesLock);
257     for(Image& mImage : mImages)
258     {
259       if (mImage->type == CImage::CHANNELLOGO)
260       {
261         ImageChannelLogo i = std::static_pointer_cast<CImageChannelLogo>(mImage);
262         if ((i->channel == channel) && (i->staticArtworkID == sa_id)) return i;
263       }
264     }
265   }
266
267   logger->trace(TAG, "createChannelLogo said = {}", sa_id);
268
269   ImageChannelLogo channelLogoImage = std::make_shared<CImageChannelLogo>();
270   channelLogoImage->type = CImage::CHANNELLOGO;
271   channelLogoImage->channel = channel;
272   channelLogoImage->staticArtworkID = sa_id;
273
274   Image image = channelLogoImage;
275
276   mImagesLock.lock();
277   mImages.push_back(image);
278   mImagesLock.unlock();
279
280   ensureLoaded(image);
281   return channelLogoImage;
282 }
283
284 void ImageLoader::ensureLoaded(ImageGeneric& ig)
285 {
286   Image image = ig;
287   ensureLoaded(image);
288 }
289
290 void ImageLoader::ensureLoaded(Image& image)
291 {
292   image->stateLock.lock();                                                                  ///////// LOCK
293
294   if (!image->ready && !image->loading) // needs loading
295   {
296     switch (image->getType())
297     {
298       case CImage::STATIC:
299       {
300         decodeStatic(image);
301         break;
302       }
303       case CImage::GENERIC:
304       {
305         ImageGeneric ign = std::static_pointer_cast<CImageGeneric>(image);
306         VDR::getInstance()->loadImageGeneric(ign);
307         break;
308       }
309       case CImage::RECFOLDER:
310       case CImage::RECTHUMB:
311       {
312         ImageRecThumb irt = std::static_pointer_cast<CImageRecThumb>(image);
313         VDR::getInstance()->loadImageRecThumb(irt);
314         break;
315       }
316       case CImage::EVENTTHUMB:
317       {
318         ImageEventThumb iet = std::static_pointer_cast<CImageEventThumb>(image);
319         VDR::getInstance()->loadImageEventThumb(iet);
320         break;
321       }
322       case CImage::CHANNELLOGO:
323       {
324         ImageChannelLogo icl = std::static_pointer_cast<CImageChannelLogo>(image);
325         VDR::getInstance()->loadImageChannelLogo(icl);
326         break;
327       }
328       case CImage::INVALID:
329       {}
330     }
331
332     image->loading = true;
333   }
334
335   image->stateLock.unlock();                                                                  ///////// UNLOCK
336 }
337
338 void ImageLoader::downloadDone(VDR_ResponsePacket* vresp)
339 {
340   // This is VDR's reception thread, don't delay
341   // Take the incoming vresp and add it to an incoming queue. Then signal the processor thread
342
343   // TODO reimplement index-map
344
345   PictureReaderWorkItem p;
346   p.vresp = vresp;
347
348   pictureReaderWorkLock.lock();
349   pictureReaderWork.push(p);
350   pictureReaderWorkLock.unlock();
351
352
353   // Now do most minimal lock & signal. Uses separate lock to the work vector.
354   // This split mechanism guarantees work won't be missed at the expense of possibly
355   // waking the thread unnecessarily.
356   pictureReaderCondSigtex.lock();
357   pictureReaderThreadRun = true;
358   pictureReaderThreadCond.notify_one();
359   pictureReaderCondSigtex.unlock();
360 }
361
362 bool ImageLoader::pictureRendered(LoadingIndex index, OsdImage& oimage)
363 {
364   // This is called form OsdVector after it is notified from the actual gfx layer
365   // that an image has been loaded into the graphics system and is ready for use
366
367   logger->trace(TAG, "pictureRendered: li {}", static_cast<void*>(index));
368
369   Image image = loadingIndexToImage(index);
370
371   if (!image)
372   {
373     // An image has been loaded but we don't know anything about it.
374     // Probably a slow load for something already dropped off screen.
375     return false;
376   }
377
378   // prob want to lock the whole thing ot update other images waiting for the same oimage
379   std::lock_guard<std::mutex> lg1(mImagesLock);
380
381   std::lock_guard<std::mutex> lg(image->stateLock);
382   image->loading = false;
383   image->ready = true;
384   image->osdImage = oimage;
385
386   logger->trace(TAG, "pictureRendered: {}", static_cast<void*>(image.get()));
387
388   // Are there any images waiting for this one?
389   for(auto mImage : mImages)
390   {
391     if ((mImage->ready == false) && (mImage->otherImage == image))
392     {
393       mImage->osdImage = image->osdImage;
394       mImage->loading = false;
395       mImage->ready = true;
396       // FIXME ?? Clear mImage->otherImage?
397     }
398   }
399
400   return true;
401 }
402
403 Image ImageLoader::loadingIndexToImage(LoadingIndex index)
404 {
405   std::lock_guard<std::mutex> lg(mImagesLock);
406
407   for(Image& i : mImages)
408   {
409     //logger->trace(TAG, "li2I {} {}", (void*)index, (void*)i.get());
410     if (i.get() == static_cast<CImage*>(index)) return i;
411   }
412
413   // It is only possible to get to here calling from pictureRendered, but the Image
414   // has already been discarded (went off screen before it ever loaded)
415
416   Image dead;
417   return dead;
418 }
419
420 void ImageLoader::decodeStatic(Image& image)
421 {
422   PictureReaderWorkItem p;
423   p.image = image;
424   pictureReaderWorkLock.lock();
425   pictureReaderWork.push(p);
426   pictureReaderWorkLock.unlock();
427
428   // Same split as above, separate locks for vector and signal
429
430   pictureReaderCondSigtex.lock();
431   pictureReaderThreadRun = true;
432   pictureReaderThreadCond.notify_one();
433   pictureReaderCondSigtex.unlock();
434 }
435
436
437 void ImageLoader::pictureReaderThreadMethod()
438 {
439   std::unique_lock<std::mutex> ul(pictureReaderCondSigtex); // Lock for most minimal time
440   while (true)
441   {
442     pictureReaderThreadCond.wait(ul, [this]{ return pictureReaderThreadQuit || pictureReaderThreadRun; });
443     // locked
444     if (pictureReaderThreadQuit) return; // unlock
445     pictureReaderThreadRun = false;
446     ul.unlock();
447
448     // unlocked while we work
449
450     bool miniRunAgain;
451     do
452     {
453       miniRunAgain = false;
454
455       PictureInfo pictinf;
456       decoders_lock.lock();
457       std::list<PictureDecoder*>::iterator itty = decoders.begin();
458
459       while (itty != decoders.end())
460       {
461         if ((*itty)->getDecodedPicture(pictinf)) // pictinf now contains handle (vgImage) and reference (a EGLImageKHR)
462         {
463           miniRunAgain = true;
464           // logger->trace(TAG, "got decoded pic {}", (void*)pictinf.lindex );
465
466           OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance()); // put this here, osdvector is not created yet when tihs method starts
467           osdvector->createPicture(pictinf); // This goes into OsdOpenVG which generates Message::NEW_PICTURE
468         }
469         itty++;
470       }
471
472       pictureReaderProcessOne();
473
474       decoders_lock.unlock();
475
476       if (pictureReaderWork.size()) miniRunAgain = true;
477
478     } while(miniRunAgain);
479
480     ul.lock();
481   }
482 }
483
484 void ImageLoader::pictureReaderProcessOne()
485 {
486   OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance());
487
488   pictureReaderWorkLock.lock();
489   if (pictureReaderWork.empty())
490   {
491     pictureReaderWorkLock.unlock();
492     return;
493   }
494
495   PictureReaderWorkItem pwi;
496   pwi = pictureReaderWork.front();
497   pictureReaderWork.pop();
498   pictureReaderWorkLock.unlock();
499
500
501   // Either pwi has a image or a vresp. If it's a image then it's a static artwork decode
502   // If it's a vresp then it's a server download done
503
504   // Set up the common variables
505   Image image;
506   bool free_it = false;
507   u1* imageData{};
508   u4 length{};
509
510
511   if (pwi.vresp)
512   {
513     // Now find the Image with this vresp->getStreamID() == image.serverLoadingRef
514     mImagesLock.lock();
515
516     //      logger->trace(TAG, "mImages size: {}", mImages.size());
517     //     for(Image& i : mImages)
518     //     {
519     //       logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
520     //     }
521
522     for(Image& i : mImages)
523     {
524       if (i->serverLoadingRef == pwi.vresp->getStreamID())
525       {
526         image = i;
527         break;
528       }
529     }
530     mImagesLock.unlock();
531
532
533     if (!image)
534     {
535       logger->trace(TAG, "Could not find Image for VRESP in download-done!!");
536       abort();
537     }
538
539     std::lock_guard<std::mutex> lg(image->stateLock);
540
541     if (pwi.vresp->getFlag() != 2)  // Server image success
542     {
543       imageData = pwi.vresp->getUserData();
544       length = pwi.vresp->getUserDataLength();
545       free_it = true;
546     }
547     else
548     {
549       image->serverLoadingRef = 0;
550       image->usingFallback = true;
551       logger->trace(TAG, "Fallback for {}", static_cast<void*>(image.get()));
552     }
553
554     delete pwi.vresp;
555   }
556   else
557   {
558     image = pwi.image;
559   }
560
561   // vresp is gone. Now we have a Image which might have some downloaded data
562
563   // Before we go on, does anyone still want this image?
564   if (image.use_count() == 2) return; // It's only in the mImages and here
565
566   //logger->trace(TAG, "Still needed {} {}", (void*)image.get(), image->staticArtworkID);
567
568   // Set up a static decode?
569   if (      (image->getType() == CImage::STATIC)
570         || ((image->usingFallback == true) && (image->staticArtworkID != -1))
571      )
572   {
573     //logger->trace(TAG, "Do static {}", (void*)image.get());
574
575     // Do we already have this static decoded?
576     mImagesLock.lock();
577     for(Image& i : mImages)
578     {
579       if (i == image) continue;
580
581       //logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
582
583       if (    ((i->getType() == CImage::STATIC) || (i->usingFallback == true))
584           &&  (i->staticArtworkID == image->staticArtworkID)
585           &&  (!i->otherImage) // Don't link to another image which is already linked to another image
586          )
587       {
588         if (i->ready) // Link osdImage, set ready, done.
589         {
590           image->osdImage = i->osdImage;
591           image->ready = true;
592           image->loading = false;
593           mImagesLock.unlock();
594           return;
595         }
596         else // Link to the other image file and wait for it to be ready
597         {
598           image->otherImage = i;
599           mImagesLock.unlock();
600           return;
601         }
602       }
603     }
604     mImagesLock.unlock();
605
606     // Continue with a new decode
607
608     osdvector->getStaticImageData(image->staticArtworkID, &imageData, &length);
609   }
610
611   // Now decode.
612
613   if (imageData)
614   {
615     //logger->trace(TAG, "Doing decode {}", (void*)image.get());
616
617     std::list<PictureDecoder*>::iterator it = decoders.begin();
618
619     while (it != decoders.end())
620     {
621       imageData = (*it)->decodePicture(image.get(), imageData, length, free_it);
622       //logger->trace(TAG, "Doing decode done {} {}", (void*)image.get(), (void*) imageData);
623
624       if (!imageData) break;
625       it++;
626     }
627
628     if (imageData)
629     {
630       image->failed = true;
631       if (free_it) free(imageData);
632     }
633   }
634 }