You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.2 KiB
238 lines
8.2 KiB
http://sourceforge.net/tracker/?func=detail&aid=3291468&group_id=129766&atid=715782
|
|
|
|
[PATCH] Add the cache feature of ffmpegthumbnailer.
|
|
|
|
I want the cache feature of ffmpegthumbnailer because my machine
|
|
is not powerful :-(
|
|
So I created this patch for the cache feature.
|
|
|
|
This patch adds a new option "cache-dir" in config.xml.
|
|
If not specifying any string, the cache feature is disable.
|
|
And specifying some directory, the cache feature is enable
|
|
and the cache files will be created under the directory.
|
|
|
|
Signed-off-by: Ken'ichi Ohmichi <ken1ohmichi@gmail.com>
|
|
---
|
|
diff --git a/src/common.h b/src/common.h
|
|
index d1998b3..358f4d1 100644
|
|
--- a/src/common.h
|
|
+++ b/src/common.h
|
|
@@ -367,6 +367,8 @@
|
|
#define DEFAULT_FFMPEGTHUMBNAILER_FILMSTRIP_OVERLAY YES
|
|
#define DEFAULT_FFMPEGTHUMBNAILER_WORKAROUND_BUGS NO
|
|
#define DEFAULT_FFMPEGTHUMBNAILER_IMAGE_QUALITY 8
|
|
+ #define DEFAULT_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED YES
|
|
+ #define DEFAULT_FFMPEGTHUMBNAILER_CACHE_DIR ""
|
|
#endif
|
|
|
|
#if defined(HAVE_LASTFMLIB)
|
|
diff --git a/src/config_manager.cc b/src/config_manager.cc
|
|
index 8c975f8..2902090 100644
|
|
--- a/src/config_manager.cc
|
|
+++ b/src/config_manager.cc
|
|
@@ -1873,6 +1873,24 @@ void ConfigManager::validate(String serverhome)
|
|
|
|
NEW_INT_OPTION(temp_int);
|
|
SET_INT_OPTION(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_IMAGE_QUALITY);
|
|
+
|
|
+ temp = getOption("/server/extended-runtime-options/ffmpegthumbnailer/"
|
|
+ "cache-dir", DEFAULT_FFMPEGTHUMBNAILER_CACHE_DIR);
|
|
+
|
|
+ NEW_OPTION(temp);
|
|
+ SET_OPTION(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR);
|
|
+
|
|
+ temp = getOption("/server/extended-runtime-options/ffmpegthumbnailer/"
|
|
+ "cache-dir/attribute::enabled",
|
|
+ DEFAULT_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED);
|
|
+
|
|
+ if (!validateYesNo(temp))
|
|
+ throw _Exception(_("Error in config file: "
|
|
+ "invalid \"enabled\" attribute value in "
|
|
+ "ffmpegthumbnailer <cache-dir> tag"));
|
|
+
|
|
+ NEW_BOOL_OPTION(temp == YES ? true : false);
|
|
+ SET_BOOL_OPTION(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED);
|
|
}
|
|
#endif
|
|
|
|
diff --git a/src/config_manager.h b/src/config_manager.h
|
|
index 52b9842..a447a60 100644
|
|
--- a/src/config_manager.h
|
|
+++ b/src/config_manager.h
|
|
@@ -110,6 +110,8 @@ typedef enum
|
|
CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_FILMSTRIP_OVERLAY,
|
|
CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_WORKAROUND_BUGS,
|
|
CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_IMAGE_QUALITY,
|
|
+ CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED,
|
|
+ CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR,
|
|
#endif
|
|
CFG_SERVER_EXTOPTS_MARK_PLAYED_ITEMS_ENABLED,
|
|
CFG_SERVER_EXTOPTS_MARK_PLAYED_ITEMS_STRING_MODE_PREPEND,
|
|
diff --git a/src/metadata/ffmpeg_handler.cc b/src/metadata/ffmpeg_handler.cc
|
|
index a637d9c..8e7ef23 100644
|
|
--- a/src/metadata/ffmpeg_handler.cc
|
|
+++ b/src/metadata/ffmpeg_handler.cc
|
|
@@ -50,6 +50,9 @@
|
|
// INT64_C is not defined in ffmpeg/avformat.h but is needed
|
|
// macro defines included via autoconfig.h
|
|
#include <stdint.h>
|
|
+#include <sys/stat.h>
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
|
|
//#ifdef FFMPEG_NEEDS_EXTERN_C
|
|
extern "C"
|
|
@@ -279,6 +282,122 @@ void FfmpegHandler::fillMetadata(Ref<CdsItem> item)
|
|
av_close_input_file(pFormatCtx);
|
|
}
|
|
|
|
+#ifdef HAVE_FFMPEGTHUMBNAILER
|
|
+
|
|
+static int _mkdir(const char *path)
|
|
+{
|
|
+ int ret = mkdir(path, 0777);
|
|
+
|
|
+ if (ret == 0) {
|
|
+ // Make sure we are +x in case of restrictive umask that strips +x.
|
|
+ struct stat st;
|
|
+ if (stat(path, &st)) {
|
|
+ log_warning("could not stat(%s): %s\n", path, strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+ mode_t xbits = S_IXUSR | S_IXGRP | S_IXOTH;
|
|
+ if (!(st.st_mode & xbits)) {
|
|
+ if (chmod(path, st.st_mode | xbits)) {
|
|
+ log_warning("could not chmod(%s, +x): %s\n", path, strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static bool makeThumbnailCacheDir(String& path)
|
|
+{
|
|
+ char *path_temp = strdup(path.c_str());
|
|
+ char *last_slash = strrchr(path_temp, '/');
|
|
+ char *slash = last_slash;
|
|
+ bool ret = false;
|
|
+
|
|
+ if (!last_slash)
|
|
+ return ret;
|
|
+
|
|
+ // Assume most dirs exist, so scan backwards first.
|
|
+ // Avoid stat/access checks due to TOCTOU races.
|
|
+ errno = 0;
|
|
+ for (slash = last_slash; slash > path_temp; --slash) {
|
|
+ if (*slash != '/')
|
|
+ continue;
|
|
+ *slash = '\0';
|
|
+ if (_mkdir(path_temp) == 0) {
|
|
+ // Now we can forward scan.
|
|
+ while (slash < last_slash) {
|
|
+ *slash = DIR_SEPARATOR;
|
|
+ if (_mkdir(path_temp) < 0)
|
|
+ // Allow EEXIST in case of someone else doing `mkdir`.
|
|
+ if (errno != EEXIST)
|
|
+ goto done;
|
|
+ slash += strlen(slash);
|
|
+ }
|
|
+ if (slash == last_slash)
|
|
+ ret = true;
|
|
+ break;
|
|
+ } else if (errno == EEXIST) {
|
|
+ ret = true;
|
|
+ break;
|
|
+ } else if (errno != ENOENT) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ done:
|
|
+ free(path_temp);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static String getThumbnailCacheFilePath(String& movie_filename, bool create)
|
|
+{
|
|
+ Ref<ConfigManager> cfg = ConfigManager::getInstance();
|
|
+ String cache_dir = cfg->getOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR);
|
|
+
|
|
+ if (cache_dir.length() == 0) {
|
|
+ String home_dir = cfg->getOption(CFG_SERVER_HOME);
|
|
+ cache_dir = home_dir + "/cache-dir";
|
|
+ }
|
|
+
|
|
+ cache_dir = cache_dir + movie_filename + "-thumb.jpg";
|
|
+ if (create && !makeThumbnailCacheDir(cache_dir))
|
|
+ cache_dir = "";
|
|
+ return cache_dir;
|
|
+}
|
|
+
|
|
+static bool readThumbnailCacheFile(String movie_filename, uint8_t **ptr_img, size_t *size_img)
|
|
+{
|
|
+ String path = getThumbnailCacheFilePath(movie_filename, false);
|
|
+ FILE *fp = fopen(path.c_str(), "rb");
|
|
+ if (!fp)
|
|
+ return false;
|
|
+
|
|
+ size_t bytesRead;
|
|
+ uint8_t buffer[1024];
|
|
+ *ptr_img = NULL;
|
|
+ *size_img = 0;
|
|
+ while ((bytesRead = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
|
|
+ *ptr_img = (uint8_t *)realloc(*ptr_img, *size_img + bytesRead);
|
|
+ memcpy(*ptr_img + *size_img, buffer, bytesRead);
|
|
+ *size_img += bytesRead;
|
|
+ }
|
|
+ fclose(fp);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void writeThumbnailCacheFile(String movie_filename, uint8_t *ptr_img, int size_img)
|
|
+{
|
|
+ String path = getThumbnailCacheFilePath(movie_filename, true);
|
|
+ FILE *fp = fopen(path.c_str(), "wb");
|
|
+ if (!fp)
|
|
+ return;
|
|
+ fwrite(ptr_img, sizeof(uint8_t), size_img, fp);
|
|
+ fclose(fp);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
Ref<IOHandler> FfmpegHandler::serveContent(Ref<CdsItem> item, int resNum, off_t *data_size)
|
|
{
|
|
*data_size = -1;
|
|
@@ -288,6 +407,18 @@ Ref<IOHandler> FfmpegHandler::serveContent(Ref<CdsItem> item, int resNum, off_t
|
|
if (!cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_ENABLED))
|
|
return nil;
|
|
|
|
+ if (cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED)) {
|
|
+ uint8_t *ptr_image;
|
|
+ size_t size_image;
|
|
+ if (readThumbnailCacheFile(item->getLocation(),
|
|
+ &ptr_image, &size_image)) {
|
|
+ *data_size = (off_t)size_image;
|
|
+ Ref<IOHandler> h(new MemIOHandler(ptr_image, size_image));
|
|
+ free(ptr_image);
|
|
+ log_debug("Returning cached thumbnail for file: %s\n", item->getLocation().c_str());
|
|
+ return h;
|
|
+ }
|
|
+ }
|
|
#ifdef FFMPEGTHUMBNAILER_OLD_API
|
|
video_thumbnailer *th = create_thumbnailer();
|
|
image_data *img = create_image_data();
|
|
@@ -318,6 +449,10 @@ Ref<IOHandler> FfmpegHandler::serveContent(Ref<CdsItem> item, int resNum, off_t
|
|
#endif // old api
|
|
throw _Exception(_("Could not generate thumbnail for ") +
|
|
item->getLocation());
|
|
+ if (cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED)) {
|
|
+ writeThumbnailCacheFile(item->getLocation(),
|
|
+ img->image_data_ptr, img->image_data_size);
|
|
+ }
|
|
|
|
*data_size = (off_t)img->image_data_size;
|
|
Ref<IOHandler> h(new MemIOHandler((void *)img->image_data_ptr,
|