2 Copyright 2021 Chris Tallon
3 Portions Copyright 2012 Marten Richter
5 This file is part of VOMP.
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.
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.
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/>.
23 #include "vdrresponsepacket.h"
24 #include "staticartwork.h"
26 #include "imageloader.h"
28 const static char* TAG = "ImageLoader";
30 ImageLoader* ImageLoader::instance = NULL;
32 ImageLoader::ImageLoader()
35 logger = LogNT::getInstance();
38 ImageLoader::~ImageLoader()
43 while (decoders.size())
45 PictureDecoder* dec = decoders.front();
49 decoders_lock.unlock();
52 ImageLoader* ImageLoader::getInstance()
57 bool ImageLoader::init()
59 // Start picture reader thread
60 pictureReaderCondSigtex.lock(); // misuse condsigtex here
61 pictureReaderThread = std::thread( [this]
63 pictureReaderCondSigtex.lock();
64 pictureReaderCondSigtex.unlock();
65 pictureReaderThreadMethod();
67 pictureReaderCondSigtex.unlock();
72 void ImageLoader::shutdown()
74 std::unique_lock<std::mutex> lg(pictureReaderCondSigtex);
76 if (pictureReaderThread.joinable())
78 pictureReaderThreadQuit = true;
79 pictureReaderThreadCond.notify_one();
81 pictureReaderThread.join();
85 void ImageLoader::addDecoder(PictureDecoder* decoder)
88 logger->debug(TAG, "addDecoder {}", static_cast<void*>(decoder));
90 decoders.push_front(decoder);
91 decoders_lock.unlock();
94 void ImageLoader::removeDecoder(PictureDecoder* decoder)
97 std::list<PictureDecoder*>::iterator itty = decoders.begin();
99 while (itty != decoders.end())
101 if ((*itty) == decoder)
103 decoders.erase(itty);
110 logger->debug(TAG, "removeDecoder");
113 decoders_lock.unlock();
116 void ImageLoader::garbageCollect()
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();
124 void ImageLoader::dumpImages()
126 logger->trace(TAG, "Num Images: {}", mImages.size());
129 ImageStatic ImageLoader::createStatic(int sa_id)
131 ImageStatic staticImage = std::make_shared<CImageStatic>();
132 staticImage->type = CImage::STATIC;
133 staticImage->staticArtworkID = sa_id;
134 Image image = staticImage;
137 mImages.push_back(image);
138 mImagesLock.unlock();
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()
148 ImageGeneric genericImage = std::make_shared<CImageGeneric>();
149 genericImage->type = CImage::GENERIC;
150 Image image = genericImage;
153 mImages.push_back(image);
154 mImagesLock.unlock();
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)
162 // FIXME use unique_lock, lock around add as well, unlock before ensureLoaded
164 ImageRecFolder ImageLoader::createRecFolder(const char* recName)
166 // Does this image already exist?
168 std::lock_guard<std::mutex> lg(mImagesLock);
169 for(Image& mImage : mImages)
171 if (mImage->type == CImage::RECFOLDER)
173 ImageRecFolder i = std::static_pointer_cast<CImageRecFolder>(mImage);
174 if (i->fileName == recName) return i;
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;
186 mImages.push_back(image);
187 mImagesLock.unlock();
190 return recFolderImage;
193 ImageRecThumb ImageLoader::createRecThumb(const char* recName)
195 // Does this image already exist?
197 std::lock_guard<std::mutex> lg(mImagesLock);
198 for(Image& mImage : mImages)
200 if (mImage->type == CImage::RECTHUMB)
202 ImageRecThumb i = std::static_pointer_cast<CImageRecThumb>(mImage);
203 if (i->fileName == recName) return i;
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;
215 mImages.push_back(image);
216 mImagesLock.unlock();
219 return recThumbImage;
222 ImageEventThumb ImageLoader::createEventThumb(u4 channel, u4 event)
224 // Does this image already exist?
226 std::lock_guard<std::mutex> lg(mImagesLock);
227 for(Image& mImage : mImages)
229 if (mImage->type == CImage::EVENTTHUMB)
231 ImageEventThumb i = std::static_pointer_cast<CImageEventThumb>(mImage);
232 if ((i->channel == channel) && (i->event == event)) return i;
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;
245 mImages.push_back(image);
246 mImagesLock.unlock();
249 return eventThumbImage;
252 ImageChannelLogo ImageLoader::createChannelLogo(u4 channel, int sa_id)
254 // Does this image already exist?
256 std::lock_guard<std::mutex> lg(mImagesLock);
257 for(Image& mImage : mImages)
259 if (mImage->type == CImage::CHANNELLOGO)
261 ImageChannelLogo i = std::static_pointer_cast<CImageChannelLogo>(mImage);
262 if ((i->channel == channel) && (i->staticArtworkID == sa_id)) return i;
267 logger->trace(TAG, "createChannelLogo said = {}", sa_id);
269 ImageChannelLogo channelLogoImage = std::make_shared<CImageChannelLogo>();
270 channelLogoImage->type = CImage::CHANNELLOGO;
271 channelLogoImage->channel = channel;
272 channelLogoImage->staticArtworkID = sa_id;
274 Image image = channelLogoImage;
277 mImages.push_back(image);
278 mImagesLock.unlock();
281 return channelLogoImage;
284 void ImageLoader::ensureLoaded(ImageGeneric& ig)
290 void ImageLoader::ensureLoaded(Image& image)
292 image->stateLock.lock(); ///////// LOCK
294 if (!image->ready && !image->loading) // needs loading
296 switch (image->getType())
303 case CImage::GENERIC:
305 ImageGeneric ign = std::static_pointer_cast<CImageGeneric>(image);
306 VDR::getInstance()->loadImageGeneric(ign);
309 case CImage::RECFOLDER:
310 case CImage::RECTHUMB:
312 ImageRecThumb irt = std::static_pointer_cast<CImageRecThumb>(image);
313 VDR::getInstance()->loadImageRecThumb(irt);
316 case CImage::EVENTTHUMB:
318 ImageEventThumb iet = std::static_pointer_cast<CImageEventThumb>(image);
319 VDR::getInstance()->loadImageEventThumb(iet);
322 case CImage::CHANNELLOGO:
324 ImageChannelLogo icl = std::static_pointer_cast<CImageChannelLogo>(image);
325 VDR::getInstance()->loadImageChannelLogo(icl);
330 image->loading = true;
333 image->stateLock.unlock(); ///////// UNLOCK
336 void ImageLoader::downloadDone(VDR_ResponsePacket* vresp)
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
341 // TODO reimplement index-map
343 PictureReaderWorkItem p;
346 pictureReaderWorkLock.lock();
347 pictureReaderWork.push(p);
348 pictureReaderWorkLock.unlock();
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();
360 bool ImageLoader::pictureRendered(LoadingIndex index, OsdImage& oimage)
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
365 logger->trace(TAG, "pictureRendered: li {}", static_cast<void*>(index));
367 Image image = loadingIndexToImage(index);
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.
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);
379 std::lock_guard<std::mutex> lg(image->stateLock);
380 image->loading = false;
382 image->osdImage = oimage;
384 logger->trace(TAG, "pictureRendered: {}", static_cast<void*>(image.get()));
386 // Are there any images waiting for this one?
387 for(auto mImage : mImages)
389 if ((mImage->ready == false) && (mImage->otherImage == image))
391 mImage->osdImage = image->osdImage;
392 mImage->loading = false;
393 mImage->ready = true;
394 // FIXME ?? Clear mImage->otherImage?
401 Image ImageLoader::loadingIndexToImage(LoadingIndex index)
403 std::lock_guard<std::mutex> lg(mImagesLock);
405 for(Image& i : mImages)
407 //logger->trace(TAG, "li2I {} {}", (void*)index, (void*)i.get());
408 if (i.get() == static_cast<CImage*>(index)) return i;
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)
418 void ImageLoader::decodeStatic(Image& image)
420 PictureReaderWorkItem p;
422 pictureReaderWorkLock.lock();
423 pictureReaderWork.push(p);
424 pictureReaderWorkLock.unlock();
426 // Same split as above, separate locks for vector and signal
428 pictureReaderCondSigtex.lock();
429 pictureReaderThreadRun = true;
430 pictureReaderThreadCond.notify_one();
431 pictureReaderCondSigtex.unlock();
435 void ImageLoader::pictureReaderThreadMethod()
437 std::unique_lock<std::mutex> ul(pictureReaderCondSigtex); // Lock for most minimal time
440 pictureReaderThreadCond.wait(ul, [this]{ return pictureReaderThreadQuit || pictureReaderThreadRun; });
442 if (pictureReaderThreadQuit) return; // unlock
443 pictureReaderThreadRun = false;
446 // unlocked while we work
451 miniRunAgain = false;
454 decoders_lock.lock();
455 std::list<PictureDecoder*>::iterator itty = decoders.begin();
457 while (itty != decoders.end())
459 if ((*itty)->getDecodedPicture(pictinf)) // pictinf now contains handle (vgImage) and reference (a EGLImageKHR)
462 // logger->trace(TAG, "got decoded pic {}", (void*)pictinf.lindex );
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
470 pictureReaderProcessOne();
472 decoders_lock.unlock();
474 if (pictureReaderWork.size()) miniRunAgain = true;
476 } while(miniRunAgain);
482 void ImageLoader::pictureReaderProcessOne()
484 OsdVector* osdvector = dynamic_cast<OsdVector*>(Osd::getInstance());
486 pictureReaderWorkLock.lock();
487 if (pictureReaderWork.empty())
489 pictureReaderWorkLock.unlock();
493 PictureReaderWorkItem pwi;
494 pwi = pictureReaderWork.front();
495 pictureReaderWork.pop();
496 pictureReaderWorkLock.unlock();
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
502 // Set up the common variables
504 bool free_it = false;
511 // Now find the Image with this vresp->getStreamID() == image.serverLoadingRef
514 // logger->trace(TAG, "mImages size: {}", mImages.size());
515 // for(Image& i : mImages)
517 // logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
520 for(Image& i : mImages)
522 if (i->serverLoadingRef == pwi.vresp->getStreamID())
528 mImagesLock.unlock();
533 logger->trace(TAG, "Could not find Image for VRESP in download-done!!");
537 std::lock_guard<std::mutex> lg(image->stateLock);
539 if (pwi.vresp->getFlag() != 2) // Server image success
541 imageData = pwi.vresp->getUserData();
542 length = pwi.vresp->getUserDataLength();
547 image->serverLoadingRef = 0;
548 image->usingFallback = true;
549 logger->trace(TAG, "Fallback for {}", static_cast<void*>(image.get()));
559 // vresp is gone. Now we have a Image which might have some downloaded data
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
564 //logger->trace(TAG, "Still needed {} {}", (void*)image.get(), image->staticArtworkID);
566 // Set up a static decode?
567 if ( (image->getType() == CImage::STATIC)
568 || ((image->usingFallback == true) && (image->staticArtworkID != -1))
571 //logger->trace(TAG, "Do static {}", (void*)image.get());
573 // Do we already have this static decoded?
575 for(Image& i : mImages)
577 if (i == image) continue;
579 //logger->trace(TAG, "Image said: {} {}", (void*)i.get(), i->staticArtworkID);
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
586 if (i->ready) // Link osdImage, set ready, done.
588 image->osdImage = i->osdImage;
590 image->loading = false;
591 mImagesLock.unlock();
594 else // Link to the other image file and wait for it to be ready
596 image->otherImage = i;
597 mImagesLock.unlock();
602 mImagesLock.unlock();
604 // Continue with a new decode
606 osdvector->getStaticImageData(image->staticArtworkID, &imageData, &length);
613 //logger->trace(TAG, "Doing decode {}", (void*)image.get());
615 std::list<PictureDecoder*>::iterator it = decoders.begin();
617 while (it != decoders.end())
619 imageData = (*it)->decodePicture(image.get(), imageData, length, free_it);
620 //logger->trace(TAG, "Doing decode done {} {}", (void*)image.get(), (void*) imageData);
622 if (!imageData) break;
628 image->failed = true;
629 if (free_it) free(imageData);