From 131d13393f1aad919f7531b9d71877bd1c759921 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 4 Sep 2006 19:48:33 +0000 Subject: [PATCH] r858: Merge 2.1: Add garbage collection and reference-counting to Asset and CICacheItem classes. Additionally, the frame cache has been reworked considerably. Added new GarbageObject and Garbage classes. Classes being placed under garbage collection inherit from GarbageObject, which provides reference counting. The Garbage class provides the Garbage::garbage singleton which keeps track of all objects currently in use. Garbage::garbage->delete_object() is the only acceptable way of deleting a GarbageObject. Asset and CICacheItem now inherit from GarbageObject (via multiple inheritance). --- cinelerra/Makefile.am | 4 + cinelerra/amodule.C | 7 +- cinelerra/asset.C | 12 +- cinelerra/asset.h | 3 +- cinelerra/assetedit.C | 2 +- cinelerra/assets.C | 10 +- cinelerra/avc1394transport.C | 1 + cinelerra/awindowgui.C | 2 +- cinelerra/batch.C | 2 +- cinelerra/batchrender.C | 2 +- cinelerra/cache.C | 330 +++++++++++++++++++++++-------------------- cinelerra/cache.h | 44 +++--- cinelerra/cachebase.C | 202 ++++++++++++++++++++++++++ cinelerra/cachebase.h | 80 +++++++++++ cinelerra/cachebase.inc | 9 ++ cinelerra/edlsession.C | 8 ++ cinelerra/file.C | 3 +- cinelerra/framecache.C | 160 ++++++++------------- cinelerra/framecache.h | 32 ++--- cinelerra/garbage.C | 96 +++++++++++++ cinelerra/garbage.h | 77 ++++++++++ cinelerra/garbage.inc | 11 ++ cinelerra/indexfile.C | 2 +- cinelerra/main.C | 2 + cinelerra/mainindexes.C | 4 +- cinelerra/menueffects.C | 47 +++--- cinelerra/mwindow.C | 88 +++++++++--- cinelerra/mwindow.h | 6 +- cinelerra/packagerenderer.C | 6 +- cinelerra/playbackengine.C | 11 +- cinelerra/pluginarray.C | 2 +- cinelerra/preferences.C | 2 +- cinelerra/record.C | 2 +- cinelerra/render.C | 6 +- cinelerra/renderfarmclient.C | 2 +- cinelerra/resourcepixmap.C | 5 +- cinelerra/resourcepixmap.h | 3 + cinelerra/vedit.C | 3 +- cinelerra/vmodule.C | 3 +- cinelerra/vrender.C | 6 +- cinelerra/vwindow.C | 4 +- 41 files changed, 917 insertions(+), 384 deletions(-) create mode 100644 cinelerra/cachebase.C create mode 100644 cinelerra/cachebase.h create mode 100644 cinelerra/cachebase.inc create mode 100644 cinelerra/garbage.C create mode 100644 cinelerra/garbage.h create mode 100644 cinelerra/garbage.inc diff --git a/cinelerra/Makefile.am b/cinelerra/Makefile.am index c23c03a8..20f210c2 100644 --- a/cinelerra/Makefile.am +++ b/cinelerra/Makefile.am @@ -53,6 +53,7 @@ cinelerra_SOURCES = aattachmentpoint.C \ browsebutton.C \ byteorderpopup.C \ cache.C \ + cachebase.C \ canvas.C \ canvastools.C \ channel.C \ @@ -117,6 +118,7 @@ cinelerra_SOURCES = aattachmentpoint.C \ formatpresets.C \ formattools.C \ framecache.C \ + garbage.C \ gwindow.C \ gwindowgui.C \ indexfile.C \ @@ -345,6 +347,7 @@ noinst_HEADERS = aattachmentpoint.h \ byteorder.h \ byteorderpopup.h \ cache.h \ + cachebase.h \ cameraauto.h \ canvas.h \ canvastools.h \ @@ -415,6 +418,7 @@ noinst_HEADERS = aattachmentpoint.h \ formattools.h \ formatwindow.h \ framecache.h \ + garbage.h \ gwindow.h \ gwindowgui.h \ headers.h \ diff --git a/cinelerra/amodule.C b/cinelerra/amodule.C index 00c9a3ab..f8190d2e 100644 --- a/cinelerra/amodule.C +++ b/cinelerra/amodule.C @@ -208,7 +208,8 @@ int AModule::render(double *buffer, - if(!(source = get_cache()->check_out(playable_edit->asset))) + if(!(source = get_cache()->check_out(playable_edit->asset, + get_edl()))) { // couldn't open source file / skip the edit result = 1; @@ -284,7 +285,9 @@ int AModule::render(double *buffer, { File *source; get_cache()->age(); - if(!(source = get_cache()->check_out(previous_edit->asset))) + if(!(source = get_cache()->check_out( + previous_edit->asset, + get_edl()))) { // couldn't open source file / skip the edit printf(_("VirtualAConsole::load_track Couldn't open %s.\n"), playable_edit->asset->path); diff --git a/cinelerra/asset.C b/cinelerra/asset.C index 37c1bf3f..71486a90 100644 --- a/cinelerra/asset.C +++ b/cinelerra/asset.C @@ -15,24 +15,28 @@ #include -Asset::Asset() : ListItem() +Asset::Asset() + : ListItem(), GarbageObject("Asset") { init_values(); } -Asset::Asset(Asset &asset) : ListItem() +Asset::Asset(Asset &asset) + : ListItem(), GarbageObject("Asset") { init_values(); *this = asset; } -Asset::Asset(const char *path) : ListItem() +Asset::Asset(const char *path) + : ListItem(), GarbageObject("Asset") { init_values(); strcpy(this->path, path); } -Asset::Asset(const int plugin_type, const char *plugin_title) : ListItem() +Asset::Asset(const int plugin_type, const char *plugin_title) + : ListItem(), GarbageObject("Asset") { init_values(); } diff --git a/cinelerra/asset.h b/cinelerra/asset.h index fef54efa..ee289fa3 100644 --- a/cinelerra/asset.h +++ b/cinelerra/asset.h @@ -6,6 +6,7 @@ #include "bcwindowbase.inc" #include "bchash.inc" #include "filexml.inc" +#include "garbage.h" #include "linklist.h" #include "pluginserver.inc" @@ -23,7 +24,7 @@ // 2) an EDL // 3) a log // The EDL can reference itself if it contains a media file -class Asset : public ListItem +class Asset : public ListItem, public GarbageObject { public: Asset(); diff --git a/cinelerra/assetedit.C b/cinelerra/assetedit.C index 121a2b6a..23658535 100644 --- a/cinelerra/assetedit.C +++ b/cinelerra/assetedit.C @@ -116,7 +116,7 @@ void AssetEdit::run() } } - delete new_asset; + Garbage::delete_object(new_asset); delete window; window = 0; } diff --git a/cinelerra/assets.C b/cinelerra/assets.C index cd2771b4..cb373447 100644 --- a/cinelerra/assets.C +++ b/cinelerra/assets.C @@ -46,11 +46,12 @@ int Assets::load(ArrayList *plugindb, //printf("Assets::load 2\n"); char *path = file->tag.get_property("SRC"); //printf("Assets::load 3\n"); - Asset new_asset(path ? path : SILENCE); + Asset *new_asset = new Asset(path ? path : SILENCE); //printf("Assets::load 4\n"); - new_asset.read(file); + new_asset->read(file); //printf("Assets::load 5\n"); - update(&new_asset); + update(new_asset); + Garbage::delete_object(new_asset); //printf("Assets::load 6\n"); } } @@ -175,7 +176,8 @@ Asset* Assets::get_asset(const char *filename) Asset* Assets::remove_asset(Asset *asset) { - delete asset; + remove_pointer(asset); + Garbage::delete_object(asset); } diff --git a/cinelerra/avc1394transport.C b/cinelerra/avc1394transport.C index 0828e95a..6db50433 100644 --- a/cinelerra/avc1394transport.C +++ b/cinelerra/avc1394transport.C @@ -27,6 +27,7 @@ AVC1394TransportThread::~AVC1394TransportThread() void AVC1394TransportThread::run() { char *text; + sleep(5); while(!done) { Thread::disable_cancel(); diff --git a/cinelerra/awindowgui.C b/cinelerra/awindowgui.C index c665215b..2c6a3c81 100644 --- a/cinelerra/awindowgui.C +++ b/cinelerra/awindowgui.C @@ -142,7 +142,7 @@ void AssetPicon::create_objects() { if(mwindow->preferences->use_thumbnails) { - File *file = mwindow->video_cache->check_out(asset); + File *file = mwindow->video_cache->check_out(asset, mwindow->edl); if(file) { diff --git a/cinelerra/batch.C b/cinelerra/batch.C index cd604c81..7e6a2b3b 100644 --- a/cinelerra/batch.C +++ b/cinelerra/batch.C @@ -43,7 +43,7 @@ Batch::Batch(MWindow *mwindow, Record *record) Batch::~Batch() { for(int i = 0; i < assets.total; i++) - delete assets.values[i]; + Garbage::delete_object(assets.values[i]); assets.remove_all(); delete labels; if(edl) delete edl; diff --git a/cinelerra/batchrender.C b/cinelerra/batchrender.C index 969353d7..fd943b44 100644 --- a/cinelerra/batchrender.C +++ b/cinelerra/batchrender.C @@ -74,7 +74,7 @@ BatchRenderJob::BatchRenderJob(Preferences *preferences) BatchRenderJob::~BatchRenderJob() { - delete asset; + Garbage::delete_object(asset); } void BatchRenderJob::copy_from(BatchRenderJob *src) diff --git a/cinelerra/cache.C b/cinelerra/cache.C index 2ce47084..2f783ea0 100644 --- a/cinelerra/cache.C +++ b/cinelerra/cache.C @@ -2,6 +2,7 @@ #include "assets.h" #include "bcsignals.h" #include "cache.h" +#include "condition.h" #include "datatype.h" #include "edl.h" #include "edlsession.h" @@ -13,163 +14,187 @@ #include // edl came from a command which won't exist anymore -CICache::CICache(EDL *edl, - Preferences *preferences, +CICache::CICache(Preferences *preferences, ArrayList *plugindb) : List() { - this->edl = new EDL; - this->edl->create_objects(); - this->edl->copy_all(edl); this->plugindb = plugindb; this->preferences = preferences; - check_in_lock = new Mutex("CICache::check_in_lock"); - check_out_lock = new Mutex("CICache::check_out_lock"); + check_out_lock = new Condition(0, "CICache::check_out_lock", 0); total_lock = new Mutex("CICache::total_lock"); } CICache::~CICache() { - while(last) delete last; - delete edl; - delete check_in_lock; + while(last) + { + CICacheItem *item = last; +//printf("CICache::~CICache: %s\n", item->asset->path); + remove_pointer(item); + Garbage::delete_object(item); + } delete check_out_lock; delete total_lock; } -void CICache::set_edl(EDL *edl) -{ - this->edl->copy_all(edl); -} -void CICache::update(File* &file) -{ -// Check if exists - for(CICacheItem *current = first; current; current = NEXT) - { - if(!current->asset->test_path(file->asset->path)) - { - if(file != current->file) - { - delete file; - file = current->file; - } - return; - } - } - CICacheItem *item; - append(item = new CICacheItem(this, file)); - item->asset = new Asset(*(file->asset)); - file->set_asset(item->asset); -} -File* CICache::check_out(Asset *asset) -{ - File *result = 0; - check_out_lock->lock("CICache::check_out"); -// search for it in the cache +File* CICache::check_out(Asset *asset, EDL *edl, int block) +{ CICacheItem *current, *new_item = 0; - for(current = first; current && !new_item; current = NEXT) + while(1) { - if(!strcmp(current->asset->path, asset->path)) +// Scan directory for item + int got_it = 0; + total_lock->lock("CICache::check_out"); + for(current = first; current && !got_it; current = NEXT) { - current->counter = 0; - new_item = current; + if(!strcmp(current->asset->path, asset->path)) + { + got_it = 1; + break; + } } - } - -// didn't find it so create a new one - if(!new_item) - { - new_item = append(new CICacheItem(this, asset)); - } - if(new_item) - { - if(new_item->file) +// Test availability + if(got_it) { -// opened successfully - new_item->item_lock->lock("CICache::check_out"); - new_item->checked_out = 1; - - result = new_item->file; + if(!current->checked_out) + { +// Return it + current->age = EDL::next_id(); + current->checked_out = 1; + current->GarbageObject::add_user(); + total_lock->unlock(); + return current->file; + } } else { -// failed - delete new_item; - new_item = 0; - } - } +// Create new item + new_item = append(new CICacheItem(this, edl, asset)); + if(new_item->file) + { +// opened successfully. + new_item->age = EDL::next_id(); + new_item->checked_out = 1; + new_item->GarbageObject::add_user(); + total_lock->unlock(); + return new_item->file; + } +// Failed to open + else + { + remove_pointer(new_item); + Garbage::delete_object(new_item); + total_lock->unlock(); + return 0; + } + } -//printf("CICache::check_out %s\n", asset->path); - check_out_lock->unlock(); +// Try again after blocking + total_lock->unlock(); + if(block) + check_out_lock->lock("CICache::check_out"); + else + return 0; + } - return result; + return 0; } int CICache::check_in(Asset *asset) { - check_in_lock->lock("CICache::check_in"); - CICacheItem *current; - int result = 0; + int got_it = 0; + total_lock->lock("CICache::check_in"); - for(current = first; current && !result; current = NEXT) + for(current = first; current; current = NEXT) { -// Pointers are different +// Need to compare paths because +// asset pointers are different if(!strcmp(current->asset->path, asset->path)) { current->checked_out = 0; - current->item_lock->unlock(); - result = 1; + current->GarbageObject::remove_user(); +// Pointer no longer valid here + break; } } total_lock->unlock(); - check_in_lock->unlock(); +// Release for blocking check_out operations + check_out_lock->unlock(); age(); -//dump(); - return result; + return 0; +} + +void CICache::remove_all() +{ + total_lock->lock("CICache::remove_all"); + CICacheItem *current, *temp; + for(current = first; current; current = temp) + { + temp = current->next; +// Must not be checked out because we need the pointer to check back in. +// Really need to give the user the CacheItem. + if(!current->checked_out) + { +//printf("CICache::remove_all: %s\n", current->asset->path); + remove_pointer(current); + Garbage::delete_object(current); + } + } + total_lock->unlock(); } int CICache::delete_entry(char *path) { - Asset *asset = edl->assets->get_asset(path); - if(asset) delete_entry(asset); + total_lock->lock("CICache::delete_entry"); + for(CICacheItem *current = first; current; current = NEXT) + { + if(!strcmp(current->asset->path, path)) + { + if(!current->checked_out) + { +//printf("CICache::delete_entry: %s\n", current->asset->path); + remove_pointer(current); + Garbage::delete_object(current); + break; + } + } + } + total_lock->unlock(); return 0; } int CICache::delete_entry(Asset *asset) { - lock_all(); + total_lock->lock("CICache::delete_entry"); int result = 0; CICacheItem *current, *temp; - for(current = first; current; current = temp) + for(current = first; current; current = NEXT) { - temp = NEXT; - if(current->asset->equivalent(*asset, 0, 0)) + if(!strcmp(current->asset->path, asset->path)) { if(!current->checked_out) { - delete current; - } - else - { - printf("CICache::delete_entry asset checked out\n"); +//printf("CICache::delete_entry: %s\n", current->asset->path); + remove_pointer(current); + Garbage::delete_object(current); + break; } } - current = temp; } - unlock_all(); + total_lock->unlock(); return 0; } @@ -177,12 +202,8 @@ int CICache::age() { CICacheItem *current; - for(current = first; current; current = NEXT) - { - current->counter++; - } - // delete old assets if memory usage is exceeded + int64_t prev_memory_usage; int64_t memory_usage; int result = 0; do @@ -191,9 +212,14 @@ int CICache::age() if(memory_usage > preferences->cache_size) { +//printf("CICache::age 3 %p %lld %lld\n", this, memory_usage, preferences->cache_size); result = delete_oldest(); } - }while(memory_usage > preferences->cache_size && !result); + prev_memory_usage = memory_usage; + memory_usage = get_memory_usage(0); + }while(prev_memory_usage != memory_usage && + memory_usage > preferences->cache_size && + !result); } @@ -211,39 +237,69 @@ int64_t CICache::get_memory_usage(int use_lock) return result; } +int CICache::get_oldest() +{ + CICacheItem *current; + int oldest = 0x7fffffff; + total_lock->lock("CICache::get_oldest"); + for(current = last; current; current = PREVIOUS) + { + if(current->age < oldest) + { + oldest = current->age; + } + } + total_lock->unlock(); + + return oldest; +} + int CICache::delete_oldest() { CICacheItem *current; - int highest_counter = 1; + int lowest_age = 0x7fffffff; CICacheItem *oldest = 0; - for(current = last; current; current = PREVIOUS) + total_lock->lock("CICache::delete_oldest"); + + for(current = last; current; current = PREVIOUS) { - if(current->counter >= highest_counter) + if(current->age < lowest_age) { oldest = current; - highest_counter = current->counter; + lowest_age = current->age; } } -// if(highest_counter > 1 && oldest) + if(oldest) { - total_lock->lock("CICache::delete_oldest"); - - // Got the oldest file. Try requesting cache purge. + if(!oldest->file || oldest->file->purge_cache()) { + // Delete the file if cache already empty and not checked out. - if(!oldest->checked_out) delete oldest; + if(!oldest->checked_out) + { + + remove_pointer(oldest); + + Garbage::delete_object(oldest); + + } + } + total_lock->unlock(); - return 0; // success +// success + return 0; } else { - return 1; // nothing was old enough to delete + total_lock->unlock(); +// nothing was old enough to delete + return 1; } } @@ -254,11 +310,11 @@ int CICache::dump() printf("CICache::dump total size %lld\n", get_memory_usage(0)); for(current = first; current; current = NEXT) { - printf("cache item %x asset %x %s counter %lld\n", + printf("cache item %x asset %x %s age=%d\n", current, current->asset, current->asset->path, - current->counter); + current->age); } total_lock->unlock(); } @@ -271,37 +327,23 @@ int CICache::dump() -int CICache::lock_all() -{ - check_in_lock->lock("CICache::lock_all"); - check_out_lock->lock("CICache::lock_all"); -} - -int CICache::unlock_all() +CICacheItem::CICacheItem() +: ListItem(), GarbageObject("CICacheItem") { - check_in_lock->unlock(); - check_out_lock->unlock(); } - - - - - - - - - -// File not already opened. -CICacheItem::CICacheItem(CICache *cache, Asset *asset) - : ListItem() +CICacheItem::CICacheItem(CICache *cache, EDL *edl, Asset *asset) + : ListItem(), GarbageObject("CICacheItem") { int result = 0; - counter = 0; + age = EDL::next_id(); + this->asset = new Asset; - item_lock = new Mutex("CICacheItem::item_lock"); + + item_lock = new Condition(1, "CICacheItem::item_lock", 0); + // Must copy Asset since this belongs to an EDL which won't exist forever. *this->asset = *asset; this->cache = cache; @@ -310,11 +352,11 @@ CICacheItem::CICacheItem(CICache *cache, Asset *asset) file = new File; file->set_processors(cache->preferences->processors); - file->set_preload(cache->edl->session->playback_preload); + file->set_preload(edl->session->playback_preload); // Copy decoding parameters from session to asset so file can see them. - this->asset->divx_use_deblocking = cache->edl->session->mpeg4_deblock; + this->asset->divx_use_deblocking = edl->session->mpeg4_deblock; @@ -328,25 +370,9 @@ SET_TRACE } -// File already opened -CICacheItem::CICacheItem(CICache *cache, File *file) - : ListItem() -{ - counter = 0; - this->asset = new Asset; - item_lock = new Mutex("CICacheItem::item_lock"); - *this->asset = *file->asset; - this->file = file; - this->cache = cache; - checked_out = 0; - - file->set_processors(cache->preferences->processors); - file->set_preload(cache->edl->session->playback_preload); -} - CICacheItem::~CICacheItem() { - delete file; - delete asset; - delete item_lock; + if(file) delete file; + if(asset) Garbage::delete_object(asset); + if(item_lock) delete item_lock; } diff --git a/cinelerra/cache.h b/cinelerra/cache.h index eb8645d4..b0367549 100644 --- a/cinelerra/cache.h +++ b/cinelerra/cache.h @@ -14,8 +14,10 @@ #include "arraylist.h" #include "asset.inc" #include "cache.inc" +#include "condition.inc" #include "edl.inc" #include "file.inc" +#include "garbage.h" #include "linklist.h" #include "mutex.inc" #include "pluginserver.inc" @@ -23,19 +25,18 @@ #include -class CICacheItem : public ListItem +class CICacheItem : public ListItem, public GarbageObject { public: - CICacheItem(CICache *cache, File *file); - CICacheItem(CICache *cache, Asset *asset); - CICacheItem() {}; + CICacheItem(CICache *cache, EDL *edl, Asset *asset); + CICacheItem(); ~CICacheItem(); File *file; - int64_t counter; // number of age calls ago this asset was last needed - // assets used in the last render have counter == 1 +// Number of last get or put operation involving this object. + int age; Asset *asset; // Copy of asset. CICache should outlive EDLs. - Mutex *item_lock; + Condition *item_lock; int checked_out; private: CICache *cache; @@ -44,8 +45,7 @@ private: class CICache : public List { public: - CICache(EDL *edl, - Preferences *preferences, + CICache(Preferences *preferences, ArrayList *plugindb); ~CICache(); @@ -54,11 +54,13 @@ public: // Enter a new file into the cache which is already open. // If the file doesn't exist return the arguments. // If the file exists delete the arguments and return the file which exists. - void update(File* &file); - void set_edl(EDL *edl); +// void update(File* &file); +// void set_edl(EDL *edl); // open it, lock it and add it to the cache if it isn't here already - File* check_out(Asset *asset); +// If it's already checked out, the value of block causes it to wait +// until it's checked in. + File* check_out(Asset *asset, EDL *edl, int block = 1); // unlock a file from the cache int check_in(Asset *asset); @@ -67,12 +69,20 @@ public: // before deleting an asset, starting a new project or something int delete_entry(Asset *asset); int delete_entry(char *path); +// Remove all entries from the cache. + void remove_all(); +// Get ID of oldest member. +// Called by MWindow::age_caches. + int get_oldest(); int64_t get_memory_usage(int use_lock); +// Called by age() and MWindow::age_caches +// returns 1 if nothing was available to delete +// 0 if successful + int delete_oldest(); -// increment counters after rendering a buffer length -// since you can't know how big the cache is until after rendering the buffer +// Called by check_in() and modules. // deletes oldest assets until under the memory limit int age(); @@ -82,9 +92,6 @@ public: ArrayList *plugindb; private: -// returns 1 if nothing was available to delete -// 0 if successful - int delete_oldest(); // for deleting items int lock_all(); @@ -93,7 +100,8 @@ private: // to prevent one from checking the same asset out before it's checked in // yet without blocking the asset trying to get checked in // use a seperate mutex for checkouts and checkins - Mutex *check_in_lock, *check_out_lock, *total_lock; + Mutex *total_lock; + Condition *check_out_lock; // Copy of EDL EDL *edl; Preferences *preferences; diff --git a/cinelerra/cachebase.C b/cinelerra/cachebase.C new file mode 100644 index 00000000..c432a91a --- /dev/null +++ b/cinelerra/cachebase.C @@ -0,0 +1,202 @@ +#include "asset.h" +#include "bcsignals.h" +#include "cachebase.h" +#include "edl.h" +#include "mutex.h" + +#include + + + + +CacheItemBase::CacheItemBase() + : ListItem() +{ + age = 0; + asset_id = -1; + path = 0; +} + +CacheItemBase::~CacheItemBase() +{ + delete [] path; +} + + + + +int CacheItemBase::get_size() +{ + return 0; +} + + + + + +CacheBase::CacheBase() + : List() +{ + lock = new Mutex("CacheBase::lock"); + current_item = 0; +} + +CacheBase::~CacheBase() +{ + delete lock; +} + + + +int CacheBase::get_age() +{ + return EDL::next_id(); +} + + +// Called when done with the item returned by get_. +// Ignore if item was 0. +void CacheBase::unlock() +{ + lock->unlock(); +} + +void CacheBase::remove_all() +{ + int total = 0; + lock->lock("CacheBase::remove_all"); + while(last) + { + delete last; + total++; + } + current_item = 0; + lock->unlock(); +//printf("CacheBase::remove_all: removed %d entries\n", total); +} + + +void CacheBase::remove_asset(Asset *asset) +{ + int total = 0; + lock->lock("CacheBase::remove_id"); + for(current_item = first; current_item; ) + { + if(current_item->path && !strcmp(current_item->path, asset->path) || + current_item->asset_id == asset->id) + { + CacheItemBase *next = current_item->next; + delete current_item; + total++; + current_item = next; + } + else + current_item = current_item->next; + } + lock->unlock(); +//printf("CacheBase::remove_asset: removed %d entries for %s\n", total, asset->path); +} + +int CacheBase::get_oldest() +{ + int oldest = 0x7fffffff; + lock->lock("CacheBase::get_oldest"); + for(CacheItemBase *current = first; current; current = NEXT) + { + if(current->age < oldest) + oldest = current->age; + } + lock->unlock(); + return oldest; +} + + + +int CacheBase::delete_oldest() +{ + int oldest = 0x7fffffff; + CacheItemBase *oldest_item = 0; + + lock->lock("CacheBase::delete_oldest"); + for(CacheItemBase *current = first; current; current = NEXT) + { + if(current->age < oldest) + { + oldest = current->age; + oldest_item = current; + } + } + + if(oldest_item) + { +// Too much data to debug if audio. +// printf("CacheBase::delete_oldest: deleted position=%lld %d bytes\n", +// oldest_item->position, oldest_item->get_size()); + delete oldest_item; + if(current_item == oldest_item) current_item = 0; + lock->unlock(); + return 0; + } + + lock->unlock(); + return 1; +} + + +int64_t CacheBase::get_memory_usage() +{ + int64_t result = 0; + lock->lock("CacheBase::get_memory_usage"); + for(CacheItemBase *current = first; current; current = NEXT) + { + result += current->get_size(); + } + lock->unlock(); + return result; +} + +void CacheBase::put_item(CacheItemBase *item) +{ +// Get first position >= item + if(!current_item) current_item = first; + while(current_item && current_item->position < item->position) + current_item = current_item->next; + if(!current_item) current_item = last; + while(current_item && current_item->position >= item->position) + current_item = current_item->previous; + if(!current_item) + current_item = first; + else + current_item = current_item->next; + + if(!current_item) + { + append(item); + current_item = item; + } + else + insert_before(current_item, item); +} + +// Get first item from list with matching position or 0 if none found. +CacheItemBase* CacheBase::get_item(int64_t position) +{ + if(!current_item) current_item = first; + while(current_item && current_item->position < position) + current_item = current_item->next; + if(!current_item) current_item = last; + while(current_item && current_item->position >= position) + current_item = current_item->previous; + if(!current_item) + current_item = first; + else + if(current_item->next) + current_item = current_item->next; + if(!current_item || current_item->position != position) return 0; + return current_item; +} + + + + + diff --git a/cinelerra/cachebase.h b/cinelerra/cachebase.h new file mode 100644 index 00000000..ee84feaf --- /dev/null +++ b/cinelerra/cachebase.h @@ -0,0 +1,80 @@ +#ifndef CACHEBASE_H +#define CACHEBASE_H + + +#include "asset.inc" +#include "linklist.h" +#include "mutex.inc" +#include + + +// Store rendered elements from files but not the files. +// Drawing caches must be separate from file caches to avoid +// delaying other file accesses for the drawing routines. + + +class CacheItemBase : public ListItem +{ +public: + CacheItemBase(); + virtual ~CacheItemBase(); + + + + virtual int get_size(); + +// asset_id - supplied by user if the cache is not part of a file. +// Used for fast accesses. + int asset_id; +// path is needed since the item may need to be deleted based on file. +// Used for deletion. + char *path; +// Number of last get or put operation involving this object. + int age; +// Starting point of item in asset's native rate. + int64_t position; +}; + + + +class CacheBase : public List +{ +public: + CacheBase(); + virtual ~CacheBase(); + + int get_age(); + + void remove_all(); + +// Remove all items with the asset id. + void remove_asset(Asset *asset); + +// Insert item in list in ascending position order. + void put_item(CacheItemBase *item); + +// Get first item from list with matching position or 0 if none found. + CacheItemBase* get_item(int64_t position); + +// Called when done with the item returned by get_. +// Ignore if item was 0. + void unlock(); + +// Get ID of oldest member. +// Called by MWindow::age_caches. + int get_oldest(); + +// Delete oldest item. Return 0 if successful. Return 1 if nothing to delete. + int delete_oldest(); + +// Calculate current size of cache in bytes + int64_t get_memory_usage(); + + Mutex *lock; +// Current position of search + CacheItemBase *current_item; +}; + + + +#endif diff --git a/cinelerra/cachebase.inc b/cinelerra/cachebase.inc new file mode 100644 index 00000000..f7407b4e --- /dev/null +++ b/cinelerra/cachebase.inc @@ -0,0 +1,9 @@ +#ifndef CACHEBASE_INC +#define CACHEBASE_INC + + +class CacheBase; + + + +#endif diff --git a/cinelerra/edlsession.C b/cinelerra/edlsession.C index 68b4b784..a6b8fa72 100644 --- a/cinelerra/edlsession.C +++ b/cinelerra/edlsession.C @@ -63,6 +63,7 @@ EDLSession::~EDLSession() delete auto_conf; delete vconfig_in; delete playback_config; + Garbage::delete_object(recording_format); } @@ -184,6 +185,13 @@ int EDLSession::load_defaults(BC_Hash *defaults) record_sync_drives = defaults->get("RECORD_SYNC_DRIVES", 0); record_speed = defaults->get("RECORD_SPEED", 8); record_write_length = defaults->get("RECORD_WRITE_LENGTH", 131072); + recording_format->load_defaults(defaults, + "RECORD_", + 1, + 1, + 1, + 1, + 1); safe_regions = defaults->get("SAFE_REGIONS", 1); sample_rate = defaults->get("SAMPLERATE", 48000); scrub_speed = defaults->get("SCRUB_SPEED", (float)2); diff --git a/cinelerra/file.C b/cinelerra/file.C index 51522d20..b7eb0f29 100644 --- a/cinelerra/file.C +++ b/cinelerra/file.C @@ -61,8 +61,7 @@ File::~File() if(temp_frame) delete temp_frame; close_file(0); - reset_parameters(); - delete asset; + Garbage::delete_object(asset); delete format_completion; delete write_lock; if(frame_cache) delete frame_cache; diff --git a/cinelerra/framecache.C b/cinelerra/framecache.C index 9d38067e..f0b30ed0 100644 --- a/cinelerra/framecache.C +++ b/cinelerra/framecache.C @@ -11,18 +11,23 @@ FrameCacheItem::FrameCacheItem() + : CacheItemBase() { data = 0; position = 0; frame_rate = (double)30000.0 / 1001; - age = 0; } FrameCacheItem::~FrameCacheItem() { - if(data) delete data; + delete data; } +int FrameCacheItem::get_size() +{ + if(data) return data->get_data_size() + (path ? strlen(path) : 0); + return 0; +} @@ -39,16 +44,12 @@ FrameCacheItem::~FrameCacheItem() FrameCache::FrameCache() + : CacheBase() { - lock = new Mutex("FrameCache::lock"); - max_bytes = 0; - current_age = 0; } FrameCache::~FrameCache() { - items.remove_all_objects(); - delete lock; } @@ -58,21 +59,23 @@ int FrameCache::get_frame(VFrame *frame, double frame_rate) { lock->lock("FrameCache::get_frame"); - int item_number = -1; + FrameCacheItem *result = 0; if(frame_exists(frame, position, frame_rate, - &item_number)) + &result)) { - FrameCacheItem *item = items.values[item_number]; - if(item->data) frame->copy_from(item->data); - item->age = current_age; - current_age++; + if(result->data) + { + frame->copy_from(result->data); + frame->copy_stacks(result->data); + } + result->age = get_age(); } lock->unlock(); - if(item_number >= 0) return 1; + if(result) return 1; return 0; } @@ -83,34 +86,22 @@ VFrame* FrameCache::get_frame_ptr(int64_t position, int w, int h) { - lock->lock("FrameCache::get_frame"); - int item_number = -1; - FrameCacheItem *item = 0; + lock->lock("FrameCache::get_frame_ptr"); + FrameCacheItem *result = 0; if(frame_exists(position, frame_rate, color_model, w, h, - &item_number)) + &result)) { - item = items.values[item_number]; - item->age = current_age; - current_age++; + result->age = get_age(); + return result->data; } - if(item) - return item->data; - else - { - lock->unlock(); - return 0; - } -} - -void FrameCache::unlock() -{ - lock->unlock(); + lock->unlock(); + return 0; } // Puts frame in cache if enough space exists and the frame doesn't already @@ -121,21 +112,19 @@ void FrameCache::put_frame(VFrame *frame, int use_copy) { lock->lock("FrameCache::put_frame"); - int item_number = -1; + FrameCacheItem *item = 0; if(frame_exists(frame, position, frame_rate, - &item_number)) + &item)) { - FrameCacheItem *item = items.values[item_number]; - item->age = current_age; - current_age++; + item->age = get_age(); lock->unlock(); return; } - FrameCacheItem *item = new FrameCacheItem; + item = new FrameCacheItem; if(use_copy) { @@ -148,10 +137,9 @@ void FrameCache::put_frame(VFrame *frame, item->position = position; item->frame_rate = frame_rate; - item->age = current_age; + item->age = get_age(); - items.append(item); - current_age++; + put_item(item); lock->unlock(); } @@ -161,18 +149,19 @@ void FrameCache::put_frame(VFrame *frame, int FrameCache::frame_exists(VFrame *format, int64_t position, double frame_rate, - int *item_return) + FrameCacheItem **item_return) { - for(int i = 0; i < items.total; i++) + FrameCacheItem *item = (FrameCacheItem*)get_item(position); + while(item && item->position == position) { - FrameCacheItem *item = items.values[i]; - if(item->position == position && - EQUIV(item->frame_rate, frame_rate) && + if(EQUIV(item->frame_rate, frame_rate) && format->equivalent(item->data, 1)) { - *item_return = i; + *item_return = item; return 1; } + else + item = (FrameCacheItem*)item->next; } return 0; } @@ -182,77 +171,40 @@ int FrameCache::frame_exists(int64_t position, int color_model, int w, int h, - int *item_return) + FrameCacheItem **item_return) { - for(int i = 0; i < items.total; i++) + FrameCacheItem *item = (FrameCacheItem*)get_item(position); + while(item && item->position == position) { - FrameCacheItem *item = items.values[i]; - if(item->position == position && - EQUIV(item->frame_rate, frame_rate) && + if(EQUIV(item->frame_rate, frame_rate) && color_model == item->data->get_color_model() && w == item->data->get_w() && h == item->data->get_h()) { - *item_return = i; + *item_return = item; return 1; } + else + item = (FrameCacheItem*)item->next; } return 0; } -// Calculate current size of cache in bytes -int64_t FrameCache::get_memory_usage() -{ - int64_t result = 0; - lock->lock("FrameCache::get_memory_usage"); - for(int i = 0; i < items.total; i++) - { - FrameCacheItem *item = items.values[i]; - result += item->data->get_data_size(); - } - lock->unlock(); - return result; -} - -int FrameCache::delete_oldest() -{ - int64_t oldest = 0x7fffffff; - int oldest_item = -1; - - lock->lock("FrameCache::delete_oldest"); - for(int i = 0; i < items.total; i++) - { - if(items.values[i]->age < oldest) - { - oldest = items.values[i]->age; - oldest_item = i; - } - } - - if(oldest_item >= 0) - { - items.remove_object_number(oldest_item); - lock->unlock(); - return 0; - } - lock->unlock(); - return 1; -} void FrameCache::dump() { - lock->lock("FrameCache::dump"); - printf("FrameCache::dump 1 %d\n", items.total); - for(int i = 0; i < items.total; i++) - { - FrameCacheItem *item = items.values[i]; - printf(" position=%lld frame_rate=%f age=%d size=%d\n", - item->position, - item->frame_rate, - item->age, - item->data->get_data_size()); - } - lock->unlock(); +// lock->lock("FrameCache::dump"); +// printf("FrameCache::dump 1 %d\n", items.total); +// for(int i = 0; i < items.total; i++) +// { +// FrameCacheItem *item = (FrameCacheItem*)items.values[i]; +// printf(" position=%lld frame_rate=%f age=%d size=%d\n", +// item->position, +// item->frame_rate, +// item->age, +// item->data->get_data_size()); +// } +// lock->unlock(); } diff --git a/cinelerra/framecache.h b/cinelerra/framecache.h index b7cbc961..e42ce71c 100644 --- a/cinelerra/framecache.h +++ b/cinelerra/framecache.h @@ -2,7 +2,7 @@ #define FRAMECACHE_H -#include "arraylist.h" +#include "cachebase.h" #include "mutex.inc" #include "vframe.inc" @@ -11,23 +11,24 @@ // Simply a table of images described by frame position and dimensions. // The frame position is relative to the frame rate of the source file. -// This object is held by File. CICache scans all the files for +// This object is used by File for playback. +// and MWindow for timeline drawing. +// CICache scans all the files for // frame caches and deletes what's needed to maintain the cache size. -class FrameCacheItem +class FrameCacheItem : public CacheItemBase { public: FrameCacheItem(); ~FrameCacheItem(); + int get_size(); + VFrame *data; - int64_t position; double frame_rate; -// Number of last get or put operation involving this object. - int age; }; -class FrameCache +class FrameCache : public CacheBase { public: FrameCache(); @@ -46,7 +47,6 @@ public: int color_model, int w, int h); - void unlock(); // Puts the frame in cache. // use_copy - if 1 a copy of the frame is made. if 0 the argument is stored. // The copy of the frame is deleted by FrameCache in a future delete_oldest. @@ -55,11 +55,6 @@ public: double frame_rate, int use_copy); -// Delete oldest item. Return 0 if successful. Return 1 if nothing to delete. - int delete_oldest(); - -// Calculate current size of cache in bytes - int64_t get_memory_usage(); void dump(); @@ -71,20 +66,13 @@ private: int frame_exists(VFrame *format, int64_t position, double frame_rate, - int *item_return); + FrameCacheItem **item_return); int frame_exists(int64_t position, double frame_rate, int color_model, int w, int h, - int *item_return); - - Mutex *lock; -// Current get or put operation since creation of FrameCache object - int current_age; - ArrayList items; -// Maximum size of cache in bytes. - int64_t max_bytes; + FrameCacheItem **item_return); }; diff --git a/cinelerra/garbage.C b/cinelerra/garbage.C new file mode 100644 index 00000000..57844dbb --- /dev/null +++ b/cinelerra/garbage.C @@ -0,0 +1,96 @@ +#include "bcsignals.h" +#include "garbage.h" +#include "mutex.h" + +#include + +Garbage *Garbage::garbage = 0; + + +GarbageObject::GarbageObject(char *title) +{ + Garbage::garbage->add_object(this); + users = 0; + deleted = 0; + this->title = new char[strlen(title) + 1]; + strcpy(this->title, title); +} + +GarbageObject::~GarbageObject() +{ + if(!deleted) + printf("GarbageObject::~GarbageObject: title=%s users=%d was not deleted by Garbage::delete_object\n", title, users); + delete [] title; +} + + +void GarbageObject::add_user() +{ + Garbage::garbage->lock->lock("GarbageObject::add_user"); + users++; + Garbage::garbage->lock->unlock(); +} + +void GarbageObject::remove_user() +{ + Garbage::garbage->lock->lock("GarbageObject::add_user"); + users--; + if(users < 0) printf("GarbageObject::remove_user: users=%d Should be >= 0.\n", users); + Garbage::garbage->remove_expired(); +// *this is now invalid + Garbage::garbage->lock->unlock(); +} + + + + + +Garbage::Garbage() +{ + lock = new Mutex("Garbage::lock", 1); +} + + +Garbage::~Garbage() +{ + delete lock; +} + +void Garbage::add_object(GarbageObject *ptr) +{ + lock->lock("Garbage::add_object"); + objects.append(ptr); + lock->unlock(); +} + +void Garbage::delete_object(GarbageObject *ptr) +{ + Garbage *garbage = Garbage::garbage; + garbage->lock->lock("Garbage::delete_object"); + ptr->deleted = 1; + +// Remove expired objects here + remove_expired(); + garbage->lock->unlock(); +} + +void Garbage::remove_expired() +{ + Garbage *garbage = Garbage::garbage; + for(int i = 0; i < garbage->objects.total; i++) + { + GarbageObject *ptr = garbage->objects.values[i]; + if(ptr->users <= 0 && ptr->deleted) + { +// Must remove pointer to prevent recursive deletion of the same object. +// But i is still invalid. + garbage->objects.remove_number(i); + + delete ptr; + i--; + } + } +} + + + diff --git a/cinelerra/garbage.h b/cinelerra/garbage.h new file mode 100644 index 00000000..eb1948e5 --- /dev/null +++ b/cinelerra/garbage.h @@ -0,0 +1,77 @@ +#ifndef GARBAGE_H +#define GARBAGE_H + + +#include "arraylist.h" +#include "garbage.inc" +#include "mutex.inc" + +// Garbage collection +// The objects inherit from GarbageObject. +// The constructor sets users to 0 so the caller must call add_user if it +// wants to use it. If it doesn't intend to use it after calling the constructor, +// it should not call add_user. +// Other users of the object must call add_user to increment the user count +// and remove_user to decriment the user count. +// The object is only deleted if a call to Garbage::delete_object is made and +// the user count is 0. +// A user who is using it and wants to delete it must first call +// remove_user and then call Garbage::delete_object. + + +// They are deleted by calling delete_object. They get deleted at a +// random point later on. The objects must not change anything in their +// destructors. + +// Can't make graphics elements inherit because they must be deleted +// when the window is locked and they change their parent pointers. + +// Elements of link lists must first be unlinked with remove_pointer and then +// passed to delete_object. + +// ArrayList objects must be deleted one at a time with delete_object. +// Then the pointers must be deleted with remove_all. +class GarbageObject +{ +public: + GarbageObject(char *title); + virtual ~GarbageObject(); + +// Called when user begins to use the object. + void add_user(); +// Called when user is done with the object. + void remove_user(); + + int users; + int deleted; + char *title; +}; + + + +class Garbage +{ +public: + Garbage(); + ~Garbage(); + +// Called by GarbageObject constructor + void add_object(GarbageObject *ptr); + +// Called by user to delete the object. +// Flags the object for deletion as soon as it has no users. + static void delete_object(GarbageObject *ptr); + + +// Called by remove_user and delete_object + static void remove_expired(); + Mutex *lock; + ArrayList objects; + +// Global garbage collector + static Garbage *garbage; +}; + + + +#endif diff --git a/cinelerra/garbage.inc b/cinelerra/garbage.inc new file mode 100644 index 00000000..a5f5492b --- /dev/null +++ b/cinelerra/garbage.inc @@ -0,0 +1,11 @@ +#ifndef GARBAGE_INC +#define GARBAGE_INC + + + +class Garbage; +class GarbageObject; + + + +#endif diff --git a/cinelerra/indexfile.C b/cinelerra/indexfile.C index 3c1d27e6..ca2f298c 100644 --- a/cinelerra/indexfile.C +++ b/cinelerra/indexfile.C @@ -143,7 +143,7 @@ int IndexFile::open_file() fseek(file, 0, SEEK_SET); result = 0; } - delete test_asset; + Garbage::delete_object(test_asset); } else { diff --git a/cinelerra/main.C b/cinelerra/main.C index fd523074..71ef0737 100644 --- a/cinelerra/main.C +++ b/cinelerra/main.C @@ -5,6 +5,7 @@ #include "edl.h" #include "filexml.h" #include "filesystem.h" +#include "garbage.h" #include "language.h" #include "loadfile.inc" #include "mainmenu.h" @@ -53,6 +54,7 @@ int main(int argc, char *argv[]) config_path[0] = 0; batch_path[0] = 0; deamon_path[0] = 0; + Garbage::garbage = new Garbage; EDL::id_lock = new Mutex("EDL::id_lock"); // detect an UTF-8 locale and try to use a non-Unicode locale instead diff --git a/cinelerra/mainindexes.C b/cinelerra/mainindexes.C index 48342227..4b032ed6 100644 --- a/cinelerra/mainindexes.C +++ b/cinelerra/mainindexes.C @@ -108,7 +108,9 @@ void MainIndexes::add_next_asset(File *file, Asset *asset) void MainIndexes::delete_current_assets() { - current_assets.remove_all_objects(); + for(int i = 0; i < current_assets.total; i++) + Garbage::delete_object(current_assets.values[i]); + current_assets.remove_all(); } void MainIndexes::start_loop() diff --git a/cinelerra/menueffects.C b/cinelerra/menueffects.C index fe91c919..7a6be76a 100644 --- a/cinelerra/menueffects.C +++ b/cinelerra/menueffects.C @@ -100,18 +100,19 @@ void MenuEffectThread::run() int i; int result = 0; // Default configuration - Asset default_asset; + Asset *default_asset = new Asset; // Output ArrayList assets; // check for recordable tracks - if(!get_recordable_tracks(&default_asset)) + if(!get_recordable_tracks(default_asset)) { sprintf(string, _("No recordable tracks specified.")); ErrorBox error(PROGRAM_NAME ": Error"); error.create_objects(string); error.run_window(); + Garbage::delete_object(default_asset); return; } @@ -122,13 +123,14 @@ void MenuEffectThread::run() ErrorBox error(PROGRAM_NAME ": Error"); error.create_objects(string); error.run_window(); + Garbage::delete_object(default_asset); return; } // get default attributes for output file // used after completion - get_derived_attributes(&default_asset, defaults); + get_derived_attributes(default_asset, defaults); // to_tracks = defaults->get("RENDER_EFFECT_TO_TRACKS", 1); load_mode = defaults->get("RENDER_EFFECT_LOADMODE", LOAD_PASTE); strategy = defaults->get("RENDER_EFFECT_STRATEGY", SINGLE_PASS); @@ -143,8 +145,8 @@ void MenuEffectThread::run() // generate a list of plugins for the window if(need_plugin) { - mwindow->create_plugindb(default_asset.audio_data, - default_asset.video_data, + mwindow->create_plugindb(default_asset->audio_data, + default_asset->video_data, -1, 0, 0, @@ -165,7 +167,7 @@ void MenuEffectThread::run() MenuEffectWindow window(mwindow, this, need_plugin ? &plugin_list : 0, - &default_asset); + default_asset); window.create_objects(); result = window.run_window(); plugin_number = window.result; @@ -173,13 +175,13 @@ void MenuEffectThread::run() if(!result) { - FormatCheck format_check(&default_asset); + FormatCheck format_check(default_asset); format_error = format_check.check_format(); } }while(format_error && !result); // save defaults - save_derived_attributes(&default_asset, defaults); + save_derived_attributes(default_asset, defaults); defaults->update("RENDER_EFFECT_LOADMODE", load_mode); defaults->update("RENDER_EFFECT_STRATEGY", strategy); mwindow->save_defaults(); @@ -215,7 +217,7 @@ void MenuEffectThread::run() fix_menu(title); } - if(!result && !strlen(default_asset.path)) + if(!result && !strlen(default_asset->path)) { result = 1; // no output path given ErrorBox error(PROGRAM_NAME ": Error"); @@ -303,8 +305,8 @@ void MenuEffectThread::run() // Close plugin. plugin->save_data(&plugin_data); delete plugin; - default_asset.sample_rate = mwindow->edl->session->sample_rate; - default_asset.frame_rate = mwindow->edl->session->frame_rate; + default_asset->sample_rate = mwindow->edl->session->sample_rate; + default_asset->frame_rate = mwindow->edl->session->frame_rate; realtime = 1; } else @@ -314,22 +316,22 @@ void MenuEffectThread::run() plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0, -1); result = plugin->get_parameters((int64_t)total_start, (int64_t)total_end, - get_recordable_tracks(&default_asset)); + get_recordable_tracks(default_asset)); // some plugins can change the sample rate and the frame rate if(!result) { - default_asset.sample_rate = plugin->get_samplerate(); - default_asset.frame_rate = plugin->get_framerate(); + default_asset->sample_rate = plugin->get_samplerate(); + default_asset->frame_rate = plugin->get_framerate(); } delete plugin; realtime = 0; } // Should take from first recordable track - default_asset.width = mwindow->edl->session->output_w; - default_asset.height = mwindow->edl->session->output_h; + default_asset->width = mwindow->edl->session->output_w; + default_asset->height = mwindow->edl->session->output_h; } // Process the total length in fragments @@ -342,7 +344,7 @@ void MenuEffectThread::run() int current_number; int number_start; int total_digits; - Render::get_starting_number(default_asset.path, + Render::get_starting_number(default_asset->path, current_number, number_start, total_digits); @@ -374,12 +376,12 @@ void MenuEffectThread::run() char path[BCTEXTLEN]; if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM) Render::create_filename(path, - default_asset.path, + default_asset->path, current_number, total_digits, number_start); else - strcpy(path, default_asset.path); + strcpy(path, default_asset->path); current_number++; MenuEffectPacket *packet = new MenuEffectPacket(path, @@ -405,7 +407,7 @@ void MenuEffectThread::run() current_packet < packets.total && !result; current_packet++) { - Asset *asset = new Asset(default_asset); + Asset *asset = new Asset(*default_asset); MenuEffectPacket *packet = packets.values[current_packet]; int64_t fragment_start = packet->start; int64_t fragment_end = packet->end; @@ -506,7 +508,10 @@ void MenuEffectThread::run() mwindow->gui->unlock_window(); } - assets.remove_all_objects(); + for(int i = 0; i < assets.total; i++) + Garbage::delete_object(assets.values[i]); + assets.remove_all(); + Garbage::delete_object(default_asset); } diff --git a/cinelerra/mwindow.C b/cinelerra/mwindow.C index 593077f6..f113fae4 100644 --- a/cinelerra/mwindow.C +++ b/cinelerra/mwindow.C @@ -628,8 +628,8 @@ void MWindow::init_viewer() void MWindow::init_cache() { - audio_cache = new CICache(edl, preferences, plugindb); - video_cache = new CICache(edl, preferences, plugindb); + audio_cache = new CICache(preferences, plugindb); + video_cache = new CICache(preferences, plugindb); } void MWindow::init_channeldb() @@ -883,7 +883,7 @@ SET_TRACE SET_TRACE new_edls.append(new_edl); SET_TRACE - delete new_asset; + Garbage::delete_object(new_asset); new_asset = 0; SET_TRACE } @@ -990,7 +990,7 @@ SET_TRACE { asset_to_edl(new_edl, new_asset); new_edls.append(new_edl); - delete new_asset; + Garbage::delete_object(new_asset); new_asset = 0; } else @@ -1036,7 +1036,7 @@ SET_TRACE if(result) { delete new_edl; - delete new_asset; + Garbage::delete_object(new_asset); new_edl = 0; new_asset = 0; } @@ -1109,8 +1109,9 @@ SET_TRACE } mainindexes->add_next_asset(got_it ? new_file : 0, - new_assets.values[i]); - edl->assets->update(new_assets.values[i]); + new_asset); + edl->assets->update(new_asset); + } @@ -1126,7 +1127,10 @@ SET_TRACE new_edls.remove_all_objects(); SET_TRACE - new_assets.remove_all_objects(); + for(int i = 0; i < new_assets.total; i++) + Garbage::delete_object(new_assets.values[i]); +SET_TRACE + new_assets.remove_all(); SET_TRACE new_files.remove_all_objects(); @@ -1490,10 +1494,34 @@ void MWindow::sync_parameters(int change_type) } } -void MWindow::update_caches() +void MWindow::age_caches() { - audio_cache->set_edl(edl); - video_cache->set_edl(edl); + int64_t prev_memory_usage; + int64_t memory_usage; + int result = 0; + do + { + memory_usage = audio_cache->get_memory_usage(1) + + video_cache->get_memory_usage(1); + + if(memory_usage > preferences->cache_size) + { + int target = 1; + int oldest1 = audio_cache->get_oldest(); + int oldest2 = video_cache->get_oldest(); + if(oldest2 < oldest1) target = 2; + switch(target) + { + case 1: audio_cache->delete_oldest(); break; + case 2: video_cache->delete_oldest(); break; + } + } + prev_memory_usage = memory_usage; + memory_usage = audio_cache->get_memory_usage(1) + + video_cache->get_memory_usage(1); + }while(!result && + prev_memory_usage != memory_usage && + memory_usage > preferences->cache_size); } void MWindow::show_plugin(Plugin *plugin) @@ -1751,8 +1779,6 @@ void MWindow::update_project(int load_mode) restart_brender(); edl->tracks->update_y_pixels(theme); -// Draw timeline - update_caches(); gui->update(1, 1, 1, 1, 1, 1, 1); @@ -1846,20 +1872,44 @@ int MWindow::create_aspect_ratio(float &w, float &h, int width, int height) return 0; } +void MWindow::reset_caches() +{ + audio_cache->remove_all(); + video_cache->remove_all(); + if(cwindow->playback_engine && cwindow->playback_engine->audio_cache) + cwindow->playback_engine->audio_cache->remove_all(); + if(cwindow->playback_engine && cwindow->playback_engine->video_cache) + cwindow->playback_engine->video_cache->remove_all(); + if(vwindow->playback_engine && vwindow->playback_engine->audio_cache) + vwindow->playback_engine->audio_cache->remove_all(); + if(vwindow->playback_engine && vwindow->playback_engine->video_cache) + vwindow->playback_engine->video_cache->remove_all(); +} + +void MWindow::remove_asset_from_caches(Asset *asset) +{ + audio_cache->delete_entry(asset); + video_cache->delete_entry(asset); + if(cwindow->playback_engine && cwindow->playback_engine->audio_cache) + cwindow->playback_engine->audio_cache->delete_entry(asset); + if(cwindow->playback_engine && cwindow->playback_engine->video_cache) + cwindow->playback_engine->video_cache->delete_entry(asset); + if(vwindow->playback_engine && vwindow->playback_engine->audio_cache) + vwindow->playback_engine->audio_cache->delete_entry(asset); + if(vwindow->playback_engine && vwindow->playback_engine->video_cache) + vwindow->playback_engine->video_cache->delete_entry(asset); +} + void MWindow::remove_assets_from_project(int push_undo) { -// Remove from caches for(int i = 0; i < session->drag_assets->total; i++) { - audio_cache->delete_entry(session->drag_assets->values[i]); - video_cache->delete_entry(session->drag_assets->values[i]); + Asset *asset = session->drag_assets->values[i]; + remove_asset_from_caches(asset); } -video_cache->dump(); -audio_cache->dump(); - // Remove from VWindow. for(int i = 0; i < session->drag_clips->total; i++) { diff --git a/cinelerra/mwindow.h b/cinelerra/mwindow.h index 8b1063a8..b06b1a46 100644 --- a/cinelerra/mwindow.h +++ b/cinelerra/mwindow.h @@ -330,7 +330,9 @@ public: void paste_audio_transition(); void paste_video_transition(); void rebuild_indices(); -// Asset removal +// Asset removal from caches + void reset_caches(); + void remove_asset_from_caches(Asset *asset); void remove_assets_from_project(int push_undo = 0); void remove_assets_from_disk(); void resize_track(Track *track, int w, int h); @@ -375,7 +377,7 @@ public: // Send new EDL to caches - void update_caches(); + void age_caches(); int optimize_assets(); // delete unused assets from the cache and assets diff --git a/cinelerra/packagerenderer.C b/cinelerra/packagerenderer.C index b30391ff..e0c9fedc 100644 --- a/cinelerra/packagerenderer.C +++ b/cinelerra/packagerenderer.C @@ -110,8 +110,8 @@ int PackageRenderer::initialize(MWindow *mwindow, command->get_edl()->session->aspect_h; result = Render::check_asset(edl, *default_asset); - audio_cache = new CICache(command->get_edl(), preferences, plugindb); - video_cache = new CICache(command->get_edl(), preferences, plugindb); + audio_cache = new CICache(preferences, plugindb); + video_cache = new CICache(preferences, plugindb); PlaybackConfig *config = command->get_edl()->session->playback_config; aconfig = new AudioOutConfig(0); @@ -479,7 +479,7 @@ void PackageRenderer::close_output() mwindow->sighandler->pull_file(file); file->close_file(); delete file; - delete asset; + Garbage::delete_object(asset); } // Aborts and returns 1 if an error is encountered. diff --git a/cinelerra/playbackengine.C b/cinelerra/playbackengine.C index ddb62450..098d868f 100644 --- a/cinelerra/playbackengine.C +++ b/cinelerra/playbackengine.C @@ -147,14 +147,9 @@ void PlaybackEngine::create_cache() if(video_cache) delete video_cache; video_cache = 0; if(!audio_cache) - audio_cache = new CICache(command->get_edl(), preferences, mwindow->plugindb); - else - audio_cache->set_edl(command->get_edl()); - + audio_cache = new CICache(preferences, mwindow->plugindb); if(!video_cache) - video_cache = new CICache(command->get_edl(), preferences, mwindow->plugindb); - else - video_cache->set_edl(command->get_edl()); + video_cache = new CICache(preferences, mwindow->plugindb); } @@ -165,8 +160,6 @@ void PlaybackEngine::perform_change() case CHANGE_ALL: create_cache(); case CHANGE_EDL: - audio_cache->set_edl(command->get_edl()); - video_cache->set_edl(command->get_edl()); create_render_engine(); case CHANGE_PARAMS: if(command->change_type != CHANGE_EDL && diff --git a/cinelerra/pluginarray.C b/cinelerra/pluginarray.C index 4cd58af2..3ee8d727 100644 --- a/cinelerra/pluginarray.C +++ b/cinelerra/pluginarray.C @@ -50,7 +50,7 @@ int PluginArray::start_plugins(MWindow *mwindow, this->end = end; this->file = file; - cache = new CICache(this->edl, mwindow->preferences, mwindow->plugindb); + cache = new CICache(mwindow->preferences, mwindow->plugindb); buffer_size = get_bufsize(); get_recordable_tracks(); create_modules(); diff --git a/cinelerra/preferences.C b/cinelerra/preferences.C index b5a9e0da..7ac93b0f 100644 --- a/cinelerra/preferences.C +++ b/cinelerra/preferences.C @@ -70,7 +70,7 @@ Preferences::Preferences() Preferences::~Preferences() { - delete brender_asset; + Garbage::delete_object(brender_asset); delete preferences_lock; } diff --git a/cinelerra/record.C b/cinelerra/record.C index 9253da69..ca451bf3 100644 --- a/cinelerra/record.C +++ b/cinelerra/record.C @@ -599,7 +599,7 @@ SET_TRACE // Delete everything script = 0; batches.remove_all_objects(); - delete default_asset; + Garbage::delete_object(default_asset); } void Record::activate_batch(int number, int stop_operation) diff --git a/cinelerra/render.C b/cinelerra/render.C index 51d820c3..ea4cc268 100644 --- a/cinelerra/render.C +++ b/cinelerra/render.C @@ -366,7 +366,7 @@ printf("Render::run 10\n"); if(!result) render(1, asset, mwindow->edl, strategy); printf("Render::run 11\n"); - delete asset; + Garbage::delete_object(asset); printf("Render::run 12\n"); } else @@ -619,8 +619,8 @@ int Render::render(int test_overwrite, } // Create caches - audio_cache = new CICache(command->get_edl(), preferences, plugindb); - video_cache = new CICache(command->get_edl(), preferences, plugindb); + audio_cache = new CICache(preferences, plugindb); + video_cache = new CICache(preferences, plugindb); default_asset->frame_rate = command->get_edl()->session->frame_rate; default_asset->sample_rate = command->get_edl()->session->sample_rate; diff --git a/cinelerra/renderfarmclient.C b/cinelerra/renderfarmclient.C index e6f1c2ca..a0a402a6 100644 --- a/cinelerra/renderfarmclient.C +++ b/cinelerra/renderfarmclient.C @@ -592,7 +592,7 @@ SET_TRACE //printf("RenderFarmClientThread::run 9\n"); - delete default_asset; + Garbage::delete_object(default_asset); //printf("RenderFarmClientThread::run 10\n"); delete edl; //printf("RenderFarmClientThread::run 11\n"); diff --git a/cinelerra/resourcepixmap.C b/cinelerra/resourcepixmap.C index 08a94a22..f67b7530 100644 --- a/cinelerra/resourcepixmap.C +++ b/cinelerra/resourcepixmap.C @@ -480,7 +480,8 @@ void ResourcePixmap::draw_audio_resource(Edit *edit, int x, int w) void ResourcePixmap::draw_audio_source(Edit *edit, int x, int w) { - File *source = mwindow->audio_cache->check_out(edit->asset); + File *source = mwindow->audio_cache->check_out(edit->asset, + mwindow->edl); if(!source) { @@ -742,7 +743,7 @@ void ResourcePixmap::draw_video_resource(Edit *edit, x = Units::round((double)project_frame * frame_w + edit_x - pixmap_x); } - File *source = mwindow->video_cache->check_out(edit->asset); + File *source = mwindow->video_cache->check_out(edit->asset, mwindow->edl); if(!source) return; while(x < refresh_x + refresh_w) diff --git a/cinelerra/resourcepixmap.h b/cinelerra/resourcepixmap.h index 1ece3b62..92c37738 100644 --- a/cinelerra/resourcepixmap.h +++ b/cinelerra/resourcepixmap.h @@ -7,6 +7,9 @@ #include "mwindow.inc" #include "trackcanvas.inc" + +// Can't use garbage collection for GUI elements because they need to +// lock the window for deletion. class ResourcePixmap : public BC_Pixmap { public: diff --git a/cinelerra/vedit.C b/cinelerra/vedit.C index 9e866f49..ad3ca744 100644 --- a/cinelerra/vedit.C +++ b/cinelerra/vedit.C @@ -46,7 +46,8 @@ int VEdit::read_frame(VFrame *video_out, int use_nudge, int use_cache) { - File *file = cache->check_out(asset); + File *file = cache->check_out(asset, + edl); int result = 0; if(use_nudge) input_position += track->nudge; diff --git a/cinelerra/vmodule.C b/cinelerra/vmodule.C index 98540537..d757797e 100644 --- a/cinelerra/vmodule.C +++ b/cinelerra/vmodule.C @@ -127,7 +127,8 @@ int VModule::import_frame(VFrame *output, current_edit->asset) { get_cache()->age(); - File *source = get_cache()->check_out(current_edit->asset); + File *source = get_cache()->check_out(current_edit->asset, + get_edl()); // get_cache()->dump(); if(source) diff --git a/cinelerra/vrender.C b/cinelerra/vrender.C index 47abaa94..7800c552 100644 --- a/cinelerra/vrender.C +++ b/cinelerra/vrender.C @@ -133,7 +133,8 @@ SET_TRACE { SET_TRACE Asset *asset = renderengine->preferences->brender_asset; - File *file = renderengine->get_vcache()->check_out(asset); + File *file = renderengine->get_vcache()->check_out(asset, + renderengine->edl); if(file) { int64_t corrected_position = current_position; @@ -341,7 +342,8 @@ int VRender::get_colormodel(Edit* &playable_edit, asset = playable_edit->asset; } - file = renderengine->get_vcache()->check_out(asset); + file = renderengine->get_vcache()->check_out(asset, + renderengine->edl); if(file) { diff --git a/cinelerra/vwindow.C b/cinelerra/vwindow.C index c87f6fee..fc21dab5 100644 --- a/cinelerra/vwindow.C +++ b/cinelerra/vwindow.C @@ -49,7 +49,7 @@ void VWindow::delete_edl() mwindow->edl->vwindow_edl_shared = 0; } - if(asset) delete asset; + if(asset) Garbage::delete_object(asset); asset = 0; } @@ -109,7 +109,7 @@ void VWindow::change_source() } else { - if(asset) delete asset; + if(asset) Garbage::delete_object(asset); asset = 0; mwindow->edl->vwindow_edl_shared = 0; } -- 2.11.4.GIT