these thumbnails are generated in the bg by rage_thumb and will appear once generated (or already found cached). it'll generate new thumbs if modified dates are equal or newer on tthe src vs the thumbnail cache. the caches store 160xN thumbs (keep aspect) with 70% lossy jpeg quality in an eet file with one thumb per 10 second interval. so it has limited accuracy.v-0.4.0
parent
848b3af260
commit
706e4948d7
13 changed files with 712 additions and 45 deletions
@ -0,0 +1,382 @@ |
||||
#include <Elementary.h> |
||||
#include "videothumb.h" |
||||
#include "sha1.h" |
||||
|
||||
typedef struct _Videothumb Videothumb; |
||||
|
||||
struct _Videothumb |
||||
{ |
||||
Evas_Object_Smart_Clipped_Data __clipped_data; |
||||
Evas_Object *o_img; |
||||
Ecore_Exe *thumb_exe; |
||||
Ecore_Event_Handler *exe_handler; |
||||
const char *file; |
||||
const char *realfile; |
||||
double pos; |
||||
unsigned int realpos; |
||||
int iw, ih; |
||||
Evas_Coord w, h; |
||||
Eina_Bool seen : 1; |
||||
}; |
||||
|
||||
static Evas_Smart *_smart = NULL; |
||||
static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL; |
||||
|
||||
static Eina_List *busy_thumbs = NULL; |
||||
static Eina_List *vidthumbs = NULL; |
||||
|
||||
static Eina_Bool |
||||
_busy_add(const char *file) |
||||
{ |
||||
Eina_List *l; |
||||
const char *s; |
||||
|
||||
EINA_LIST_FOREACH(busy_thumbs, l, s) |
||||
{ |
||||
if (!strcmp(file, s)) return EINA_FALSE; |
||||
} |
||||
busy_thumbs = eina_list_append(busy_thumbs, eina_stringshare_add(file)); |
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
static Eina_Bool |
||||
_busy_del(const char *file) |
||||
{ |
||||
Eina_List *l; |
||||
const char *s; |
||||
|
||||
EINA_LIST_FOREACH(busy_thumbs, l, s) |
||||
{ |
||||
if (!strcmp(file, s)) |
||||
{ |
||||
eina_stringshare_del(s); |
||||
busy_thumbs = eina_list_remove_list(busy_thumbs, l); |
||||
return EINA_TRUE; |
||||
} |
||||
} |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
static void |
||||
_thumb_update(Evas_Object *obj) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
char buf[PATH_MAX]; |
||||
|
||||
if (!sd) return; |
||||
snprintf(buf, sizeof(buf), "%u", sd->realpos); |
||||
evas_object_image_file_set(sd->o_img, NULL, NULL); |
||||
evas_object_image_file_set(sd->o_img, sd->realfile, buf); |
||||
evas_object_image_size_get(sd->o_img, &(sd->iw), &(sd->ih)); |
||||
if ((sd->iw <= 0) || (sd->ih <= 0)) |
||||
{ |
||||
evas_object_del(sd->o_img); |
||||
sd->o_img = NULL; |
||||
evas_object_smart_callback_call(obj, "failed", NULL); |
||||
} |
||||
else |
||||
{ |
||||
evas_object_image_preload(sd->o_img, EINA_FALSE); |
||||
evas_object_smart_callback_call(obj, "loaded", NULL); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
_thumb_match_update(Evas_Object *obj, const char *file) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
|
||||
if (!sd) return; |
||||
if (!strcmp(sd->file, file)) _thumb_update(obj); |
||||
} |
||||
|
||||
static Eina_Bool |
||||
_cb_thumb_exe(void *data, int type EINA_UNUSED, void *event) |
||||
{ |
||||
Ecore_Exe_Event_Del *ev; |
||||
Videothumb *sd = evas_object_smart_data_get(data); |
||||
|
||||
if (!sd) return EINA_TRUE; |
||||
ev = event; |
||||
if (ev->exe == sd->thumb_exe) |
||||
{ |
||||
Eina_List *l; |
||||
Evas_Object *o; |
||||
|
||||
_busy_del(sd->file); |
||||
EINA_LIST_FOREACH(vidthumbs, l, o) _thumb_match_update(o, sd->file); |
||||
if ((sd->iw <= 0) || (sd->ih <= 0)) |
||||
{ |
||||
if (sd->exe_handler) |
||||
{ |
||||
ecore_event_handler_del(sd->exe_handler); |
||||
sd->exe_handler = NULL; |
||||
} |
||||
} |
||||
sd->thumb_exe = NULL; |
||||
return EINA_FALSE; |
||||
} |
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
static void |
||||
_cb_preload(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(data); |
||||
|
||||
if (!sd) return; |
||||
evas_object_show(sd->o_img); |
||||
evas_object_smart_callback_call(data, "data", NULL); |
||||
} |
||||
|
||||
static void |
||||
_videothumb_image_load(Evas_Object *obj) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
char buf_base[PATH_MAX]; |
||||
char buf_file[PATH_MAX]; |
||||
char buf[PATH_MAX]; |
||||
char *s; |
||||
const char *libdir; |
||||
unsigned char sum[20]; |
||||
|
||||
if (!sd) return; |
||||
if (!sd->file) return; |
||||
sd->o_img = evas_object_image_filled_add(evas_object_evas_get(obj)); |
||||
evas_object_smart_member_add(sd->o_img, obj); |
||||
if (!sha1((unsigned char *)sd->file, strlen(sd->file), sum)) return; |
||||
snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x", |
||||
efreet_cache_home_get(), sum[0]); |
||||
snprintf(buf_file, sizeof(buf_base), |
||||
"%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" |
||||
"%02x%02x%02x%02x%02x%02x%02x%02x.eet", |
||||
buf_base, |
||||
sum[1], sum[2], sum[3], |
||||
sum[4], sum[5], sum[6], sum[7], |
||||
sum[8], sum[9], sum[10], sum[11], |
||||
sum[12], sum[13], sum[14], sum[15], |
||||
sum[16], sum[17], sum[18], sum[19]); |
||||
if (sd->realfile) eina_stringshare_del(sd->realfile); |
||||
sd->realfile = eina_stringshare_add(buf_file); |
||||
sd->realpos = (((unsigned int)(sd->pos * 1000.0)) / 10000) * 10000; |
||||
snprintf(buf, sizeof(buf), "%u", sd->realpos); |
||||
evas_object_event_callback_add(sd->o_img, |
||||
EVAS_CALLBACK_IMAGE_PRELOADED, |
||||
_cb_preload, obj); |
||||
evas_object_image_file_set(sd->o_img, sd->realfile, buf); |
||||
evas_object_image_size_get(sd->o_img, &(sd->iw), &(sd->ih)); |
||||
if (sd->iw > 0) |
||||
{ |
||||
Eina_Bool ok = EINA_FALSE; |
||||
struct stat st1, st2; |
||||
|
||||
if (stat(sd->file, &st1) == 0) |
||||
{ |
||||
if (stat(sd->realfile, &st2) == 0) |
||||
{ |
||||
if (st1.st_mtime < st2.st_mtime) ok = EINA_TRUE; |
||||
} |
||||
} |
||||
if (ok) |
||||
{ |
||||
evas_object_image_preload(sd->o_img, EINA_FALSE); |
||||
return; |
||||
} |
||||
} |
||||
if (!_busy_add(sd->file)) return; |
||||
ecore_exe_run_priority_set(10); |
||||
if (sd->thumb_exe) |
||||
{ |
||||
ecore_exe_free(sd->thumb_exe); |
||||
sd->thumb_exe = NULL; |
||||
} |
||||
s = ecore_file_escape_name(sd->file); |
||||
if (s) |
||||
{ |
||||
libdir = elm_app_lib_dir_get(); |
||||
if (libdir) |
||||
{ |
||||
if (!sd->exe_handler) |
||||
sd->exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, |
||||
_cb_thumb_exe, obj); |
||||
snprintf(buf, sizeof(buf), |
||||
"%s/rage/utils/rage_thumb %s 10000 >& /dev/null", |
||||
libdir, s); |
||||
sd->thumb_exe = ecore_exe_pipe_run(buf, |
||||
ECORE_EXE_TERM_WITH_PARENT | |
||||
ECORE_EXE_NOT_LEADER, |
||||
obj); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void |
||||
_videothumb_eval(Evas_Object *obj, Eina_Bool force) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
Evas_Coord ox, oy, ow, oh, vw, vh; |
||||
Eina_Bool seen = EINA_FALSE; |
||||
|
||||
if (!sd) return; |
||||
evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); |
||||
evas_output_viewport_get(evas_object_evas_get(obj), NULL, NULL, &vw, &vh); |
||||
// XXX: not listening to canvas resizes! :(
|
||||
if (ELM_RECTS_INTERSECT(ox, oy, ow, oh, 0, 0, vw, vh)) seen = EINA_TRUE; |
||||
// XXX: fix - wrokaround and always visible
|
||||
seen = EINA_TRUE; |
||||
if (force) |
||||
{ |
||||
sd->seen = seen; |
||||
if (sd->o_img) |
||||
{ |
||||
evas_object_del(sd->o_img); |
||||
sd->o_img = NULL; |
||||
} |
||||
_videothumb_image_load(obj); |
||||
evas_object_move(sd->o_img, ox, oy); |
||||
evas_object_resize(sd->o_img, ow, oh); |
||||
} |
||||
else |
||||
{ |
||||
if (seen != sd->seen) |
||||
{ |
||||
sd->seen = seen; |
||||
if (sd->seen) |
||||
{ |
||||
if (sd->o_img) |
||||
{ |
||||
evas_object_del(sd->o_img); |
||||
sd->o_img = NULL; |
||||
} |
||||
_videothumb_image_load(obj); |
||||
evas_object_move(sd->o_img, ox, oy); |
||||
evas_object_resize(sd->o_img, ow, oh); |
||||
} |
||||
else |
||||
{ |
||||
if (sd->o_img) |
||||
{ |
||||
evas_object_del(sd->o_img); |
||||
sd->o_img = NULL; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void |
||||
_smart_add(Evas_Object *obj) |
||||
{ |
||||
Videothumb *sd; |
||||
|
||||
sd = calloc(1, sizeof(Videothumb)); |
||||
EINA_SAFETY_ON_NULL_RETURN(sd); |
||||
evas_object_smart_data_set(obj, sd); |
||||
_parent_sc.add(obj); |
||||
} |
||||
|
||||
static void |
||||
_smart_del(Evas_Object *obj) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
if (!sd) return; |
||||
vidthumbs = eina_list_remove(vidthumbs, obj); |
||||
if (sd->file) eina_stringshare_del(sd->file); |
||||
if (sd->realfile) eina_stringshare_del(sd->realfile); |
||||
if (sd->o_img) evas_object_del(sd->o_img); |
||||
if (sd->thumb_exe) ecore_exe_free(sd->thumb_exe); |
||||
if (sd->exe_handler) ecore_event_handler_del(sd->exe_handler); |
||||
_parent_sc.del(obj); |
||||
} |
||||
|
||||
static void |
||||
_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
Evas_Coord ox, oy, ow, oh; |
||||
if (!sd) return; |
||||
evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); |
||||
if ((ow == w) && (oh == h)) return; |
||||
evas_object_smart_changed(obj); |
||||
} |
||||
|
||||
static void |
||||
_smart_calculate(Evas_Object *obj) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
Evas_Coord ox, oy, ow, oh; |
||||
|
||||
if (!sd) return; |
||||
evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); |
||||
sd->w = ow; |
||||
sd->h = oh; |
||||
_videothumb_eval(obj, EINA_FALSE); |
||||
if (sd->o_img) |
||||
{ |
||||
evas_object_move(sd->o_img, ox, oy); |
||||
evas_object_resize(sd->o_img, ow, oh); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
_smart_move(Evas_Object *obj, Evas_Coord x EINA_UNUSED, Evas_Coord y EINA_UNUSED) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
if (!sd) return; |
||||
evas_object_smart_changed(obj); |
||||
} |
||||
|
||||
static void |
||||
_smart_init(void) |
||||
{ |
||||
static Evas_Smart_Class sc; |
||||
|
||||
evas_object_smart_clipped_smart_set(&_parent_sc); |
||||
sc = _parent_sc; |
||||
sc.name = "videothumb"; |
||||
sc.version = EVAS_SMART_CLASS_VERSION; |
||||
sc.add = _smart_add; |
||||
sc.del = _smart_del; |
||||
sc.resize = _smart_resize; |
||||
sc.move = _smart_move; |
||||
sc.calculate = _smart_calculate; |
||||
_smart = evas_smart_class_new(&sc); |
||||
} |
||||
|
||||
Evas_Object * |
||||
videothumb_add(Evas_Object *parent) |
||||
{ |
||||
Evas *e; |
||||
Evas_Object *obj; |
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); |
||||
e = evas_object_evas_get(parent); |
||||
if (!e) return NULL; |
||||
if (!_smart) _smart_init(); |
||||
obj = evas_object_smart_add(e, _smart); |
||||
vidthumbs = eina_list_prepend(vidthumbs, obj); |
||||
return obj; |
||||
} |
||||
|
||||
void |
||||
videothumb_file_set(Evas_Object *obj, const char *file, double pos) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
if (!sd) return; |
||||
if ((sd->file == file) && (sd->pos == pos)) return; |
||||
if (sd->file) eina_stringshare_del(sd->file); |
||||
sd->file = eina_stringshare_add(file); |
||||
sd->pos = pos; |
||||
_videothumb_eval(obj, EINA_TRUE); |
||||
} |
||||
|
||||
void |
||||
videothumb_size_get(Evas_Object *obj, int *w, int *h) |
||||
{ |
||||
Videothumb *sd = evas_object_smart_data_get(obj); |
||||
*w = 1; |
||||
*h = 1; |
||||
if (!sd) return; |
||||
*w = sd->iw; |
||||
*h = sd->ih; |
||||
} |
@ -0,0 +1,8 @@ |
||||
#ifndef _VIDEOTHUMB_H__ |
||||
#define _VIDEOTHUMB_H__ 1 |
||||
|
||||
Evas_Object *videothumb_add(Evas_Object *parent); |
||||
void videothumb_file_set(Evas_Object *obj, const char *file, double pos); |
||||
void videothumb_size_get(Evas_Object *obj, int *w, int *h); |
||||
|
||||
#endif |
Loading…
Reference in new issue