aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-10-17 05:53:22 +0200
committerdec05eba <dec05eba@protonmail.com>2021-11-17 09:53:11 +0100
commitfc49d40c0d2f6edbbe9dde1f1b53d6a17e9d9f7d (patch)
tree5913c49b84a27927a33e3e712b4911ffba419661
parent5a073cf9cc4f9a58e39bcbe1ee1786e8d925d6bb (diff)
Limit image loading to one thread in async image loader
-rw-r--r--TODO1
m---------depends/mglpp0
-rw-r--r--include/AsyncImageLoader.hpp12
-rw-r--r--src/AsyncImageLoader.cpp138
-rw-r--r--src/Body.cpp2
5 files changed, 113 insertions, 40 deletions
diff --git a/TODO b/TODO
index e2dc677..ef34c82 100644
--- a/TODO
+++ b/TODO
@@ -207,3 +207,4 @@ Add flag to quickmedia to use svp, by making svp use the same input ipc socket a
Support directly going to a youtube channel for a url. This is helpful for opening channel urls directly with quickmedia and also going to another channel from a youtube description.
Support downloading soundcloud/youtube playlists. Such downloads should also have a different download gui as you would select a folder instead of an output file.
Support downloading .m3u8 files, such as soundcloud music without using youtube-dl.
+Fix lbry and peertube download which fail because for lbry all videos are .m3u8 and some peertube videos are .m3u8.
diff --git a/depends/mglpp b/depends/mglpp
new file mode 160000
+Subproject 929402828d99810211c644ce4ecf07b98013be1
diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp
index 94b225d..7556fbc 100644
--- a/include/AsyncImageLoader.hpp
+++ b/include/AsyncImageLoader.hpp
@@ -15,6 +15,7 @@ namespace QuickMedia {
enum class LoadingState {
NOT_LOADED,
LOADING,
+ READY_TO_LOAD,
FINISHED_LOADING,
APPLIED_TO_TEXTURE
};
@@ -24,6 +25,7 @@ namespace QuickMedia {
sf::Texture texture;
std::unique_ptr<sf::Image> image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory
size_t counter = 0;
+ Path thumbnail_path;
};
struct ThumbnailLoadData {
@@ -70,19 +72,25 @@ namespace QuickMedia {
// set |resize_target_size| to {0, 0} to disable resizing.
// Note: this method is not thread-safe
- void load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data);
+ bool load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path);
// Returns -1 if all threads are busy
int get_free_load_index() const;
+ void load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size);
+ void process_thumbnail(ThumbnailLoadData &thumbnail_load_data);
+ private:
void reset_download(Download &download);
private:
std::mutex download_mutex;
// TODO: Use curl single-threaded multi-download feature instead
Download downloads[NUM_IMAGE_LOAD_PARALLEL];
AsyncTask<void> load_thread;
- MessageQueue<ThumbnailLoadData> image_load_queue;
+ MessageQueue<ThumbnailLoadData> image_thumbnail_create_queue;
std::unordered_map<std::string, std::shared_ptr<ThumbnailData>> thumbnails;
size_t counter = 0;
+
+ std::mutex image_load_mutex;
+ ThumbnailLoadData current_loading_image;
};
}
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp
index 762408c..4661960 100644
--- a/src/AsyncImageLoader.cpp
+++ b/src/AsyncImageLoader.cpp
@@ -147,29 +147,33 @@ namespace QuickMedia {
return true;
}
- // Create thumbnail and load it. On failure load the original image
- static void create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) {
+ void AsyncImageLoader::load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) {
FileAnalyzer file_analyzer;
if(!file_analyzer.load_file(thumbnail_path.data.c_str(), false)) {
- fprintf(stderr, "Failed to convert %s to a thumbnail, using the original image\n", thumbnail_path.data.c_str());
+ fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str());
+ thumbnail_data->image = std::make_unique<sf::Image>();
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
return;
}
if(is_content_type_video(file_analyzer.get_content_type())) {
- if(video_get_first_frame(thumbnail_path.data.c_str(), thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y))
- load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data);
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ if(video_get_first_frame(thumbnail_path.data.c_str(), thumbnail_path_resized.data.c_str(), resize_target_size.x, resize_target_size.y)) {
+ thumbnail_data->loading_state = LoadingState::READY_TO_LOAD;
+ } else {
+ fprintf(stderr, "Failed to get first frame of %s\n", thumbnail_path.data.c_str());
+ thumbnail_data->image = std::make_unique<sf::Image>();
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ }
return;
}
if(create_thumbnail(thumbnail_path, thumbnail_path_resized, resize_target_size, file_analyzer.get_content_type(), true)) {
- load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data);
+ thumbnail_data->loading_state = LoadingState::READY_TO_LOAD;
} else {
- load_image_from_file(*thumbnail_data->image, thumbnail_path.data);
- fprintf(stderr, "Failed to convert %s to a thumbnail, using the original image\n", thumbnail_path.data.c_str());
+ fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str());
+ thumbnail_data->image = std::make_unique<sf::Image>();
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
}
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
}
AsyncImageLoader& AsyncImageLoader::get_instance() {
@@ -179,7 +183,31 @@ namespace QuickMedia {
return *instance;
}
- static void process_thumbnail(ThumbnailLoadData &thumbnail_load_data) {
+ void AsyncImageLoader::process_thumbnail(ThumbnailLoadData &thumbnail_load_data) {
+ Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path;
+ if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
+ thumbnail_path_resized.append("_" + std::to_string(thumbnail_load_data.resize_target_size.x) + "x" + std::to_string(thumbnail_load_data.resize_target_size.y));
+
+ if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) {
+ fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str());
+ thumbnail_load_data.thumbnail_data->image = std::make_unique<sf::Image>();
+ thumbnail_load_data.thumbnail_data->loading_state = LoadingState::READY_TO_LOAD;
+ return;
+ }
+
+ Path thumbnail_original_path;
+ if(thumbnail_load_data.local)
+ thumbnail_original_path = thumbnail_load_data.path;
+ else
+ thumbnail_original_path = thumbnail_load_data.thumbnail_path;
+
+ if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
+ load_create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size);
+ else
+ thumbnail_load_data.thumbnail_data->loading_state = LoadingState::READY_TO_LOAD;
+ }
+
+ static void load_processed_thumbnail(ThumbnailLoadData &thumbnail_load_data) {
thumbnail_load_data.thumbnail_data->image = std::make_unique<sf::Image>();
Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path;
@@ -189,7 +217,6 @@ namespace QuickMedia {
if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) {
load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data);
thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str());
return;
}
@@ -199,12 +226,13 @@ namespace QuickMedia {
else
thumbnail_original_path = thumbnail_load_data.thumbnail_path;
- if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
- create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size);
- else
+ if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) {
+ load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data);
+ thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ } else {
load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_original_path.data);
-
- thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ }
}
AsyncImageLoader::AsyncImageLoader() {
@@ -215,10 +243,26 @@ namespace QuickMedia {
load_thread = AsyncTask<void>([this]() mutable {
std::optional<ThumbnailLoadData> thumbnail_load_data_opt;
- while(image_load_queue.is_running()) {
- thumbnail_load_data_opt = image_load_queue.pop_if_available();
- if(thumbnail_load_data_opt)
+ while(image_thumbnail_create_queue.is_running()) {
+ ThumbnailLoadData load_image_data;
+ {
+ std::lock_guard<std::mutex> lock(image_load_mutex);
+ if(current_loading_image.thumbnail_data) {
+ load_image_data = std::move(current_loading_image);
+ current_loading_image = ThumbnailLoadData();
+ }
+ }
+
+ if(load_image_data.thumbnail_data) {
+ load_processed_thumbnail(load_image_data);
+ load_image_data = ThumbnailLoadData();
+ }
+
+ thumbnail_load_data_opt = image_thumbnail_create_queue.pop_if_available();
+ if(thumbnail_load_data_opt) {
process_thumbnail(thumbnail_load_data_opt.value());
+ thumbnail_load_data_opt = std::nullopt;
+ }
for(int i = 0; i < NUM_IMAGE_LOAD_PARALLEL; ++i) {
Download &download = downloads[i];
@@ -246,7 +290,7 @@ namespace QuickMedia {
}
AsyncImageLoader::~AsyncImageLoader() {
- image_load_queue.close();
+ image_thumbnail_create_queue.close();
}
static bool download_file_async(const char *url, const char *save_filepath, ReadProgram *read_program) {
@@ -260,19 +304,16 @@ namespace QuickMedia {
return exec_program_pipe(args, read_program) == 0;
}
- void AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data) {
+ bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path) {
if(thumbnail_data->loading_state != LoadingState::NOT_LOADED)
- return;
+ return true;
if(url.empty()) {
thumbnail_data->image = std::make_unique<sf::Image>();
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
+ return false;
}
- SHA256 sha256;
- sha256.add(url.data(), url.size());
- Path thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash());
if(local) {
struct stat file_stat;
memset(&file_stat, 0, sizeof(file_stat));
@@ -280,24 +321,24 @@ namespace QuickMedia {
fprintf(stderr, "Failed to load thumbnail %s: no such file\n", url.c_str());
thumbnail_data->image = std::make_unique<sf::Image>();
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- return;
+ return false;
}
thumbnail_path.append("_" + std::to_string(file_stat.st_mtim.tv_sec));
thumbnail_data->loading_state = LoadingState::LOADING;
- image_load_queue.push({ url, std::move(thumbnail_path), true, thumbnail_data, resize_target_size });
- return;
+ image_thumbnail_create_queue.push({ url, thumbnail_path, true, thumbnail_data, resize_target_size });
+ return true;
}
if(get_file_type(thumbnail_path) == FileType::REGULAR) {
thumbnail_data->loading_state = LoadingState::LOADING;
- image_load_queue.push({ url, std::move(thumbnail_path), false, thumbnail_data, resize_target_size });
- return;
+ image_thumbnail_create_queue.push({ url, thumbnail_path, false, thumbnail_data, resize_target_size });
+ return true;
}
int free_index = get_free_load_index();
if(free_index == -1)
- return;
+ return false;
std::lock_guard<std::mutex> lock(download_mutex);
thumbnail_data->loading_state = LoadingState::LOADING;
@@ -305,14 +346,15 @@ namespace QuickMedia {
tmp_thumbnail_path.append(".tmp");
if(!download_file_async(url.c_str(), tmp_thumbnail_path.data.c_str(), &downloads[free_index].read_program)) {
fprintf(stderr, "Failed to start download of %s\n", url.c_str());
- return;
+ return false;
}
downloads[free_index].download_start = get_boottime_milliseconds();
- downloads[free_index].thumbnail_path = std::move(thumbnail_path);
+ downloads[free_index].thumbnail_path = thumbnail_path;
downloads[free_index].thumbnail_data = thumbnail_data;
downloads[free_index].resize_target_size = resize_target_size;
downloads[free_index].url = url;
+ return true;
}
std::shared_ptr<ThumbnailData> AsyncImageLoader::get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size) {
@@ -321,7 +363,22 @@ namespace QuickMedia {
if(!thumbnail_data)
thumbnail_data = std::make_shared<ThumbnailData>();
thumbnail_data->counter = counter;
- load_thumbnail(url, local, resize_target_size, thumbnail_data);
+
+ if(thumbnail_data->thumbnail_path.data.empty()) {
+ SHA256 sha256;
+ sha256.add(url.data(), url.size());
+ thumbnail_data->thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash());
+ }
+
+ if(!load_thumbnail(url, local, resize_target_size, thumbnail_data, thumbnail_data->thumbnail_path))
+ return thumbnail_data;
+
+ if(thumbnail_data->loading_state == LoadingState::READY_TO_LOAD) {
+ std::lock_guard<std::mutex> lock(image_load_mutex);
+ if(!current_loading_image.thumbnail_data)
+ current_loading_image = { url, thumbnail_data->thumbnail_path, local, thumbnail_data, resize_target_size };
+ }
+
return thumbnail_data;
}
@@ -342,9 +399,16 @@ namespace QuickMedia {
}
}
- image_load_queue.erase_if([&it](ThumbnailLoadData &load_data) {
+ image_thumbnail_create_queue.erase_if([&it](ThumbnailLoadData &load_data) {
return load_data.path.data == it->first;
});
+
+ {
+ std::lock_guard<std::mutex> lock(image_load_mutex);
+ if(current_loading_image.thumbnail_data == it->second)
+ current_loading_image = ThumbnailLoadData();
+ }
+
it = thumbnails.erase(it);
loaded_textures_changed = true;
} else {
diff --git a/src/Body.cpp b/src/Body.cpp
index d4b6c5c..eb7a1be 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -1070,7 +1070,7 @@ namespace QuickMedia {
std::shared_ptr<BodyItem> &item = items[index];
assert(item->visible);
- int prev_index;
+ int prev_index = 0;
if(attach_side == AttachSide::BOTTOM) {
prev_index = get_previous_visible_item(index);
if(prev_index == -1)