]> git.vomp.tv Git - vompclient.git/blob - src/imageloader.cc
Type change: UCHAR -> u1
[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     }
329
330     image->loading = true;
331   }
332
333   image->stateLock.unlock();                                                                  ///////// UNLOCK
334 }
335
336 void ImageLoader::downloadDone(VDR_ResponsePacket* vresp)
337 {
338   // This is VDR's reception thread, don't delay
339   // Take the incoming vresp and add it to an incoming queue. Then signal the processor thread
340
341   // TODO reimplement index-map
342
343   PictureReaderWorkItem p;
344   p.vresp = vresp;
345
346   pictureReaderWorkLock.lock();
347   pictureReaderWork.push(p);
348   pictureReaderWorkLock.unlock();
349
350
351   // Now do most minimal lock & signal. Uses separate lock to the work vector.
352   // This split mechanism guarantees work won't be missed at the expense of possibly
353   // waking the thread unnecessarily.
354   pictureReaderCondSigtex.lock();
355   pictureReaderThreadRun = true;
356   pictureReaderThreadCond.notify_one();
357   pictureReaderCondSigtex.unlock();
358 }
359
360 bool ImageLoader::pictureRendered(LoadingIndex index, OsdImage& oimage)
361 {
362   // This is called form OsdVector after it is notified from the actual gfx layer
363   // that an image has been loaded into the graphics system and is ready for use
364
365   logger->trace(TAG, "pictureRendered: li {}", static_cast<void*>(index));
366
367   Image image = loadingIndexToImage(index);
368
369   if (!image)
370   {
371     // An image has been loaded but we don't know anything about it.
372     // Probably a slow load for something already dropped off screen.
373     return false;
374   }
375
376   // prob want to lock the whole thing ot update other images waiting for the same oimage
377   std::lock_guard<std::mutex> lg1(mImagesLock);
378
379   std::lock_guard<std::mutex> lg(image->stateLock);
380   image->loading = false;
381   image->ready = true;
382   image->osdImage = oimage;
383
384   logger->trace(TAG, "pictureRendered: {}", static_cast<void*>(image.get()));
385
386   // Are there any images waiting for this one?
387   for(auto mImage : mImages)
388   {
389     if ((mImage->ready == false) && (mImage->otherImage == image))
390     {
391       mImage->osdImage = image->osdImage;
392       mImage->loading = false;
393       mImage->ready = true;
394       // FIXME ?? Clear mImage->otherImage?
395     }
396   }
397
398   return true;
399 }
400
401 Image ImageLoader::loadingIndexToImage(LoadingIndex index)
402 {
403   std::lock_guard<std::mutex> lg(mImagesLock);
404
405   for(Image& i : mImages)
406   {
407     //logger->trace(TAG, "li2I {} {}", (void*)index, (void*)i.get());
408     if (i.get() == static_cast<CImage*>(index)) return i;
409   }
410
411   // It is only possible to get to here calling from pictureRendered, but the Image
412   // has already been discarded (went off screen before it ever loaded)
413
414   Image dead;
415   return dead;
416 }
417
418 void ImageLoader::decodeStatic(Image& image)
419 {
420   PictureReaderWorkItem p;
421   p.image = image;
422   pictureReaderWorkLock.lock();
423   pictureReaderWork.push(p);
424   pictureReaderWorkLock.unlock();
425
426   // Same split as above, separate locks for vector and signal
427
428   pictureReaderCondSigtex.lock();
429   pictureReaderThreadRun = true;
430   pictureReaderThreadCond.notify_one();
431   pictureReaderCondSigtex.unlock();
432 }
433
434
435 void ImageLoader::pictureReaderThreadMethod()
436 {
437   std::unique_lock<std::mutex> ul(pictureReaderCondSigtex); // Lock for most minimal time
438   while (true)
439   {
440     pictureReaderThreadCond.wait(ul, [this]{ return pictureReaderThreadQuit || pictureReaderThreadRun; });
441     // locked
442     if (pictureReaderThreadQuit) return; // unlock
443     pictureReaderThreadRun = false;
444     ul.unlock();
445
446     // unlocked while we work
447
448     bool miniRunAgain;
449     do
450     {
451       miniRunAgain = false;
452
453       PictureInfo pictinf;
454       decoders_lock.lock();
455       std::list<PictureDecoder*>::iterator itty = decoders.begin();
456
457       while (itty != decoders.end())
458       {
459         if ((*itty)->getDecodedPicture(pictinf)) // pictinf now contains handle (vgImage) and reference (a EGLImageKHR)
460         {
461           miniRunAgain = true;
462           // logger->trace(TAG, "got decoded pic {}", (void*)pictinf.lindex );
463
464           OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance()); // put this here, osdvector is not created yet when tihs method starts
465           osdvector->createPicture(pictinf); // This goes into OsdOpenVG which generates Message::NEW_PICTURE
466         }
467         itty++;
468       }
469
470       pictureReaderProcessOne();
471
472       decoders_lock.unlock();
473
474       if (pictureReaderWork.size()) miniRunAgain = true;
475
476     } while(miniRunAgain);
477
478     ul.lock();
479   }
480 }
481
482 void ImageLoader::pictureReaderProcessOne()
483 {
484   OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance());
485
486   pictureReaderWorkLock.lock();
487   if (pictureReaderWork.empty())
488   {
489     pictureReaderWorkLock.unlock();
490     return;
491   }
492
493   PictureReaderWorkItem pwi;
494   pwi = pictureReaderWork.front();
495   pictureReaderWork.pop();
496   pictureReaderWorkLock.unlock();
497
498
499   // Either pwi has a image or a vresp. If it's a image then it's a static artwork decode
500   // If it's a vresp then it's a server download done
501
502   // Set up the common variables
503   Image image;
504   bool free_it = false;
505   u1* imageData{};
506   u4 length{};
507
508
509   if (pwi.vresp)
510   {
511     // Now find the Image with this vresp->getStreamID() == image.serverLoadingRef
512     mImagesLock.lock();
513
514     //      logger->trace(TAG, "mImages size: {}", mImages.size());
515     //     for(Image& i : mImages)
516     //     {
517     //       logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
518     //     }
519
520     for(Image& i : mImages)
521     {
522       if (i->serverLoadingRef == pwi.vresp->getStreamID())
523       {
524         image = i;
525         break;
526       }
527     }
528     mImagesLock.unlock();
529
530
531     if (!image)
532     {
533       logger->trace(TAG, "Could not find Image for VRESP in download-done!!");
534       abort();
535     }
536
537     std::lock_guard<std::mutex> lg(image->stateLock);
538
539     if (pwi.vresp->getFlag() != 2)  // Server image success
540     {
541       imageData = pwi.vresp->getUserData();
542       length = pwi.vresp->getUserDataLength();
543       free_it = true;
544     }
545     else
546     {
547       image->serverLoadingRef = 0;
548       image->usingFallback = true;
549       logger->trace(TAG, "Fallback for {}", static_cast<void*>(image.get()));
550     }
551
552     delete pwi.vresp;
553   }
554   else
555   {
556     image = pwi.image;
557   }
558
559   // vresp is gone. Now we have a Image which might have some downloaded data
560
561   // Before we go on, does anyone still want this image?
562   if (image.use_count() == 2) return; // It's only in the mImages and here
563
564   //logger->trace(TAG, "Still needed {} {}", (void*)image.get(), image->staticArtworkID);
565
566   // Set up a static decode?
567   if (      (image->getType() == CImage::STATIC)
568         || ((image->usingFallback == true) && (image->staticArtworkID != -1))
569      )
570   {
571     //logger->trace(TAG, "Do static {}", (void*)image.get());
572
573     // Do we already have this static decoded?
574     mImagesLock.lock();
575     for(Image& i : mImages)
576     {
577       if (i == image) continue;
578
579       //logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
580
581       if (    ((i->getType() == CImage::STATIC) || (i->usingFallback == true))
582           &&  (i->staticArtworkID == image->staticArtworkID)
583           &&  (!i->otherImage) // Don't link to another image which is already linked to another image
584          )
585       {
586         if (i->ready) // Link osdImage, set ready, done.
587         {
588           image->osdImage = i->osdImage;
589           image->ready = true;
590           image->loading = false;
591           mImagesLock.unlock();
592           return;
593         }
594         else // Link to the other image file and wait for it to be ready
595         {
596           image->otherImage = i;
597           mImagesLock.unlock();
598           return;
599         }
600       }
601     }
602     mImagesLock.unlock();
603
604     // Continue with a new decode
605
606     osdvector->getStaticImageData(image->staticArtworkID, &imageData, &length);
607   }
608
609   // Now decode.
610
611   if (imageData)
612   {
613     //logger->trace(TAG, "Doing decode {}", (void*)image.get());
614
615     std::list<PictureDecoder*>::iterator it = decoders.begin();
616
617     while (it != decoders.end())
618     {
619       imageData = (*it)->decodePicture(image.get(), imageData, length, free_it);
620       //logger->trace(TAG, "Doing decode done {} {}", (void*)image.get(), (void*) imageData);
621
622       if (!imageData) break;
623       it++;
624     }
625
626     if (imageData)
627     {
628       image->failed = true;
629       if (free_it) free(imageData);
630     }
631   }
632 }