Ticket #1681: osdimagecache-sizelimit-0.19-fixes.diff

File osdimagecache-sizelimit-0.19-fixes.diff, 17.1 KB (added by Pekka Jääskeläinen <pekka.jaaskelainen@…>, 18 years ago)

0.19-fixes

  • libs/libmythtv/osdtypes.h

     
    274274    int m_drawwidth;
    275275    bool m_onlyusefirst;
    276276
    277     OSDImageCache cache;
     277    static OSDImageCache cache;
     278    OSDImageCacheValue* m_cacheitem;
    278279};
    279280
    280281class OSDTypePosSlider : public OSDTypeImage
  • libs/libmythtv/osdtypes.cpp

     
    657657
    658658//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    659659
     660// share the OSD image cache between all OSDTypeImages by using a static
     661// class variable
     662OSDImageCache OSDTypeImage::cache;
     663
    660664OSDTypeImage::OSDTypeImage(const QString &name, const QString &filename,
    661665                           QPoint displaypos, float wmult, float hmult,
    662666                           int scalew, int scaleh)
     
    674678
    675679    m_scalew = scalew;
    676680    m_scaleh = scaleh;
     681    m_cacheitem = NULL;
    677682
    678683    LoadImage(filename, wmult, hmult, scalew, scaleh);
    679684}
     
    691696    m_name = other.m_name;
    692697    m_scalew = other.m_scalew;
    693698    m_scaleh = other.m_scaleh;
     699    m_cacheitem = NULL;
    694700
    695701    m_alpha = m_yuv = NULL;
    696702    if (m_isvalid)
     
    727733    m_vbuffer = NULL;
    728734    m_isvalid = false;
    729735    m_filename = "";
     736    m_cacheitem = NULL;
    730737}
    731738
    732739OSDTypeImage::OSDTypeImage(void)
     
    746753    m_vbuffer = NULL;
    747754    m_isvalid = false;
    748755    m_filename = "";
     756    m_cacheitem = NULL;
    749757}
    750758
    751759OSDTypeImage::~OSDTypeImage()
    752760{
    753     if (!cache.InMemCache())
    754     {
    755         if (m_yuv)
    756             delete [] m_yuv;
    757         if (m_alpha)
    758             delete [] m_alpha;   
    759     }
    760     cache.Reset();
     761    // in case we have a cache item in hand, it's safe to delete it, as it
     762    // should not be in OSDImageCache anymore (see Take()) and it should have
     763    // been written to the file cache for faster access in the future
     764    delete m_cacheitem;
     765    m_cacheitem = NULL;     
    761766}
    762767
    763768void OSDTypeImage::SetName(const QString &name)
     
    785790void OSDTypeImage::LoadImage(const QString &filename, float wmult, float hmult,
    786791                             int scalew, int scaleh)
    787792{
    788     // Try to get it from the cache first
    789     QString ckey = OSDImageCache::CreateKey(
    790         filename, wmult, hmult, scalew, scaleh);
    791     OSDImageCacheValue value = cache.Load(ckey, !filename.isEmpty());
     793    QString ckey;
     794 
     795    if (!filename.isEmpty() && filename.length() >= 2) {
     796        ckey = OSDImageCache::CreateKey(
     797            filename, wmult, hmult, scalew, scaleh);
     798    } else {
     799        // this method requires a backing file
     800        return;
     801    }
     802 
     803    // take the item from the cache so it's not freed while in use
     804    OSDImageCacheValue* value = cache.Take(ckey, true);
    792805
    793     if (value.IsValid())
     806    if (value != NULL)
    794807    {
    795         m_yuv       = value.m_yuv;
    796         m_ybuffer   = value.m_ybuffer;
    797         m_ubuffer   = value.m_ubuffer;
    798         m_vbuffer   = value.m_vbuffer;
    799         m_alpha     = value.m_alpha;
    800         m_imagesize = value.m_imagesize;
    801         m_isvalid   = true;
    802         return;
     808        m_yuv       = value->m_yuv;
     809        m_ybuffer   = value->m_ybuffer;
     810        m_ubuffer   = value->m_ubuffer;
     811        m_vbuffer   = value->m_vbuffer;
     812        m_alpha     = value->m_alpha;
     813        m_imagesize = value->m_imagesize;
     814        m_isvalid   = true;
     815        // put the old image back to the cache so it can be reused in the
     816        // future, and possibly freed by the cache system if the size limit
     817        // is reached
     818        if (m_cacheitem != NULL)
     819            cache.Insert(m_cacheitem);
     820        m_cacheitem = value;
     821        return;
    803822    }
    804 
    805     if (filename.length() < 2)
    806         return;
    807 
     823 
     824    // scaled image was not found in cache, have to create it
     825 
    808826    QImage tmpimage(filename);
    809827
    810828    if (tmpimage.width() == 0)
     
    843861
    844862    m_imagesize = QRect(0, 0, imwidth, imheight);
    845863
    846     cache.Save(ckey, !filename.isEmpty(),
    847                OSDImageCacheValue(m_yuv,     m_ybuffer, m_ubuffer,
    848                                   m_vbuffer, m_alpha,   m_imagesize));
     864    // put the old image back to the cache so it can be reused in the
     865    // future, and possibly freed by the cache system if the size limit
     866    // is reached
     867    if (m_cacheitem != NULL)
     868        cache.Insert(m_cacheitem);
     869 
     870    m_cacheitem = new OSDImageCacheValue(
     871        ckey,
     872        m_yuv,     m_ybuffer, m_ubuffer,
     873        m_vbuffer, m_alpha,   m_imagesize);
     874
     875    // save the new cache item to the file cache
     876    if (!filename.isEmpty())
     877        cache.SaveToDisk(m_cacheitem);
    849878}
    850879
    851880void OSDTypeImage::LoadFromQImage(const QImage &img)
     
    853882    // this method is not cached as it's used mostly for
    854883    // subtitles which are displayed only once anyways, caching
    855884    // would probably only slow things down overall
    856     if (m_isvalid && !cache.InMemCache())
     885    if (m_isvalid)
    857886    {
    858         if (m_yuv)
    859             delete [] m_yuv;
    860         if (m_alpha)
    861             delete [] m_alpha;
    862 
     887        delete m_cacheitem;
     888        m_cacheitem = NULL;
    863889        m_isvalid = false;
    864890        m_yuv = NULL;
    865891        m_alpha = NULL;
     
    11951221
    11961222    m_displaypos = m_displayrect.topLeft();
    11971223
    1198     if (!cache.InMemCache())
    1199     {
    1200         if (m_ryuv)
    1201             delete [] m_ryuv;
    1202         if (m_ralpha)
    1203             delete [] m_ralpha;
    1204     }
    1205 
    12061224    LoadImage(m_redname, wmult, hmult, m_scalew, m_scaleh);
    12071225    if (m_isvalid)
    12081226    {
  • libs/libmythtv/osdimagecache.h

     
    55#include <qrect.h>
    66#include <qmutex.h>
    77#include <qstring.h>
     8#include <qasciicache.h>
    89
     10// the maximum total size of OSD images cached in *memory*
     11#define OSD_MEMCACHE_MAX_SIZE 5000000
     12
     13// print statistics of OSD image access in the destructor of OSDImageCache
     14//#define PRINT_OSD_IMAGE_CACHE_STATS
     15
    916class OSDImageCacheValue
    1017{
    1118  public:
    12     OSDImageCacheValue(unsigned char *yuv,     unsigned char *ybuffer,
     19    OSDImageCacheValue(QString cacheKey,
     20                       unsigned char *yuv,     unsigned char *ybuffer,
    1321                       unsigned char *ubuffer, unsigned char *vbuffer,
    14                        unsigned char *alpha,   QRect imagesize) :
    15         m_yuv(yuv),         m_ybuffer(ybuffer),
    16         m_ubuffer(ubuffer), m_vbuffer(vbuffer),
    17         m_alpha(alpha),     m_imagesize(imagesize) {}
     22                       unsigned char *alpha,   QRect imagesize);
    1823
    19     OSDImageCacheValue() :
    20         m_yuv(NULL),     m_ybuffer(NULL),
    21         m_ubuffer(NULL), m_vbuffer(NULL),
    22         m_alpha(NULL),   m_imagesize(0,0,0,0) {}
     24    virtual ~OSDImageCacheValue();
    2325
    24     bool IsValid(void) const { return m_alpha; }
     26    int SizeInBytes() const { return m_size_in_bytes; }
     27    QString Key() const { return m_cacheKey; }
    2528
    2629    unsigned char *m_yuv;
    2730    unsigned char *m_ybuffer;
     
    2932    unsigned char *m_vbuffer;
    3033    unsigned char *m_alpha;
    3134    QRect          m_imagesize;
     35  private:
     36    int            m_size_in_bytes;
     37    QString        m_cacheKey;
    3238};
    3339
    34 typedef QMap<QString, OSDImageCacheValue> img_cache_t;
     40typedef QAsciiCache<OSDImageCacheValue> img_cache_t;
    3541
    3642class OSDImageCache
    3743{
    3844  public:
    39     OSDImageCache() : m_cacheLock(true), m_cached(false) {}
     45    OSDImageCache();
     46    virtual ~OSDImageCache();
    4047
    41     bool InMemCache(void) const { return m_cached; }
    4248    bool InFileCache(const QString &key) const;
    4349
    4450    bool Contains(const QString &key, bool useFile) const;
    4551
    46     OSDImageCacheValue Load(const QString &key, bool useFile);
     52    OSDImageCacheValue* Take(const QString &key, bool useFile);
     53    void Insert(OSDImageCacheValue* value);
    4754
    48     void Save(const QString &key, bool useFile,
    49               const OSDImageCacheValue &value);
     55    void SaveToDisk(const OSDImageCacheValue *value);
    5056
    5157    void Reset(void);
    5258
     
    5965  private:
    6066    mutable QMutex m_cacheLock;
    6167    img_cache_t    m_imageCache;
    62     bool           m_cached;
     68    int            m_memHits;
     69    int            m_diskHits;
     70    int            m_misses;
    6371};
  • libs/libmythtv/osdimagecache.cpp

     
    11// -*- Mode: c++ -*-
    22/** OSDImageCache
    3  *  Copyright (c) 2006 by Pekka Jaaskelainen, Daniel Thor Kristjansson
     3 *  Copyright (c) 2006 by Pekka Jääskeläinen, Daniel Thor Kristjansson
    44 *  Distributed as part of MythTV under GPL v2 and later.
    55 */
    66
     
    1717#include "mythcontext.h"
    1818#include "osdimagecache.h"
    1919
     20#ifdef PRINT_OSD_IMAGE_CACHE_STATS
     21#include <iostream>
     22using std::cerr;
     23using std::endl;
     24#define LOG_PREFIX "OSDImageCache: "
     25#endif
     26
    2027#define LOC QString("OSDImgCache: ")
    2128#define LOC_ERR QString("OSDImgCache, Error: ")
    2229
     30/**
     31 * \fn OSDImageCacheValue::OSDImageCacheValue(unsigned char*, unsigned char*,
     32 * unsighed char*, unsigned char*, unsighed char*, QRect)
     33 *
     34 * \brief The main constructor that takes the image data as arguments. The
     35 * image data becomes property of the OSDImageCacheValue and will be deleted
     36 * by it.
     37 *
     38 */
     39OSDImageCacheValue::OSDImageCacheValue(
     40    QString cacheKey,
     41    unsigned char *yuv,     unsigned char *ybuffer,
     42    unsigned char *ubuffer, unsigned char *vbuffer,
     43    unsigned char *alpha,   QRect imagesize) :
     44    m_yuv(yuv),         m_ybuffer(ybuffer),
     45    m_ubuffer(ubuffer), m_vbuffer(vbuffer),
     46    m_alpha(alpha),     m_imagesize(imagesize),
     47    m_cacheKey(cacheKey) {
     48
     49    uint yuv_size = m_imagesize.width() * m_imagesize.height() * 3 / 2;
     50    m_size_in_bytes =
     51        (sizeof(OSDImageCacheValue)) + yuv_size +
     52        (m_imagesize.width() * m_imagesize.height());
     53}
     54
     55/**
     56 * \fn OSDImageCacheValue::~OSDImageCacheValue()
     57 * \brief Destructor, frees the cached bitmaps.
     58 *
     59 */
     60OSDImageCacheValue::~OSDImageCacheValue() {
     61    delete [] m_yuv;
     62    m_yuv = NULL;
     63    delete [] m_alpha;
     64    m_alpha = NULL;
     65}
     66
     67
     68/**
     69 * \fn OSDImageCache::OSDImageCache()
     70 * \brief Constructor, initializes the internal cache structures.
     71 */
     72OSDImageCache::OSDImageCache() :
     73    m_cacheLock(true), m_imageCache(OSD_MEMCACHE_MAX_SIZE, 50),
     74    m_memHits(0), m_diskHits(0), m_misses(0)
     75{
     76
     77    // when the cache gets too large, items are automatically deleted from it
     78    // in LRU order
     79    m_imageCache.setAutoDelete(true);
     80}
     81
     82/**
     83 * \fn OSDImageCache::~OSDImageCache()
     84 * \brief Destructor, frees all cached OSD images.
     85 */
     86OSDImageCache::~OSDImageCache()
     87{
     88#ifdef PRINT_OSD_IMAGE_CACHE_STATS
     89    int totalAccess = m_memHits + m_diskHits + m_misses;
     90    if (totalAccess == 0)
     91        return;
     92    cout << LOG_PREFIX << " Statistics: " << std::endl
     93         << LOG_PREFIX << m_imageCache.totalCost() << " bytes in cache\n"
     94         << LOG_PREFIX << " memory hits: "
     95         << m_memHits << ", " << m_memHits*100.0/totalAccess << "%\n"
     96         << LOG_PREFIX << "   disk hits: "
     97         << m_diskHits << ", " << m_diskHits*100.0/totalAccess << "%\n"
     98         << LOG_PREFIX << "      misses: "
     99         << m_misses << ", " << m_misses*100.0/totalAccess << "%\n";
     100#endif
     101    Reset();
     102}
     103
    23104/** \fn OSDImageCache::Contains(const QString&,bool)
    24105 *  \brief Returns true if cached OSD image was found in the cache.
    25106 *
    26  *  \param imageKey The key for this image.
    27  *  \param useDiskCache If true, also look from the disk cache.
     107 *  \param key The key for this image.
     108 *  \param useFile If true, also look from the disk cache.
    28109 */
    29110bool OSDImageCache::Contains(const QString &key, bool useFile) const
    30111{
    31112    QMutexLocker locker(&m_cacheLock);
    32113
    33     if (m_imageCache.find(key) != m_imageCache.end())
     114    if (m_imageCache.find(key) != NULL)
    34115        return true;
    35116
    36117    if (!useFile)
     
    69150    return true;
    70151}
    71152
    72 /** \fn OSDImageCache::Load(const QString&,bool)
    73  *  \brief Returns OSD image data from cache.
     153/** \fn OSDImageCache::Take(const QString&,bool)
     154 *  \brief Returns OSD image data from cache and removes the image from
     155 *  the cache so it won't be deleted while in use. The deletion of the
     156 *  taken item becomes responsibility of the client. Returns NULL if item
     157 *  with the given key is not found.
    74158 *
    75159 *  \param key The key for this image.
    76  *  \param useFile If true, also look from the disk cache.
     160 *  \param useFile If true, also search the disk cache.
    77161 */
    78 OSDImageCacheValue OSDImageCache::Load(const QString &key, bool useFile)
     162OSDImageCacheValue* OSDImageCache::Take(const QString &key, bool useFile)
    79163{
    80164    QMutexLocker locker(&m_cacheLock);
    81     img_cache_t::const_iterator it = m_imageCache.find(key);
    82     if (it != m_imageCache.end())
    83         return *it;
     165    OSDImageCacheValue* item = m_imageCache.find(key);
     166    if (item != NULL) {
     167        m_memHits++;
     168        return m_imageCache.take(key);
     169    }
    84170
    85     OSDImageCacheValue val;
     171    if (!useFile || !InFileCache(key)) {
     172        m_misses++;
     173        return NULL;
     174    }
    86175
    87     if (!useFile || !InFileCache(key))
    88         return val;
    89 
    90176    QDir dir(MythContext::GetConfDir() + "/osdcache/");
    91177    QFile cacheFile(dir.path() + "/" + key);
    92178    cacheFile.open(IO_ReadOnly);
     
    103189    {
    104190        VERBOSE(VB_IMPORTANT, LOC_ERR + key + " wrong cache file size!"
    105191                << cacheFile.size() << " != " << tot_size);
    106         return val;
     192        return NULL;
    107193    }
    108194
    109     unsigned char *tmpA = new unsigned char[yuv_size];
    110     unsigned char *tmpB = new unsigned char[imwidth * imheight];
    111     stream.readRawBytes((char*)tmpA, yuv_size);
    112     stream.readRawBytes((char*)tmpB, imwidth * imheight);
     195    unsigned char *yuv = new unsigned char[yuv_size];
     196    unsigned char *alpha = new unsigned char[imwidth * imheight];
     197    stream.readRawBytes((char*)yuv, yuv_size);
     198    stream.readRawBytes((char*)alpha, imwidth * imheight);
    113199    cacheFile.close();
    114200
    115     OSDImageCacheValue value(tmpA, tmpA,
    116                              tmpA + (imwidth * imheight),
    117                              tmpA + (imwidth * imheight * 5 / 4),
    118                              tmpB, QRect(0, 0, imwidth, imheight));
    119 
    120     Save(key, false, value);
    121 
     201    OSDImageCacheValue* value =
     202        new OSDImageCacheValue(
     203            key,
     204            yuv, yuv,
     205            yuv + (imwidth * imheight),
     206            yuv + (imwidth * imheight * 5 / 4),
     207            alpha, QRect(0, 0, imwidth, imheight));
     208    m_diskHits++;
    122209    return value;
    123210}
    124211
    125 /** \fn OSDImageCache::Save(const QString&,bool,const OSDImageCacheValue&)
    126  *  \brief Saves OSD image data to cache.
     212/** \fn OSDImageCache::Insert(OSDImageCacheValue*)
     213 *  \brief Inserts OSD image data to memory cache. The item becomes property
     214 *  of the OSDImageCache and may be deleted any time by it.
    127215 *
    128  *  \param key The key for this image.
    129  *  \param useFile If true, also save to the disk cache.
     216 *  \param value The cache item.
    130217 */
    131 void OSDImageCache::Save(const QString &key, bool useFile,
    132                          const OSDImageCacheValue &value)
     218void OSDImageCache::Insert(OSDImageCacheValue *value)
    133219{
    134     if (Contains(key, useFile))
    135         return;
     220    if (value == NULL)
     221        return;
    136222
    137223    m_cacheLock.lock();
    138     m_imageCache[key] = value;
    139     m_cached = true;
     224    if (!m_imageCache.insert(value->Key(), value, value->SizeInBytes())) {
     225        VERBOSE(
     226            VB_IMPORTANT,
     227            LOC_ERR + QString("inserting image to memory cache failed"));
     228    }
    140229    m_cacheLock.unlock();
     230}
    141231
    142     if (!useFile)
     232
     233/** \fn OSDImageCache::SaveToDisk(const OSDImageCacheValue*)
     234 *  \brief Saves OSD image data to disk cache. Item is not written to the
     235 *  memory cache, i.e., it stays as property of the client.
     236 *
     237 *  \param value The cached OSD image to save.
     238 */
     239void OSDImageCache::SaveToDisk(const OSDImageCacheValue *value)
     240{
     241    if (InFileCache(value->Key()))
    143242        return;
    144243
    145244    QDir dir(MythContext::GetConfDir() + "/osdcache/");
     
    149248        return;
    150249    }
    151250
    152     QFile cacheFile(dir.path() + "/" + key);
     251    QFile cacheFile(dir.path() + "/" + value->Key());
    153252    if (!cacheFile.open(IO_WriteOnly | IO_Truncate))
    154253    {
    155254        VERBOSE(VB_IMPORTANT, LOC_ERR + "Creating osdcache file failed.");
    156255        return;
    157256    }
    158257
    159     uint32_t imwidth  = value.m_imagesize.width();
    160     uint32_t imheight = value.m_imagesize.height();
     258    uint32_t imwidth  = value->m_imagesize.width();
     259    uint32_t imheight = value->m_imagesize.height();
    161260    uint     yuv_size = imwidth * imheight * 3 / 2;
    162261
    163262    QDataStream stream(&cacheFile);
    164263    stream << imwidth << imheight;   
    165     stream.writeRawBytes((const char*)value.m_yuv, yuv_size);
    166     stream.writeRawBytes((const char*)value.m_alpha, imwidth * imheight);
     264    stream.writeRawBytes((const char*)value->m_yuv, yuv_size);
     265    stream.writeRawBytes((const char*)value->m_alpha, imwidth * imheight);
    167266    cacheFile.close();
    168267}
    169268
     
    190289
    191290void OSDImageCache::Reset(void)
    192291{
    193     // cleanup the OSD image cache in memory
    194292    QMutexLocker locker(&m_cacheLock);
    195     QMapIterator<QString, OSDImageCacheValue> i = m_imageCache.begin();
    196     while (i != m_imageCache.end())
    197     {
    198         OSDImageCacheValue& value = i.data();
    199         delete [] value.m_yuv;
    200         delete [] value.m_alpha;
    201         ++i;
    202     }
     293    // this also deletes the images due to setAutoDelete(true)
    203294    m_imageCache.clear();
    204     m_cached = false;
    205295}