Ticket #1681: osdimagecache-sizelimit-HEAD.diff

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

HEAD

  • libs/libmythtv/osdtypes.h

     
    338338    bool m_onlyusefirst;
    339339    bool m_dontround;
    340340
    341     OSDImageCache cache;
     341    static OSDImageCache cache;
     342    OSDImageCacheValue* m_cacheitem;
    342343};
    343344
    344345class OSDTypePosSlider : public OSDTypeImage
  • libs/libmythtv/osdtypes.cpp

     
    11001100
    11011101//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    11021102
     1103// share the OSD image cache between all OSDTypeImages by using a static
     1104// class variable
     1105OSDImageCache OSDTypeImage::cache;
     1106
    11031107OSDTypeImage::OSDTypeImage(const QString &name, const QString &filename,
    11041108                           QPoint displaypos, float wmult, float hmult,
    11051109                           int scalew, int scaleh)
     
    11171121
    11181122    m_scalew = scalew;
    11191123    m_scaleh = scaleh;
     1124    m_cacheitem = NULL;
    11201125    m_dontround = false;
    11211126
    11221127    LoadImage(filename, wmult, hmult, scalew, scaleh);
     
    11351140    m_name = other.m_name;
    11361141    m_scalew = other.m_scalew;
    11371142    m_scaleh = other.m_scaleh;
     1143    m_cacheitem = NULL;
    11381144    m_dontround = other.m_dontround;
    11391145
    11401146    m_alpha = m_yuv = NULL;
     
    11721178    m_vbuffer = NULL;
    11731179    m_isvalid = false;
    11741180    m_filename = "";
     1181    m_cacheitem = NULL;
    11751182    m_dontround = false;
    11761183}
    11771184
     
    11971204
    11981205OSDTypeImage::~OSDTypeImage()
    11991206{
    1200     if (!cache.InMemCache())
    1201     {
    1202         if (m_yuv)
    1203             delete [] m_yuv;
    1204         if (m_alpha)
    1205             delete [] m_alpha;   
    1206     }
    1207     cache.Reset();
     1207     // in case we have a cache item in hand, it's safe to delete it, as it
     1208     // should not be in OSDImageCache anymore (see Take()) and it should have
     1209     // been written to the file cache for faster access in the future
     1210     delete m_cacheitem;
     1211     m_cacheitem = NULL;
    12081212}
    12091213
    12101214void OSDTypeImage::SetName(const QString &name)
     
    12321236void OSDTypeImage::LoadImage(const QString &filename, float wmult, float hmult,
    12331237                             int scalew, int scaleh)
    12341238{
    1235     // Try to get it from the cache first
    1236     QString ckey = OSDImageCache::CreateKey(
    1237         filename, wmult, hmult, scalew, scaleh);
    1238     OSDImageCacheValue value = cache.Load(ckey, !filename.isEmpty());
    1239 
    1240     if (value.IsValid())
     1239    QString ckey;
     1240   
     1241    if (!filename.isEmpty() && filename.length() >= 2) {
     1242        ckey = OSDImageCache::CreateKey(
     1243            filename, wmult, hmult, scalew, scaleh);
     1244    } else {
     1245        // this method requires a backing file
     1246        return;
     1247    }
     1248 
     1249    // take the item from the cache so it's not freed while in use
     1250    OSDImageCacheValue* value = cache.Take(ckey, true);
     1251 
     1252    if (value != NULL)
    12411253    {
    1242         m_yuv       = value.m_yuv;
    1243         m_ybuffer   = value.m_ybuffer;
    1244         m_ubuffer   = value.m_ubuffer;
    1245         m_vbuffer   = value.m_vbuffer;
    1246         m_alpha     = value.m_alpha;
    1247         m_imagesize = value.m_imagesize;
    1248         m_isvalid   = true;
    1249         return;
     1254        m_yuv       = value->m_yuv;
     1255        m_ybuffer   = value->m_ybuffer;
     1256        m_ubuffer   = value->m_ubuffer;
     1257        m_vbuffer   = value->m_vbuffer;
     1258        m_alpha     = value->m_alpha;
     1259        m_imagesize = value->m_imagesize;
     1260        m_isvalid   = true;
     1261        // put the old image back to the cache so it can be reused in the
     1262        // future, and possibly freed by the cache system if the size limit
     1263        // is reached
     1264        if (m_cacheitem != NULL)
     1265            cache.Insert(m_cacheitem);
     1266        m_cacheitem = value;
     1267        return;
    12501268    }
    1251 
    1252     if (filename.length() < 2)
    1253         return;
    1254 
     1269   
     1270    // scaled image was not found in cache, have to create it
     1271 
    12551272    QImage tmpimage(filename);
    12561273
    12571274    if (tmpimage.width() == 0)
     
    12901307
    12911308    m_imagesize = QRect(0, 0, imwidth, imheight);
    12921309
    1293     cache.Save(ckey, !filename.isEmpty(),
    1294                OSDImageCacheValue(m_yuv,     m_ybuffer, m_ubuffer,
    1295                                   m_vbuffer, m_alpha,   m_imagesize));
     1310    // put the old image back to the cache so it can be reused in the
     1311    // future, and possibly freed by the cache system if the size limit
     1312    // is reached
     1313    if (m_cacheitem != NULL)
     1314        cache.Insert(m_cacheitem);
     1315 
     1316    m_cacheitem = new OSDImageCacheValue(
     1317        ckey,
     1318        m_yuv,     m_ybuffer, m_ubuffer,
     1319        m_vbuffer, m_alpha,   m_imagesize);
     1320 
     1321    // save the new cache item to the file cache
     1322    if (!filename.isEmpty())
     1323        cache.SaveToDisk(m_cacheitem);
    12961324}
    12971325
    12981326void OSDTypeImage::LoadFromQImage(const QImage &img)
     
    13001328    // this method is not cached as it's used mostly for
    13011329    // subtitles which are displayed only once anyways, caching
    13021330    // would probably only slow things down overall
    1303     if (m_isvalid && !cache.InMemCache())
     1331    if (m_isvalid)
    13041332    {
    1305         if (m_yuv)
    1306             delete [] m_yuv;
    1307         if (m_alpha)
    1308             delete [] m_alpha;
     1333        delete m_cacheitem;
     1334        m_cacheitem = NULL;
    13091335
    13101336        m_isvalid = false;
    13111337        m_yuv = NULL;
     
    15871613
    15881614    m_scalew = scalew;
    15891615    m_scaleh = scaleh;
     1616    m_cacheitem = NULL;
    15901617
    15911618    LoadImage(m_redname, wmult, hmult, scalew, scaleh);
    15921619    if (m_isvalid)
     
    16241651
    16251652    m_displaypos = m_displayrect.topLeft();
    16261653
    1627     if (!cache.InMemCache())
    1628     {
    1629         if (m_ryuv)
    1630             delete [] m_ryuv;
    1631         if (m_ralpha)
    1632             delete [] m_ralpha;
    1633     }
    1634 
    16351654    LoadImage(m_redname, wmult, hmult, m_scalew, m_scaleh);
    16361655    if (m_isvalid)
    16371656    {
  • 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}