[gegl] initial gcut import
- From: Øyvind Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] initial gcut import
- Date: Fri, 14 Jul 2017 19:23:22 +0000 (UTC)
commit 4830622e2d212fa5f6173dbfc8ea5274ddcc161a
Author: Øyvind Kolås <pippin gimp org>
Date: Thu Jul 13 19:55:09 2017 +0200
initial gcut import
Makefile.am | 1 +
configure.ac | 1 +
gcut/Makefile.am | 59 ++
gcut/clip.c | 439 ++++++++
gcut/default.edl | 34 +
gcut/gedl-ui.c | 2855 +++++++++++++++++++++++++++++++++++++++++++++++++++
gcut/gedl.c | 1608 +++++++++++++++++++++++++++++
gcut/gedl.h | 259 +++++
gcut/iconographer.c | 748 ++++++++++++++
gcut/renderer.c | 287 ++++++
10 files changed, 6291 insertions(+), 0 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index e0a661c..2b6ff96 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,6 +10,7 @@ SUBDIRS=\
libs/npd \
seamless-clone \
bin \
+ gcut \
tools \
operations \
examples \
diff --git a/configure.ac b/configure.ac
index 27c77d6..9867a41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1283,6 +1283,7 @@ dnl bin/node-editors/Makefile
AC_CONFIG_FILES([
Makefile
bin/Makefile
+gcut/Makefile
gegl/Makefile
gegl/gegl-version.h
gegl/buffer/Makefile
diff --git a/gcut/Makefile.am b/gcut/Makefile.am
new file mode 100644
index 0000000..e6ccedb
--- /dev/null
+++ b/gcut/Makefile.am
@@ -0,0 +1,59 @@
+if OS_WIN32
+no_undefined = -no-undefined
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/gegl \
+ -I$(top_srcdir)/gegl \
+ -I$(top_builddir)/gegl/buffer \
+ -I$(top_srcdir)/gegl/buffer \
+ -I$(top_builddir)/gegl/graph \
+ -I$(top_srcdir)/gegl/graph \
+ -I$(top_builddir)/gegl/module \
+ -I$(top_srcdir)/gegl/module \
+ -I$(top_builddir)/gegl/operation \
+ -I$(top_srcdir)/gegl/operation \
+ -I$(top_builddir)/gegl/opencl \
+ -I$(top_srcdir)/gegl/opencl \
+ -I$(top_builddir)/gegl/process \
+ -I$(top_srcdir)/gegl/process \
+ -I$(top_builddir)/gegl/property-types \
+ -I$(top_srcdir)/gegl/property-types
+
+AM_CFLAGS = \
+ $(DEP_CFLAGS) $(BABL_CFLAGS) $(PNG_CFLAGS) \
+ $(MRG_CFLAGS) $(GEXIV2_CFLAGS)
+
+AM_LDFLAGS = \
+ $(no_undefined) ../gegl/libgegl-$(GEGL_API_VERSION).la \
+ $(DEP_LIBS) $(BABL_LIBS) $(PNG_LIBS) $(LIBSPIRO) $(MATH_LIB) \
+ $(MRG_LIBS) $(GEXIV2_LIBS)
+
+bin_PROGRAMS = gcut
+
+default.edl.inc: default.edl
+ cat $< | \
+ sed 's/\\/\\\\/g' | \
+ sed 's/\r/a/' | \
+ sed 's/"/\\"/g' | \
+ sed 's/^/"/' | \
+ sed 's/$$/\\n"/' > $@
+
+gcut_SOURCES = \
+ gedl.c \
+ gedl.h \
+ renderer.c \
+ iconographer.c \
+ clip.c
+
+
+if HAVE_MRG
+if HAVE_GEXIV2
+if HAVE_SDL
+gcut_SOURCES += gedl-ui.c
+AM_CFLAGS += $(SDL_CFLAGS)
+AM_LDFLAGS += $(SDL_LIBS)
+endif
+endif
+endif
diff --git a/gcut/clip.c b/gcut/clip.c
new file mode 100644
index 0000000..4a509cf
--- /dev/null
+++ b/gcut/clip.c
@@ -0,0 +1,439 @@
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <gegl.h>
+#include <gegl-audio-fragment.h>
+#include "gedl.h"
+
+Clip *clip_new (GeglEDL *edl)
+{
+ Clip *clip = g_malloc0 (sizeof (Clip));
+ clip->edl = edl;
+ clip->gegl = gegl_node_new ();
+
+ clip->chain_loader = gegl_node_new_child (clip->gegl, "operation", "gegl:nop", NULL);
+
+ clip->full_loader = gegl_node_new_child (clip->gegl, "operation", "gegl:ff-load", NULL);
+ clip->proxy_loader = gegl_node_new_child (clip->gegl, "operation", "gegl:ff-load", NULL);
+ clip->loader = gegl_node_new_child (clip->gegl, "operation", "gegl:nop", NULL);
+
+ clip->nop_scaled = gegl_node_new_child (clip->gegl, "operation", "gegl:scale-size-keepaspect",
+ "y", 0.0, //
+ "x", 1.0 * edl->width,
+ "sampler", GEDL_SAMPLER,
+ NULL);
+ clip->nop_crop = gegl_node_new_child (clip->gegl, "operation", "gegl:crop", "x", 0.0, "y", 0.0,
"width", 1.0 * edl->width,
+ "height", 1.0 * edl->height, NULL);
+
+ clip->nop_store_buf = gegl_node_new_child (clip->gegl, "operation", "gegl:write-buffer", "buffer",
edl->buffer, NULL);
+#if 0
+ clip->full_store_buf = gegl_node_new_child (clip->gegl, "operation", "gegl:write-buffer", "buffer",
edl->buffer, NULL);
+ clip->preview_store_buf = gegl_node_new_child (clip->gegl, "operation", "gegl:write-buffer", "buffer",
edl->buffer, NULL);
+#endif
+
+ gegl_node_link_many (clip->full_loader,
+ clip->loader,
+ clip->nop_scaled,
+ clip->nop_crop,
+ clip->nop_store_buf,
+ NULL);
+
+ g_mutex_init (&clip->mutex);
+
+ return clip;
+}
+
+Clip *clip_get_prev (Clip *self)
+{
+ GList *l;
+ GeglEDL *edl;
+ if (!self)
+ return NULL;
+ edl = self->edl;
+ Clip *prev = NULL;
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ if (clip->is_meta)
+ continue;
+ if (clip == self)
+ return prev;
+ prev = clip;
+ }
+ return NULL;
+}
+Clip *clip_get_next (Clip *self)
+{
+ GList *l;
+ GeglEDL *edl;
+ int found = 0;
+ if (!self)
+ return NULL;
+ edl = self->edl;
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ if (clip->is_meta)
+ continue;
+ if (found)
+ return clip;
+ if (clip == self)
+ found = 1;
+ }
+ return NULL;
+}
+
+void clip_free (Clip *clip)
+{
+ if (clip->path)
+ g_free (clip->path);
+ clip->path = NULL;
+
+ if (clip->gegl)
+ g_object_unref (clip->gegl);
+ clip->gegl = NULL;
+ g_mutex_clear (&clip->mutex);
+ g_free (clip);
+}
+
+void clip_set_path (Clip *clip, const char *in_path)
+{
+ char *path = NULL;
+ clip->is_chain = 0;
+ clip->is_meta = 0;
+
+ if (!in_path)
+ {
+ clip->is_meta = 1;
+ if (clip->path)
+ g_free (clip->path);
+ clip->path = NULL;
+ return;
+ }
+
+ if (!strcmp (in_path, "black") ||
+ !strcmp (in_path, "blue") ||
+ strstr (in_path, "gegl:"))
+ clip->is_chain = 1;
+
+ if (in_path[0] == '/' || clip->is_chain)
+ {
+ path = g_strdup (in_path);
+ }
+ else
+ {
+ if (clip->edl->parent_path)
+ path = g_strdup_printf ("%s%s", clip->edl->parent_path, in_path);
+ else
+ path = g_strdup_printf ("%s", in_path);
+ }
+
+ if (clip->path && !strcmp (clip->path, path))
+ {
+ g_free (path);
+ return;
+ }
+
+ if (clip->path)
+ g_free (clip->path);
+ clip->path = path;
+
+ if (clip->is_chain)
+ {
+ GError *error = NULL;
+ if (is_connected (clip->chain_loader, clip->loader))
+ remove_in_betweens (clip->chain_loader, clip->loader);
+ else
+ gegl_node_link_many (clip->chain_loader, clip->loader, NULL);
+
+ gegl_create_chain (path, clip->chain_loader, clip->loader, 0,
+ 400, //edl->height,
+ NULL, &error);
+ if (error)
+ {
+ /* should set error string */
+ fprintf (stderr, "chain source: %s\n", error->message);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ if (g_str_has_suffix (path, ".png") ||
+ g_str_has_suffix (path, ".jpg") ||
+ g_str_has_suffix (path, ".exr") ||
+ g_str_has_suffix (path, ".EXR") ||
+ g_str_has_suffix (path, ".PNG") ||
+ g_str_has_suffix (path, ".JPG"))
+ {
+ g_object_set (clip->full_loader, "operation", "gegl:load", NULL);
+ clip->static_source = 1;
+ }
+ else
+ {
+ g_object_set (clip->full_loader, "operation", "gegl:ff-load", NULL);
+ clip->static_source = 0;
+ }
+ }
+}
+
+int clip_get_start (Clip *clip)
+{
+ return clip->start;
+}
+
+int clip_get_end (Clip *clip)
+{
+ return clip->end;
+}
+
+int clip_get_frames (Clip *clip)
+{
+ int frames = clip_get_end (clip) - clip_get_start (clip) + 1;
+ if (frames < 0) frames = 0;
+ if (clip->is_meta)
+ return 0;
+ return frames;
+}
+
+void clip_set_start (Clip *clip, int start)
+{
+ clip->start = start;
+}
+void clip_set_end (Clip *clip, int end)
+{
+ clip->end = end;
+}
+
+void clip_set_range (Clip *clip, int start, int end)
+{
+ clip_set_start (clip, start);
+ clip_set_end (clip, end);
+}
+
+void clip_set_full (Clip *clip, const char *path, int start, int end)
+{
+ clip_set_path (clip, path);
+ clip_set_range (clip, start, end);
+}
+
+Clip *clip_new_full (GeglEDL *edl, const char *path, int start, int end)
+{
+ Clip *clip = clip_new (edl);
+ clip_set_full (clip, path, start, end);
+ return clip;
+}
+
+void clip_fade_set (Clip *clip, int do_fade_out)
+{
+ /* should cancel any computations due to fade when cancelling it, and add them when fade is set
+ */
+}
+
+const char *clip_get_path (Clip *clip)
+{
+ return clip->path;
+}
+
+static void clip_set_proxied (Clip *clip)
+{
+ if (clip->is_chain)
+ return;
+
+ if (clip->edl->use_proxies)
+ {
+ char *path = gedl_make_proxy_path (clip->edl, clip->path);
+ gchar *old = NULL;
+ gegl_node_get (clip->proxy_loader, "path", &old, NULL);
+
+ if (!old || !strcmp (old, "") || !strcmp (path, old))
+ gegl_node_set (clip->proxy_loader, "path", path, NULL);
+ gegl_node_link_many (clip->proxy_loader, clip->loader, NULL);
+ g_free (path);
+ }
+ else
+ {
+ gchar *old = NULL;
+ gegl_node_get (clip->full_loader, "path", &old, NULL);
+ if (!old || !strcmp (old, "") || !strcmp (clip->path, old))
+ gegl_node_set (clip->full_loader, "path", clip->path, NULL);
+ gegl_node_link_many (clip->full_loader, clip->loader, NULL);
+ }
+}
+
+void clip_set_frame_no (Clip *clip, int clip_frame_no)
+{
+ if (clip_frame_no < 0)
+ clip_frame_no = 0;
+
+ clip_set_proxied (clip);
+#if 0
+ {
+ gchar *old = NULL;
+ gegl_node_get (clip->full_loader, "path", &old, NULL);
+ if (!old || !strcmp (old, "") || !strcmp (clip->path, old))
+ gegl_node_set (clip->full_loader, "path", clip->path, NULL);
+ }
+#endif
+
+ if (!clip_is_static_source (clip))
+ {
+ if (clip->edl->use_proxies)
+ gegl_node_set (clip->proxy_loader, "frame", clip_frame_no, NULL);
+ else
+ gegl_node_set (clip->full_loader, "frame", clip_frame_no, NULL);
+ }
+}
+
+int clip_is_static_source (Clip *clip)
+{
+ return clip->static_source;
+}
+
+void clip_fetch_audio (Clip *clip)
+{
+ int use_proxies = clip->edl->use_proxies;
+
+ if (clip->audio)
+ {
+ g_object_unref (clip->audio);
+ clip->audio = NULL;
+ }
+
+ if (clip_is_static_source (clip))
+ clip->audio = NULL;
+ else
+ {
+ if (use_proxies)
+ gegl_node_get (clip->proxy_loader, "audio", &clip->audio, NULL);
+ else
+ gegl_node_get (clip->full_loader, "audio", &clip->audio, NULL);
+ }
+}
+
+int is_connected (GeglNode *a, GeglNode *b)
+{
+ GeglNode *iter = a;
+ while (iter && iter != b)
+ {
+ GeglNode **nodes = NULL;
+ int count = gegl_node_get_consumers (iter, "output", &nodes, NULL);
+ if (count) iter = nodes[0];
+ else
+ iter = NULL;
+ g_free (nodes);
+ }
+ if (iter == b)
+ return 1;
+ return 0;
+}
+
+void remove_in_betweens (GeglNode *nop_scaled, GeglNode *nop_filtered)
+{
+ GeglNode *iter = nop_scaled;
+ GList *collect = NULL;
+
+ iter = nop_filtered;
+ while (iter && iter != nop_scaled)
+ {
+ GeglNode **nodes = NULL;
+ iter = gegl_node_get_producer (iter, "input", NULL);
+ g_free (nodes);
+ if (iter && iter != nop_scaled)
+ collect = g_list_append (collect, iter);
+ }
+
+ while (collect)
+ {
+ g_object_unref (collect->data);
+ collect = g_list_remove (collect, collect->data);
+ }
+ gegl_node_link_many (nop_scaled, nop_filtered, NULL);
+}
+
+void clip_rig_chain (Clip *clip, int clip_frame_no)
+{
+ GeglEDL *edl = clip->edl;
+ int use_proxies = edl->use_proxies;
+
+ g_mutex_lock (&clip->mutex);
+
+ remove_in_betweens (clip->nop_scaled, clip->nop_crop);
+
+ gegl_node_set (clip->nop_scaled, "operation", "gegl:scale-size-keepaspect",
+ "y", 0.0,
+ "x", 1.0 * edl->width,
+ "sampler", use_proxies?GEDL_SAMPLER:GEGL_SAMPLER_CUBIC,
+ NULL);
+
+ gegl_node_set (clip->nop_crop, "width", 1.0 * edl->width,
+ "height", 1.0 * edl->height,
+ NULL);
+ if (clip->is_chain)
+ {
+ if (is_connected (clip->chain_loader, clip->loader))
+ remove_in_betweens (clip->chain_loader, clip->loader);
+ else
+ gegl_node_link_many (clip->chain_loader, clip->loader, NULL);
+
+ gegl_create_chain (clip->path, clip->chain_loader, clip->loader, clip_frame_no - clip->start,
+ edl->height,
+ NULL, NULL);//&error);
+ }
+
+ if (clip->filter_graph)
+ {
+ GError *error = NULL;
+ gegl_create_chain (clip->filter_graph, clip->nop_scaled, clip->nop_crop, clip_frame_no -
clip->start, edl->height, NULL, &error);
+ if (error)
+ {
+ /* should set error string */
+ fprintf (stderr, "%s\n", error->message);
+ g_error_free (error);
+ }
+ }
+ /**********************************************************************/
+
+
+ // flags,.. FULL PREVIEW FULL_CACHE|PREVIEW STORE_FULL_CACHE
+ clip_set_frame_no (clip, clip_frame_no);
+ g_mutex_unlock (&clip->mutex);
+}
+
+void clip_render_frame (Clip *clip, int clip_frame_no)
+{
+ clip_rig_chain (clip, clip_frame_no);
+ g_mutex_lock (&clip->mutex);
+ gegl_node_process (clip->loader); // for the audio fetch
+ clip_fetch_audio (clip);
+
+ g_mutex_unlock (&clip->mutex);
+}
+
+
+gchar *clip_get_frame_hash (Clip *clip, int clip_frame_no)
+{
+ GeglEDL *edl = clip->edl;
+ gchar *frame_recipe;
+ GChecksum *hash;
+ int is_static_source = clip_is_static_source (clip);
+
+ frame_recipe = g_strdup_printf ("%s: %s %i %s %ix%i",
+ "gedl-pre-4",
+ clip_get_path (clip),
+ clip->filter_graph || (!is_static_source) ? clip_frame_no : 0,
+ clip->filter_graph,
+ edl->video_width,
+ edl->video_height);
+
+ hash = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (hash, (void*)frame_recipe, -1);
+ char *ret = g_strdup (g_checksum_get_string(hash));
+
+ g_checksum_free (hash);
+ g_free (frame_recipe);
+
+ return ret;
+}
diff --git a/gcut/default.edl b/gcut/default.edl
new file mode 100644
index 0000000..089269b
--- /dev/null
+++ b/gcut/default.edl
@@ -0,0 +1,34 @@
+proxy-width=320
+proxy-height=240
+output-path=gedl.mp4
+video-width=800
+video-height=600
+fps=25.000000
+selection-start=291
+selection-end=291
+frame-scale=0.398424
+t0=56.702866
+frame-no=291
+
+gegl:color value=black gegl:crop width=400 height=400 31 50 --
+gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0 50 -- [fade=26] svg:src-over opi=0:0 aux=[
gegl:text opi=0:0 string='note: this is a gedl video\nproject for testing features\nit relies on synthetic
GEGL\npatterns instead of\nvideo footage\nto be to minimie size\nfor inclusion in gedl sources\nbeing able to
create this project \nfrom scratch in the gcut ui\nis a current goal.' size=0.071811296045780182rel
color='rgb(1.0000, 1.0000, 1.0000)' width=3 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel
y=0.051874876022338867rel ]
+-- #annotations
+gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 reset-origin=true 0 80 -- svg:src-over opi=0:0
aux=[ gegl:text opi=0:0 string='gcut' font='sans' size=0.20000000000000001rel color='rgb(1.0000, 1.0000,
1.0000)' width=1 height=1 gegl:gaussian-blur opi=0:0 std-dev-x={ 0.000000=0.400000rel 64.000000=0.000084rel
} std-dev-y={ 0.000000=0.200000rel } clip-extent=false gegl:translate opi=0:0 x=0.10000000000000001rel
y=0.3304142951965332rel ] gegl:lens-flare opi=0:0 pos-x={ 0.000000=-2.000000 60.000000=0.293004
100.000000=2.000000 } pos-y={ 0.000000=0.100000 60.000000=0.360697 100.000000=0.910000 }
+gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0 50 -- [fade=64] svg:src-over opi=0:0 aux=[
gegl:text opi=0:0 string='video editing\nfileformat+render / ui' size=0.10000000000000001rel
color='rgb(1.0000, 1.0000, 1.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel
y=0.55924856662750244rel ]
+gegl:cell-noise opi=0:0 shape={ 0.000000=1.000000 40.000000=1.850000 } palettize=true gegl:translate
opi=0:0 x={ 500.000000=50.000000 } y={ 500.000000=500.000000 } gegl:crop opi=0:0 width=400 height=400 0
40 -- [fade=23]
+gegl:simplex-noise opi=0:0 seed=605124352 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=14]
gegl:threshold opi=0:0 value=0.13152152299880981 gegl:gaussian-blur opi=0:0 std-dev-x=0.012467504478991032rel
std-dev-y=0.0055317822843790054rel gegl:exposure opi=1:0 black-level=0.029268454760313034
exposure=2.5591373443603516 gegl:levels opi=0:0 out-high=0.99985802173614502
+gegl:simplex-noise opi=0:0 scale=1.1438060998916626 seed=168548944 gegl:exposure opi=1:0
exposure=4.4479846954345703 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=34] svg:src-over opi=0:0
aux=[ gegl:text opi=0:0 string='single track ui with\ncross fade' size=0.12115363776683807rel
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:dropshadow opi=0:0 x=0.0018535566050559282rel
y=0.0019916105084121227rel radius=0.028898788616061211rel opacity=1.9511399269104004 gegl:translate opi=0:0
x=0.063941836357116699rel y=0.14936363697052002rel ]
+gegl:simplex-noise opi=0:0 scale=2.982180118560791 iterations=3 gegl:crop opi=0:0 width=400 height=400 0 25
-- [fade=34] svg:src-over opi=0:0 aux=[ gegl:text opi=0:0 string='multi-process rendering'
size=0.093153767287731171rel color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0
x=0.059999999999999998rel y=0.16329610347747803rel ]
+gegl:simplex-noise opi=0:0 seed=1031312640 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=34]
+gegl:simplex-noise opi=0:0 seed=331734624 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=34]
svg:src-over opi=0:0 aux=[ gegl:text opi=0:0 string='single track editing' size=0.10000000000000001rel
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.059999999999999998rel
y=0.80000000000000004rel ]
+gegl:simplex-noise gegl:crop width=400 height=400 0 36 -- [fade=34]
+gegl:fractal-explorer zoom={ 0=120 60=1300} shiftx={ 0=-320 60=-2500} shifty=-180 gegl:crop width=480
height=640 0 60 -- [fade=30]
+gegl:sinus opi=0:0 complexity={ 1.000000=0.500000 40.000000=4.000000 80.000000=0.200000 } gegl:crop
opi=0:0 width=400 height=400 0 80 -- [fade=60]
+gegl:noise-solid opi=0:0 y-size=4.6258320808410645 seed=668093247 gegl:crop opi=0:0 width=400 height=400 0
25 -- [fade=34] svg:src-over opi=0:0 aux=[ gegl:text opi=0:0 string='TODO' size=0.10000000000000001rel
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.050000000000000003rel
y=0.29999999999999999rel ]
+gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=36] svg:src-over opi=0:0 aux=[
gegl:text opi=0:0 string='crash recovery by default' size=0.081632360816001892rel color='rgb(1.0000, 1.0000,
0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel y=0.42925620079040527rel ]
+gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0 25 -- [fade=34] svg:src-over opi=0:0 aux=[
gegl:text opi=0:0 string='overlays:audio filter' size=0.10000000000000001rel color='rgb(1.0000, 1.0000,
0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel y=0.48208630084991455rel ]
+gegl:noise-solid gegl:crop width=400 height=400 0 25 -- [fade=34] over aux=[ text size=0.1rel
string='network rendering' color=yellow translate x=0.1rel y=0.8rel ]
+--#end titles
+gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0 474 -- svg:src-over opi=0:0 aux=[ gegl:text
opi=0:0 string='end titles\n\nfull of newlines\n\nand some images and more\n\nmost of the
time\n\n\nthough\n\nusing an image that one pans\nwill provide better typographic control\nperhaps based on a
pdf/svg\n\nthis is is also where I should stick misc info' size=0.040000000000000001rel color='rgb(1.0000,
1.0000, 1.0000)' width=1 height=8 gegl:translate opi=0:0 x=0.10000000000000001rel y={ 0.000000=1.000000rel
800.000000=-2.000000rel } ]
+-----
+fnord.mp4 40 140 0 --
diff --git a/gcut/gedl-ui.c b/gcut/gedl-ui.c
new file mode 100644
index 0000000..455f578
--- /dev/null
+++ b/gcut/gedl-ui.c
@@ -0,0 +1,2855 @@
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+
+#define USE_CAIRO_SCALING 1
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <mrg.h>
+#include <gegl.h>
+#include "gedl.h"
+#include <gegl-paramspecs.h>
+
+static int exited = 0;
+long babl_ticks (void);
+
+static unsigned char *copy_buf = NULL;
+static int copy_buf_len = 0;
+
+static int changed = 0;
+
+static int empty_selection (GeglEDL *edl)
+{
+ return edl->selection_start == edl->selection_end;
+}
+static void clip_split (Clip *oldclip, int shift);
+static void clip_remove (Clip *clip);
+
+static void mrg_gegl_blit (Mrg *mrg,
+ float x0, float y0,
+ float width, float height,
+ GeglNode *node,
+ float u, float v,
+ float opacity,
+ GeglEDL *edl)
+{
+ GeglRectangle bounds;
+
+ cairo_t *cr = mrg_cr (mrg);
+ cairo_surface_t *surface = NULL;
+
+ if (!node)
+ return;
+
+ bounds = *gegl_buffer_get_extent (edl->buffer_copy_temp);
+
+ if (width == -1 && height == -1)
+ {
+ width = bounds.width;
+ height = bounds.height;
+ }
+
+ if (width == -1)
+ width = bounds.width * height / bounds.height;
+ if (height == -1)
+ height = bounds.height * width / bounds.width;
+
+#ifdef USE_CAIRO_SCALING
+ if (copy_buf_len < bounds.width * bounds.height * 4)
+ {
+ if (copy_buf)
+ free (copy_buf);
+ copy_buf_len = bounds.width * bounds.height * 4;
+ copy_buf = malloc (copy_buf_len);
+ }
+ float scale = 1.0;
+ {
+ static int foo = 0;
+ unsigned char *buf = copy_buf;
+ GeglRectangle roi = {u, v, bounds.width, bounds.height};
+ static const Babl *fmt = NULL;
+
+foo++;
+ if (!fmt) fmt = babl_format ("cairo-RGB24");
+
+ {
+ scale = width / bounds.width;
+ if (height / bounds.height < scale)
+ scale = height / bounds.height;
+
+ // XXX: the 1.001 instead of 1.00 is to work around a gegl bug
+ gegl_buffer_get (edl->buffer_copy_temp, &roi, 1.001, fmt, buf, bounds.width * 4, GEGL_ABYSS_BLACK);
+ }
+
+ surface = cairo_image_surface_create_for_data (buf, CAIRO_FORMAT_RGB24, bounds.width, bounds.height,
bounds.width * 4);
+ }
+
+ cairo_save (cr);
+ cairo_surface_set_device_scale (surface, 1.0/scale, 1.0/scale);
+#else
+ if (copy_buf_len < width * height * 4)
+ {
+ if (copy_buf)
+ free (copy_buf);
+ copy_buf_len = width * height * 4;
+ copy_buf = malloc (copy_buf_len);
+ }
+ {
+ static int foo = 0;
+ unsigned char *buf = copy_buf;
+ GeglRectangle roi = {u, v, width, height};
+ static const Babl *fmt = NULL;
+
+foo++;
+ if (!fmt) fmt = babl_format ("cairo-RGB24");
+
+ {
+ float scale = 1.0;
+ scale = width / bounds.width;
+ if (height / bounds.height < scale)
+ scale = height / bounds.height;
+
+ gegl_buffer_get (edl->buffer_copy_temp, &roi, scale, fmt, buf, width * 4, GEGL_ABYSS_BLACK);
+ }
+
+ surface = cairo_image_surface_create_for_data (buf, CAIRO_FORMAT_RGB24, width, height, width * 4);
+ }
+
+ cairo_save (cr);
+ cairo_surface_set_device_scale (surface, 1.0, 1.0);
+#endif
+ cairo_rectangle (cr, x0, y0, width, height);
+
+
+ cairo_clip (cr);
+ cairo_translate (cr, x0, y0);
+ cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ if (opacity < 0.9)
+ {
+ cairo_paint_with_alpha (cr, opacity);
+ }
+ else
+ {
+ cairo_paint (cr);
+ }
+ cairo_surface_destroy (surface);
+ cairo_restore (cr);
+}
+
+typedef struct _State State;
+
+struct _State {
+ void (*ui) (Mrg *mrg, void *state);
+ Mrg *mrg;
+ GeglEDL *edl;
+ char *path;
+ char *save_path;
+};
+
+float fpx = 2;
+
+#if 0
+ // with copy/paste we'd want to have this:
+ // available: with a path + newline being valid, and perhaps
+ // sufficient for the drag + drop case as well
+
+static void insert_string (GeglEDL *edl, const char *string)
+{
+}
+#endif
+
+static void insert_clip (GeglEDL *edl, const char *path,
+ int in, int out)
+{
+ GList *iter;
+ Clip *clip, *cur_clip;
+ int end_frame = edl->frame_no;
+ if (in < 0)
+ in = 0;
+ if (out < 0)
+ {
+ int duration = 0;
+ if (!empty_selection (edl))
+ {
+ out = edl->selection_end - edl->selection_start;
+ if (out < 0) out = -out;
+ }
+ else
+ {
+ gedl_get_video_info (path, &duration, NULL);
+ out = duration;
+ }
+ if (out < in)
+ out = in;
+ }
+ clip = clip_new_full (edl, path, in, out);
+ clip->title = g_strdup (basename (path));
+ int clip_frame_no;
+ cur_clip = gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+
+ if (empty_selection (edl))
+ {
+ gedl_get_duration (edl);
+ if (edl->frame_no != cur_clip->abs_start)
+ {
+ gedl_get_duration (edl);
+ clip_split (cur_clip, clip_frame_no);
+ cur_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ }
+ }
+ else
+ {
+ Clip *last_clip;
+ int sin, sout;
+ sin = edl->selection_start;
+ sout = edl->selection_end + 1;
+ if (sin > sout)
+ {
+ sout = edl->selection_start + 1;
+ sin = edl->selection_end;
+ }
+ int cur_clip_frame_no;
+ cur_clip = gedl_get_clip (edl, sin, &cur_clip_frame_no);
+ clip_split (cur_clip, cur_clip_frame_no);
+ gedl_get_duration (edl);
+ int last_clip_frame_no;
+ cur_clip = gedl_get_clip (edl, sin, &cur_clip_frame_no);
+ last_clip = gedl_get_clip (edl, sout, &last_clip_frame_no);
+ if (cur_clip == last_clip)
+ {
+ clip_split (last_clip, last_clip_frame_no);
+ }
+ last_clip = edl_get_clip_for_frame (edl, sout);
+
+ cur_clip = edl_get_clip_for_frame (edl, sin);
+ while (cur_clip != last_clip)
+ {
+ clip_remove (cur_clip);
+ cur_clip = edl_get_clip_for_frame (edl, sin);
+ }
+ edl->frame_no = sin;
+ }
+
+ cur_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ iter = g_list_find (edl->clips, cur_clip);
+ edl->clips = g_list_insert_before (edl->clips, iter, clip);
+ end_frame += out - in + 1;
+ edl->frame_no = end_frame;
+
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+
+ gedl_make_proxies (edl);
+}
+
+static void drag_dropped (MrgEvent *ev, void *data1, void *data2)
+{
+ GeglEDL *edl = data2;
+
+ char *str = g_strdup (ev->string);
+ char *s = str;
+ char *e;
+
+ e = strchr (s, '\r');
+ while (e)
+ {
+ *e = '\0';
+ if (strstr (s, "file://")) s+= strlen ("file://");
+ insert_clip (edl, s, -1, -1);
+ s = e+1;
+ if (*s == '\n') s++;
+ e = strchr (s, '\r');
+ }
+
+ g_free (str);
+}
+static void scroll_to_fit (GeglEDL *edl, Mrg *mrg);
+
+static void clicked_clip (MrgEvent *e, void *data1, void *data2)
+{
+ Clip *clip = data1;
+ GeglEDL *edl = data2;
+
+ edl->frame_no = e->x;
+ edl->selection_start = edl->frame_no;
+ edl->selection_end = edl->frame_no;
+ edl->active_clip = clip;
+ edl->playing = 0;
+ scroll_to_fit (edl, e->mrg);
+ mrg_queue_draw (e->mrg, NULL);
+ changed++;
+}
+
+#include <math.h>
+
+static void drag_clip (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data2;
+ edl->frame_no = e->x;
+ if (e->x >= edl->selection_start)
+ {
+ edl->selection_end = e->x;
+ }
+ else
+ {
+ edl->selection_start = e->x;
+ }
+ scroll_to_fit (edl, e->mrg);
+ mrg_queue_draw (e->mrg, NULL);
+ changed++;
+}
+
+static void drag_t0 (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data2;
+ edl->t0 += e->delta_x;
+ if (edl->t0 < 0.0)
+ edl->t0 = 0.0;
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+ changed++;
+}
+
+static void drag_fpx (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data2;
+ edl->scale = (mrg_width(e->mrg)*edl->scale + e->delta_x) / mrg_width(e->mrg);
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+ changed++;
+}
+
+static void released_clip (MrgEvent *e, void *data1, void *data2)
+{
+ Clip *clip = data1;
+ GeglEDL *edl = data2;
+ edl->frame_no = e->x;
+ edl->active_clip = clip;
+ if (edl->selection_end < edl->selection_start)
+ {
+ int temp = edl->selection_end;
+ edl->selection_end = edl->selection_start;
+ edl->selection_start = temp;
+ }
+ scroll_to_fit (edl, e->mrg);
+ mrg_queue_draw (e->mrg, NULL);
+ changed++;
+}
+
+static void stop_playing (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ edl->playing = 0;
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void select_all (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gint end = gedl_get_duration (edl) - 1;
+ if (edl->selection_start == 0 && edl->selection_end == end)
+ {
+ gedl_set_selection (edl, 0, 0);
+ }
+ else
+ {
+ gedl_set_selection (edl, 0, end);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+
+
+static void prev_cut (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (!edl->active_clip)
+ return;
+ {
+ GList *iter = g_list_find (edl->clips, edl->active_clip);
+
+ if (iter)
+ {
+ if (edl->frame_no == edl->active_clip->abs_start)
+ {
+ iter = iter->prev;
+ if (iter) edl->active_clip = iter->data;
+ }
+ }
+ edl->frame_no = edl->active_clip->abs_start;
+ edl->selection_start = edl->selection_end = edl->frame_no;
+ }
+ mrg_event_stop_propagate (event);
+ scroll_to_fit (edl, event->mrg);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void next_cut (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (!edl->active_clip)
+ return;
+ {
+ GList *iter = g_list_find (edl->clips, edl->active_clip);
+ if (iter) iter = iter->next;
+ if (iter)
+ {
+ edl->active_clip = iter->data;
+ edl->frame_no = edl->active_clip->abs_start;
+ }
+ else
+ {
+ edl->frame_no = edl->active_clip->abs_start + clip_get_frames (edl->active_clip);
+ }
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ edl->selection_start = edl->selection_end = edl->frame_no;
+ scroll_to_fit (edl, event->mrg);
+ changed++;
+}
+
+static void extend_selection_to_previous_cut (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int sel_start, sel_end;
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+
+ gedl_get_selection (edl, &sel_start, &sel_end);
+ prev_cut (event, data1, data2);
+ sel_start = edl->frame_no;
+ gedl_set_selection (edl, sel_start, sel_end);
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+
+static void extend_selection_to_next_cut (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int sel_start, sel_end;
+
+ gedl_get_selection (edl, &sel_start, &sel_end);
+ next_cut (event, data1, data2);
+ sel_start = edl->frame_no;
+ gedl_set_selection (edl, sel_start, sel_end);
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void extend_selection_to_the_left (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int sel_start, sel_end;
+
+ gedl_get_selection (edl, &sel_start, &sel_end);
+ if (edl->frame_no == sel_end)
+ {
+ sel_end --;
+ edl->frame_no --;
+ }
+ else if (edl->frame_no == sel_start)
+ {
+ sel_start --;
+ edl->frame_no --;
+ }
+ else
+ {
+ sel_start = sel_end = edl->frame_no;
+ sel_end --;
+ edl->frame_no --;
+ }
+ gedl_set_selection (edl, sel_start, sel_end);
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+
+static void extend_selection_to_the_right (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int sel_start, sel_end;
+
+ gedl_get_selection (edl, &sel_start, &sel_end);
+ if (edl->frame_no == sel_end)
+ {
+ sel_end ++;
+ edl->frame_no ++;
+ }
+ else if (edl->frame_no == sel_start)
+ {
+ sel_start ++;
+ edl->frame_no ++;
+ }
+ else
+ {
+ sel_start = sel_end = edl->frame_no;
+ sel_end ++;
+ edl->frame_no ++;
+ }
+ gedl_set_selection (edl, sel_start, sel_end);
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static int ui_tweaks = 0;
+static int are_mergable (Clip *clip1, Clip *clip2, int delta)
+{
+ if (!clip1 || !clip2)
+ return 0;
+ if (!clip1->path)
+ return 0;
+ if (!clip2->path)
+ return 0;
+ if (strcmp (clip1->path, clip2->path))
+ return 0;
+ if (clip2->start != (clip1->end + 1 + delta))
+ return 0;
+ if (clip1->filter_graph==NULL && clip2->filter_graph != NULL)
+ return 0;
+ if (clip1->filter_graph!=NULL && clip2->filter_graph == NULL)
+ return 0;
+ if (clip1->filter_graph && strcmp (clip1->filter_graph, clip2->filter_graph))
+ return 0;
+ return 1;
+}
+
+static void clip_remove (Clip *clip)
+{
+ GeglEDL *edl = clip->edl;
+ GList *iter = g_list_find (edl->clips, clip);
+
+ if (iter->next)
+ iter = iter->next;
+ else if (iter->prev)
+ iter = iter->prev;
+ else
+ return;
+
+ edl->clips = g_list_remove (edl->clips, clip);
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+}
+
+static GeglNode *selected_node = NULL;
+
+static void remove_clip (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+
+ if (!edl->active_clip)
+ return;
+
+ if (selected_node)
+ {
+ GeglNode *producer = NULL;
+ GeglNode *consumer = NULL;
+ GeglNode **nodes = NULL;
+ const gchar **pads = NULL;
+ char *prodpad = NULL;
+
+ int count = gegl_node_get_consumers (selected_node, "output", &nodes, &pads);
+ if (count)
+ {
+ consumer= nodes[0];
+ }
+
+ producer = gegl_node_get_producer (selected_node, "input", &prodpad);
+
+ if (producer && consumer)
+ {
+ fprintf (stderr, "%p %s %p %s\n", producer, prodpad, consumer, pads[0]);
+ gegl_node_connect_to (producer, prodpad, consumer, pads[0]);
+ }
+
+ if (prodpad)
+ g_free (prodpad);
+
+ g_object_unref (selected_node);
+ selected_node = NULL;
+ ui_tweaks++;
+ }
+ else
+ {
+ clip_remove (edl->active_clip);
+ }
+
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static GeglNode *filter_start;
+
+static void make_rel_props (GeglNode *node)
+{
+ unsigned int n_props;
+ GParamSpec ** props = gegl_operation_list_properties (gegl_node_get_operation (node),
+ &n_props);
+
+ for (int i = 0; i <n_props; i ++)
+ {
+ const char *unit = gegl_operation_get_property_key (gegl_node_get_operation (node), props[i]->name,
"unit");
+
+ if (unit && !strcmp (unit, "pixel-distance"))
+ {
+ char tmpbuf[1024];
+ sprintf (tmpbuf, "%s-rel", props[i]->name);
+ GQuark rel_quark = g_quark_from_string (tmpbuf);
+ g_object_set_qdata_full (G_OBJECT(node), rel_quark, g_strdup("foo"), g_free);
+ }
+
+ }
+}
+
+
+ void insert_node (GeglNode *selected_node, GeglNode *new)
+ {
+ GeglNode **nodes = NULL;
+ const gchar **pads = NULL;
+
+ int count = gegl_node_get_consumers (selected_node, "output", &nodes, &pads);
+ make_rel_props (new);
+
+ gegl_node_link_many (selected_node, new, NULL);
+ if (count)
+ {
+ gegl_node_connect_to (new, "output", nodes[0], pads[0]);
+ }
+ }
+
+char *filter_query = NULL;
+
+static void insert_filter (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+
+ if (!edl->active_clip)
+ return;
+
+ filter_query = g_strdup ("");
+ mrg_set_cursor_pos (event->mrg, 0);
+
+ if (!selected_node)
+ selected_node = filter_start;
+
+#if 0
+ GeglNode *new = NULL;
+ new = gegl_node_new_child (edl->gegl, "operation", "gegl:unsharp-mask", NULL);
+ insert_node (selected_node, new);
+ selected_node = new;
+#endif
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+
+}
+
+static void merge_clip (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ GList *iter = g_list_find (edl->clips, edl->active_clip);
+ Clip *clip2 = NULL;
+ if (iter) iter = iter->prev;
+ if (iter) clip2 = iter->data;
+
+ if (!are_mergable (clip2, edl->active_clip, 0))
+ return;
+
+ clip2->end = edl->active_clip->end;
+
+ remove_clip (event, data1, data2);
+ edl->active_clip = clip2;
+}
+
+static void toggle_use_proxies (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+
+ if (!edl->playing) // disallowing - to avoid some races
+ {
+ gedl_set_use_proxies (edl, edl->use_proxies?0:1);
+ gedl_cache_invalid (edl);
+
+ if (edl->use_proxies)
+ gedl_make_proxies (edl);
+ }
+
+ if (event)
+ {
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ }
+}
+
+static void clip_split (Clip *oldclip, int shift)
+{
+ GeglEDL *edl = oldclip->edl;
+ GList *iter = g_list_find (edl->clips, oldclip);
+ Clip *clip = clip_new_full (edl, oldclip->path, oldclip->start, oldclip->end);
+ edl->clips = g_list_insert_before (edl->clips, iter, clip);
+
+ if (oldclip->filter_graph)
+ clip->filter_graph = g_strdup (oldclip->filter_graph);
+
+ clip->end = shift - 1;
+ oldclip->start = shift;
+}
+
+static void split_clip (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int clip_frame_no = 0;
+ Clip *clip = gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+ if (!edl->active_clip)
+ return;
+
+ if (edl->active_clip !=clip)
+ {
+ g_warning ("hmmm");
+ return;
+ }
+
+ clip_split (edl->active_clip, clip_frame_no);
+ {
+ //edl->active_clip = clip;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void toggle_fade (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+
+ if (!edl->active_clip)
+ return;
+
+ if (edl->active_clip->fade)
+ {
+ edl->active_clip->fade = 0;
+ }
+ else
+ {
+ edl->active_clip->fade = (edl->frame - edl->active_clip->abs_start)*2;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void duplicate_clip (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+
+ if (!edl->active_clip)
+ return;
+ {
+ GList *iter = g_list_find (edl->clips, edl->active_clip);
+ Clip *clip = clip_new_full (edl, edl->active_clip->path, edl->active_clip->start, edl->active_clip->end);
+ edl->clips = g_list_insert_before (edl->clips, iter, clip);
+ if (edl->active_clip->filter_graph)
+ clip->filter_graph = g_strdup (edl->active_clip->filter_graph);
+ edl->active_clip = clip;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static int help = 0;
+
+static void toggle_help (MrgEvent *event, void *data1, void *data2)
+{
+ //GeglEDL *edl = data1;
+ help = help ? 0 : 1;
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void save_edl (GeglEDL *edl)
+{
+ if (edl->path)
+ {
+ gedl_save_path (edl, edl->path);
+ }
+}
+
+#if 1
+static void save (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ save_edl (edl);
+}
+#endif
+
+static gboolean save_idle (Mrg *mrg, gpointer edl)
+{
+ if (changed)
+ {
+ changed = 0;
+ save_edl (edl);
+ }
+ return TRUE;
+}
+
+static void set_range (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ int start, end;
+
+ gedl_get_selection (edl, &start, &end);
+ gedl_set_range (edl, start, end);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+#if 0
+static void up (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip && edl->filter_edited)
+ return;
+ if (edl->clip_query_edited)
+ {
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ edl->clip_query_edited = 0;
+ }
+ else if (edl->active_source)
+ {
+ GList *l;
+ int found = 0;
+ edl->active_source->editing = 0;
+ for (l = edl->clip_db; l; l = l->next)
+ {
+ if (l->next && l->next->data == edl->active_source)
+ {
+ edl->active_source = l->data;
+ make_active_source (edl, edl->active_source);
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ edl->active_source = NULL;
+ //edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ edl->clip_query_edited = 1;
+ mrg_set_cursor_pos (event->mrg, strlen (edl->clip_query));
+ }
+ }
+ else if (edl->active_clip)
+ {
+ if(edl->filter_edited)
+ {
+ edl->active_source = edl->clip_db?g_list_last (edl->clip_db)->data:NULL;
+ make_active_source (edl, edl->active_source);
+ edl->filter_edited = 0;
+ edl->active_clip = NULL;
+ }
+ else
+ {
+ edl->active_source = NULL;
+ edl->filter_edited = 1;
+ mrg_set_cursor_pos (event->mrg, 0);
+ fprintf (stderr, "...\n");
+ }
+ }
+ else
+ {
+ fprintf (stderr, "uh\n");
+ }
+ mrg_queue_draw (event->mrg, NULL);
+ mrg_event_stop_propagate (event);
+ changed++;
+}
+#endif
+
+#if 0
+static void down (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip && edl->filter_edited)
+ return;
+ if (edl->clip_query_edited)
+ {
+ edl->active_source = edl->clip_db->data;
+ make_active_source (edl, (void*)edl->active_source);
+ edl->clip_query_edited = 0;
+ }
+ else if (edl->active_source)
+ {
+ GList *l;
+ int found = 0;
+ edl->active_source->editing = 0;
+ for (l = edl->clip_db; l; l = l->next)
+ {
+ if (l->next && l->data == edl->active_source)
+ {
+ edl->active_source = l->next->data;
+ make_active_source (edl, l->next->data);
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ edl->active_source = NULL;
+ }
+ }
+ else
+ {
+ edl->active_clip = NULL;
+ edl->clip_query_edited = 1;
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+#endif
+
+static void step_frame_back (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ stop_playing (event, data1, data2);
+ {
+ edl->selection_start = edl->selection_end;
+ edl->frame_no --;
+ if (edl->frame_no < 0)
+ edl->frame_no = 0;
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void step_frame (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ stop_playing (event, data1, data2);
+ {
+ edl->selection_start = edl->selection_end;
+ edl->frame_no ++;
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void clip_end_start_dec (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ Clip *clip1, *clip2;
+ if (edl->selection_start < edl->selection_end)
+ {
+ clip1 = edl_get_clip_for_frame (edl, edl->selection_start);
+ clip2 = edl_get_clip_for_frame (edl, edl->selection_end);
+ }
+ else
+ {
+ clip1 = edl_get_clip_for_frame (edl, edl->selection_end);
+ clip2 = edl_get_clip_for_frame (edl, edl->selection_start);
+ }
+ edl->selection_start--;
+ edl->selection_end--;
+ clip1->end--;
+ clip2->start--;
+ edl->frame_no--;
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_end_start_inc (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ Clip *clip1, *clip2;
+ if (edl->selection_start < edl->selection_end)
+ {
+ clip1 = edl_get_clip_for_frame (edl, edl->selection_start);
+ clip2 = edl_get_clip_for_frame (edl, edl->selection_end);
+ }
+ else
+ {
+ clip1 = edl_get_clip_for_frame (edl, edl->selection_end);
+ clip2 = edl_get_clip_for_frame (edl, edl->selection_start);
+ }
+ edl->selection_start++;
+ edl->selection_end++;
+ clip1->end++;
+ clip2->start++;
+ edl->frame_no++;
+
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_start_end_inc (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->end++;
+ edl->active_clip->start++;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_start_end_dec (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->end--;
+ edl->active_clip->start--;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_end_inc (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->end++;
+ edl->frame_no++;
+ }
+ gedl_cache_invalid (edl);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_end_dec (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->end--;
+ edl->frame_no--;
+ gedl_cache_invalid (edl);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void clip_start_inc (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->start++;
+ gedl_cache_invalid (edl);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void clip_start_dec (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (edl->active_clip)
+ {
+ edl->active_clip->start--;
+ gedl_cache_invalid (edl);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+#if 0
+static void toggle_edit_source (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ edl->active_source->editing = !edl->active_source->editing;
+ if (edl->active_source->editing)
+ mrg_set_cursor_pos (event->mrg, strlen (edl->active_source->title));
+ gedl_cache_invalid (edl);
+ mrg_queue_draw (event->mrg, NULL);
+}
+#endif
+
+static void do_quit (MrgEvent *event, void *data1, void *data2)
+{
+ exited = 1;
+ killpg(0, SIGUSR2);
+ mrg_quit (event->mrg);
+}
+
+long last_frame = 0;
+
+void gedl_ui (Mrg *mrg, void *data);
+
+static void zoom_timeline (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ switch (event->scroll_direction)
+ {
+ case MRG_SCROLL_DIRECTION_UP:
+ edl->t0 += event->x * edl->scale;
+ edl->scale *= 1.02;
+ edl->t0 -= event->x * edl->scale;
+ break;
+ case MRG_SCROLL_DIRECTION_DOWN:
+ edl->t0 += event->x * edl->scale;
+ edl->scale /= 1.02;
+ edl->t0 -= event->x * edl->scale;
+ break;
+ case MRG_SCROLL_DIRECTION_LEFT:
+ edl->t0 += edl->scale * 2;
+ break;
+ case MRG_SCROLL_DIRECTION_RIGHT:
+ edl->t0 -= edl->scale * 2;
+ break;
+ }
+
+ scroll_to_fit (edl, event->mrg);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+#define PAD_DIM 8
+int VID_HEIGHT=96; // XXX: ugly global
+
+void render_clip (Mrg *mrg, GeglEDL *edl, const char *clip_path, int clip_start, int clip_frames, double x,
double y, int fade, int fade2)
+{
+ char *thumb_path;
+ if (!clip_path)
+ {
+ return; // XXX: draw string!
+ }
+ thumb_path = gedl_make_thumb_path (edl, clip_path);
+
+ cairo_t *cr = mrg_cr (mrg);
+ if (fade || fade2)
+ {
+ cairo_move_to (cr, x, y + VID_HEIGHT/2);
+ cairo_line_to (cr, x + fade/2, y);
+ cairo_line_to (cr, x + clip_frames + fade2/2, y);
+ cairo_line_to (cr, x + clip_frames - fade2/2, y + VID_HEIGHT);
+ cairo_line_to (cr, x - fade/2, y + VID_HEIGHT);
+ cairo_line_to (cr, x, y + VID_HEIGHT/2);
+ }
+ else
+ {
+ cairo_rectangle (cr, x, y, clip_frames, VID_HEIGHT);
+ }
+
+ int width, height;
+ MrgImage *img = mrg_query_image (mrg, thumb_path, &width, &height);
+ g_free (thumb_path);
+ if (!edl->playing && img && width > 0)
+ {
+ cairo_surface_t *surface = mrg_image_get_surface (img);
+ cairo_matrix_t matrix;
+ cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
+
+ //cairo_matrix_init_rotate (&matrix, M_PI / 2); /* compensate for .. */
+ //cairo_matrix_translate (&matrix, 0, -width); /* vertical format */
+
+ cairo_matrix_init_scale (&matrix, 1.0, height* 1.0/ VID_HEIGHT);
+ cairo_matrix_translate (&matrix, -(x - clip_start), -y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+ cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+ cairo_set_source (cr, pattern);
+
+ cairo_save (cr);
+ cairo_clip_preserve (cr);
+ cairo_paint (cr);
+ cairo_restore (cr);
+ }
+ else
+ {
+ //cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, 0.5);
+ //cairo_fill_preserve (cr);
+ }
+}
+
+static void scroll_to_fit (GeglEDL *edl, Mrg *mrg)
+{
+ /* scroll to fit playhead */
+ if ( (edl->frame_no - edl->t0) / edl->scale > mrg_width (mrg) * 0.9)
+ edl->t0 = edl->frame_no - (mrg_width (mrg) * 0.8) * edl->scale;
+ else if ( (edl->frame_no - edl->t0) / edl->scale < mrg_width (mrg) * 0.1)
+ edl->t0 = edl->frame_no - (mrg_width (mrg) * 0.2) * edl->scale;
+}
+
+static void shuffle_forward (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+
+ GList *prev = NULL,
+ *next = NULL,
+ *self = g_list_find (edl->clips, edl->active_clip);
+
+ if (self)
+ {
+ next = self->next;
+ prev = self->prev;
+
+ if (self && next)
+ {
+ GList *nextnext = next->next;
+ if (prev)
+ prev->next = next;
+ next->prev = prev;
+ next->next = self;
+ self->prev = next;
+ self->next = nextnext;
+ if (self->next)
+ self->next->prev = self;
+ edl->frame_no += clip_get_frames (next->data);
+ }
+ }
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void shuffle_back (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+
+ GList *prev = NULL,
+ *prevprev = NULL,
+ *next = NULL,
+ *self = g_list_find (edl->clips, edl->active_clip);
+
+ if (self)
+ {
+ next = self->next;
+ prev = self->prev;
+ if (prev)
+ prevprev = prev->prev;
+
+ if (self && prev)
+ {
+ if (prevprev)
+ prevprev->next = self;
+ self->prev = prevprev;
+ self->next = prev;
+ prev->prev = self;
+ prev->next = next;
+ if (next)
+ next->prev = prev;
+
+ edl->frame_no -= clip_get_frames (prev->data);
+ }
+ }
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void slide_forward (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+
+ GList *prev = NULL,
+ *next = NULL,
+ *self = g_list_find (edl->clips, edl->active_clip);
+ /*
+ situations to deal with:
+ inside mergable clips
+ last of inside mergable clips
+ inside mergable clips if we padded prev
+ last of inside mergable clips if we padded prev
+ non mergable clips -> split next and shuffle into
+ */
+
+ if (self)
+ {
+ next = self->next;
+ prev = self->prev;
+
+ if (self && next && prev)
+ {
+ Clip *prev_clip = prev->data;
+ Clip *next_clip = next->data;
+ Clip *self_clip = self->data;
+
+ if (are_mergable (prev_clip, next_clip, 0))
+ {
+ if (clip_get_frames (next_clip) == 1)
+ {
+ prev_clip->end++;
+ edl->clips = g_list_remove (edl->clips, next_clip);
+ edl->frame_no ++;
+ }
+ else
+ {
+ prev_clip->end ++;
+ next_clip->start ++;
+ edl->frame_no ++;
+ }
+ } else if (are_mergable (prev_clip, next_clip, clip_get_frames (self_clip)))
+ {
+ if (clip_get_frames (next_clip) == 1)
+ {
+ prev_clip->end++;
+ edl->clips = g_list_remove (edl->clips, next_clip);
+ edl->frame_no ++;
+ }
+ else
+ {
+ prev_clip->end ++;
+ next_clip->start ++;
+ edl->frame_no ++;
+ }
+ }
+ else {
+ if (clip_get_frames (next_clip) == 1)
+ {
+ int frame_no = edl->frame_no + 1;
+ shuffle_forward (event, data1, data2);
+ edl->frame_no = frame_no;
+ } else {
+ int frame_no = edl->frame_no + 1;
+ clip_split (next_clip, next_clip->start + 1);
+ shuffle_forward (event, data1, data2);
+ edl->frame_no = frame_no;
+ }
+ }
+ }
+ }
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void slide_back (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+
+ GList *prev = NULL,
+ *next = NULL,
+ *self = g_list_find (edl->clips, edl->active_clip);
+ /*
+ situations to deal with:
+ inside mergable clips
+ last of inside mergable clips
+ inside mergable clips if we padded prev
+ last of inside mergable clips if we padded prev
+ non mergable clips -> split next and shuffle into
+ */
+
+ if (self)
+ {
+ next = self->next;
+ prev = self->prev;
+
+ if (self && next && prev)
+ {
+ Clip *prev_clip = prev->data;
+ Clip *next_clip = next->data;
+ Clip *self_clip = self->data;
+
+ if (are_mergable (prev_clip, next_clip, 0))
+ {
+ if (clip_get_frames (prev_clip) == 1)
+ {
+ next_clip->start --;
+ edl->clips = g_list_remove (edl->clips, prev_clip);
+ edl->frame_no --;
+ }
+ else
+ {
+ prev_clip->end --;
+ next_clip->start --;
+ edl->frame_no --;
+ }
+ } else if (are_mergable (prev_clip, next_clip, clip_get_frames (self_clip)))
+ {
+ if (clip_get_frames (prev_clip) == 1)
+ {
+ prev_clip->end--;
+ edl->clips = g_list_remove (edl->clips, prev_clip);
+ edl->frame_no --;
+ }
+ else
+ {
+ prev_clip->end --;
+ next_clip->start --;
+ edl->frame_no --;
+ }
+ }
+ else
+ {
+ if (clip_get_frames (prev_clip) == 1)
+ {
+ int frame_no = edl->frame_no - 1;
+ shuffle_back (event, data1, data2);
+ edl->frame_no = frame_no;
+ } else {
+ int frame_no = edl->frame_no - 1;
+ clip_split (prev_clip, prev_clip->end );
+ shuffle_back (event, data1, data2);
+ edl->frame_no = frame_no;
+ }
+ }
+ }
+ }
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+static void zoom_1 (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+ edl->scale = 1.0;
+ scroll_to_fit (edl, event->mrg);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void zoom_fit (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gedl_cache_invalid (edl);
+ edl->t0 = 0.0;
+ edl->scale = gedl_get_duration (edl) * 1.0 / mrg_width (event->mrg);
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+}
+
+static void tweaked_state (Mrg *mrg)
+{
+ ui_tweaks++;
+}
+
+static void toggle_bool (MrgEvent *e, void *data1, void *data2)
+{
+ GeglNode *node = data1;
+ const char *prop = data2;
+ gboolean old_value;
+ gboolean new_value;
+ gegl_node_get (node, prop, &old_value, NULL);
+ new_value = !old_value;
+ gegl_node_set (node, prop, new_value, NULL);
+
+ changed++;
+ mrg_event_stop_propagate (e);
+ mrg_queue_draw (e->mrg, NULL);
+ tweaked_state (e->mrg);
+}
+
+GeglNode *snode = NULL;
+const char *sprop = NULL;
+
+static void edit_string (MrgEvent *e, void *data1, void *data2)
+{
+ GeglNode *node = data1;
+ const char *prop = data2;
+ snode = node;
+ sprop = prop;
+ changed++;
+ mrg_event_stop_propagate (e);
+ mrg_set_cursor_pos (e->mrg, 0); // XXX: could fech strlen and use that
+ mrg_queue_draw (e->mrg, NULL);
+ tweaked_state (e->mrg);
+}
+
+static void jump_to_pos (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gint pos = GPOINTER_TO_INT(data2);
+
+ fprintf (stderr, "set frame %i\n", pos);
+ edl->frame_no = pos;
+ mrg_event_stop_propagate (e);
+ mrg_queue_draw (e->mrg, NULL);
+}
+
+static void end_edit (MrgEvent *e, void *data1, void *data2)
+{
+ snode = NULL;
+ sprop = NULL;
+ mrg_event_stop_propagate (e);
+ mrg_set_cursor_pos (e->mrg, 0); // XXX: could fech strlen and use that
+ mrg_queue_draw (e->mrg, NULL);
+}
+
+static void drag_double_slider (MrgEvent *e, void *data1, void *data2)
+{
+ gpointer *data = data1;
+ GeglParamSpecDouble *gspec = (void*)data2;
+ GParamSpec *spec = (void*)data2;
+ GeglNode *node = data[1];
+ GeglEDL *edl = data[0];
+ char tmpbuf[1024];
+ sprintf (tmpbuf, "%s-rel", spec->name);
+ GQuark rel_quark = g_quark_from_string (tmpbuf);
+ sprintf (tmpbuf, "%s-anim", spec->name);
+ GQuark anim_quark = g_quark_from_string (tmpbuf);
+ double ui_min = gspec->ui_minimum;
+ double ui_max = gspec->ui_maximum;
+ if (g_object_get_qdata (G_OBJECT (node), rel_quark) && 1)
+ {
+ ui_min /= 1000.0;
+ ui_max /= 1000.0;
+ }
+
+ float new_val = e->x * (ui_max - ui_min) + ui_min;
+
+ if (g_object_get_qdata (G_OBJECT (node), anim_quark))
+ {
+ GeglPath *path = g_object_get_qdata (G_OBJECT (node), anim_quark);
+ int nodes = gegl_path_get_n_nodes (path);
+ int i;
+ int clip_frame_no=0;
+ GeglPathItem path_item;
+ gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+
+ for (i = 0; i < nodes; i ++)
+ {
+ gegl_path_get_node (path, i, &path_item);
+ if (fabs (path_item.point[0].x - clip_frame_no) < 0.5)
+ {
+ path_item.point[0].x = clip_frame_no;
+ path_item.point[0].y = new_val;
+ gegl_path_replace_node (path, i, &path_item);
+ goto done;
+ }
+ else if (path_item.point[0].x > clip_frame_no)
+ {
+ path_item.point[0].x = clip_frame_no;
+ path_item.point[0].y = new_val;
+ gegl_path_insert_node (path, i - 1, &path_item);
+ goto done;
+ }
+ }
+ path_item.type = 'L';
+ path_item.point[0].x = clip_frame_no;
+ path_item.point[0].y = new_val;
+ gegl_path_insert_node (path, -1, &path_item);
+done:
+ if(0);
+
+ }
+ else
+ {
+ gegl_node_set (node, spec->name, new_val, NULL);
+ }
+
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+ changed++;
+ tweaked_state (e->mrg);
+}
+
+
+static void remove_key (MrgEvent *e, void *data1, void *data2)
+{
+ gpointer *data = data1;
+ GeglEDL *edl = data[0];
+ GeglNode *node = data[1];
+ const char *pname = data[2];
+ int clip_frame_no = GPOINTER_TO_INT(data[3]);
+ char tmpbuf[1024];
+ sprintf (tmpbuf, "%s-anim", pname);
+ GQuark anim_quark = g_quark_from_string (tmpbuf);
+
+ fprintf (stderr, "remove key %p %s %i\n", node, pname, clip_frame_no);
+
+ if (g_object_get_qdata (G_OBJECT (node), anim_quark))
+ {
+ GeglPath *path = g_object_get_qdata (G_OBJECT (node), anim_quark);
+ int nodes = gegl_path_get_n_nodes (path);
+ int i;
+ int clip_frame_no=0;
+ GeglPathItem path_item;
+ gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+
+ for (i = 0; i < nodes; i ++)
+ {
+ gegl_path_get_node (path, i, &path_item);
+ if (fabs (path_item.point[0].x - clip_frame_no) < 0.5)
+ {
+ gegl_path_remove_node (path, i);
+ break;
+ }
+ }
+ }
+
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+ changed++;
+ tweaked_state (e->mrg);
+}
+
+static void drag_int_slider (MrgEvent *e, void *data1, void *data2)
+{
+ GeglParamSpecInt *gspec = (void*)data2;
+ GParamSpec *spec = (void*)data2;
+ GeglNode *node = (void*)data1;
+ char tmpbuf[1024];
+ sprintf (tmpbuf, "%s-rel", spec->name);
+ GQuark rel_quark = g_quark_from_string (tmpbuf);
+ double ui_min = gspec->ui_minimum;
+ double ui_max = gspec->ui_maximum;
+ if (g_object_get_qdata (G_OBJECT (node), rel_quark) && 1)
+ {
+ ui_min /= 1000.0;
+ ui_max /= 1000.0;
+ }
+
+ gint new_val = e->x * (ui_max - ui_min) + ui_min;
+ gegl_node_set (node, spec->name, new_val, NULL);
+
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+ changed++;
+ tweaked_state (e->mrg);
+}
+
+static void update_string (const char *new_string, void *user_data)
+{
+ if (snode && sprop)
+ gegl_node_set (snode, sprop, new_string, NULL);
+ ui_tweaks++;
+}
+
+float print_props (Mrg *mrg, GeglEDL *edl, GeglNode *node, float x, float y)
+{
+ unsigned int n_props;
+ GParamSpec ** props = gegl_operation_list_properties (gegl_node_get_operation (node),
+ &n_props);
+
+ for (int i = 0; i <n_props; i ++)
+ {
+ char *str = NULL;
+ mrg_set_xy (mrg, x, y);
+ GType type = props[i]->value_type;
+
+ char tmpbuf[1024];
+
+ sprintf (tmpbuf, "%s-rel", props[i]->name);
+ GQuark rel_quark = g_quark_from_string (tmpbuf);
+ sprintf (tmpbuf, "%s-anim", props[i]->name);
+ GQuark anim_quark = g_quark_from_string (tmpbuf);
+ mrg_set_xy (mrg, x, y);
+
+ if (g_type_is_a (type, G_TYPE_DOUBLE))
+ {
+ GeglParamSpecDouble *gspec = (void*)props[i];
+ double val;
+ gegl_node_get (node, props[i]->name, &val, NULL);
+ double width = mrg_width (mrg) - x - mrg_em(mrg) * 15;
+ double ui_min = gspec->ui_minimum;
+ double ui_max = gspec->ui_maximum;
+
+ if (g_object_get_qdata (G_OBJECT (node), rel_quark) && 1)
+ {
+ ui_min /= 1000.0;
+ ui_max /= 1000.0;
+ }
+
+ cairo_save (mrg_cr (mrg));
+ cairo_translate (mrg_cr (mrg), x + mrg_em(mrg) * 10, y - mrg_em(mrg));
+ cairo_rectangle (mrg_cr (mrg), 0, 0,
+ width,
+ mrg_em (mrg));
+ cairo_save (mrg_cr (mrg));
+ cairo_scale (mrg_cr (mrg), width, 1.0);
+
+ {
+ gpointer *data = g_new0 (gpointer, 3);
+ data[0]=edl;
+ data[1]=node;
+ data[2]=gspec;
+ mrg_listen_full (mrg, MRG_DRAG, drag_double_slider, data, gspec, (void*)g_free, NULL);
+ }
+
+ cairo_restore (mrg_cr (mrg));
+ cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,1.0);
+ cairo_stroke (mrg_cr (mrg));
+
+ cairo_rectangle (mrg_cr (mrg), 0,
+ 0, (val - ui_min) / (ui_max - ui_min) * width,
+ mrg_em (mrg) );
+ cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,1.0);
+ cairo_fill (mrg_cr (mrg));
+
+ cairo_restore (mrg_cr (mrg));
+
+ str = g_strdup_printf ("%s:%f", props[i]->name, val);
+ while (str[strlen(str)-1]=='0')
+ {
+ if (str[strlen(str)-2]=='.')
+ break;
+ str[strlen(str)-1]='\0';
+ }
+ mrg_printf (mrg, "%s", str);
+ }
+ else if (g_type_is_a (type, G_TYPE_INT))
+ {
+ GeglParamSpecDouble *gspec = (void*)props[i];
+ gint val;
+ gegl_node_get (node, props[i]->name, &val, NULL);
+ double width = mrg_width (mrg) - x - mrg_em(mrg) * 15;
+ double ui_min = gspec->ui_minimum;
+ double ui_max = gspec->ui_maximum;
+
+ if (g_object_get_qdata (G_OBJECT (node), rel_quark) && 1)
+ {
+ ui_min /= 1000.0;
+ ui_max /= 1000.0;
+ }
+
+ cairo_save (mrg_cr (mrg));
+ cairo_translate (mrg_cr (mrg), x + mrg_em(mrg) * 10, y - mrg_em(mrg));
+ cairo_rectangle (mrg_cr (mrg), 0, 0,
+ width,
+ mrg_em (mrg));
+ cairo_save (mrg_cr (mrg));
+ cairo_scale (mrg_cr (mrg), width, 1.0);
+
+ mrg_listen (mrg, MRG_DRAG, drag_int_slider, node, gspec);
+ cairo_restore (mrg_cr (mrg));
+ cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,1.0);
+ cairo_stroke (mrg_cr (mrg));
+
+ cairo_rectangle (mrg_cr (mrg), 0,
+ 0,
+ (val - ui_min) / (ui_max - ui_min) * width,
+ mrg_em (mrg) );
+ cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,1.0);
+ cairo_fill (mrg_cr (mrg));
+
+ cairo_restore (mrg_cr (mrg));
+
+ str = g_strdup_printf ("%s:%d", props[i]->name, val);
+ mrg_printf (mrg, "%s", str);
+ }
+ else if (g_type_is_a (type, G_TYPE_BOOLEAN))
+ {
+ gboolean val;
+ gegl_node_get (node, props[i]->name, &val, NULL);
+ str = g_strdup_printf ("%s:%s", props[i]->name, val?"yes":"no");
+ mrg_text_listen (mrg, MRG_CLICK, toggle_bool, node, (void*)g_intern_string(props[i]->name));
+ mrg_printf (mrg, "%s", str);
+ mrg_text_listen_done (mrg);
+ }
+ else if (g_type_is_a (type, G_TYPE_STRING))
+ {
+ char *val = NULL;
+ gegl_node_get (node, props[i]->name, &val, NULL);
+ mrg_printf (mrg, "%s: \"", props[i]->name);
+ if (snode && !strcmp (props[i]->name, sprop))
+ {
+ mrg_edit_start (mrg, update_string, edl);
+ }
+ else
+ mrg_text_listen (mrg, MRG_CLICK, edit_string, node, (void*)g_intern_string(props[i]->name));
+ mrg_printf (mrg, "%s", val);
+
+ if (snode && !strcmp (props[i]->name, sprop))
+ mrg_edit_end (mrg);
+ else
+ mrg_text_listen_done (mrg);
+ mrg_printf (mrg, "\"");
+ g_free (val);
+ str= g_strdup ("");
+ }
+ else
+ {
+ str = g_strdup_printf ("%s: [todo: handle this property type]", props[i]->name);
+ mrg_printf (mrg, "%s", str);
+ }
+
+ if (str)
+ {
+ g_free (str);
+ y -= mrg_em (mrg) * 1.2;
+ }
+
+ if (g_object_get_qdata (G_OBJECT (node), rel_quark))
+ mrg_printf (mrg, "rel");
+ if (g_object_get_qdata (G_OBJECT (node), anim_quark))
+ {
+ GeglPath *path = g_object_get_qdata (G_OBJECT (node), anim_quark);
+ int clip_frame_no;
+ gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+ mrg_printf (mrg, "{anim}");
+ {
+ GeglPathItem path_item;
+ int nodes = gegl_path_get_n_nodes (path);
+ int j;
+ for (j = 0 ; j < nodes; j ++)
+ {
+ gegl_path_get_node (path, j, &path_item);
+ if (fabs (path_item.point[0].x - clip_frame_no) < 0.5)
+ {
+ gpointer *data = g_new0 (gpointer, 4);
+ data[0]=edl;
+ data[1]=node;
+ data[2]=(void*)g_intern_string(props[i]->name);
+ data[3]=GINT_TO_POINTER(clip_frame_no);
+ mrg_text_listen_full (mrg, MRG_CLICK, remove_key, data, node, (void*)g_free, NULL);
+ mrg_printf (mrg, "(key)");
+ mrg_text_listen_done (mrg);
+ }
+ }
+ // if this prop has keyframe here - permit deleting it from
+ // here
+ }
+
+ cairo_t *cr = mrg_cr (mrg);
+
+ cairo_save (cr);
+
+ cairo_scale (cr, 1.0/edl->scale, 1);
+ cairo_translate (cr, (edl->active_clip?edl->active_clip->abs_start:0)-edl->t0,
+ mrg_height (mrg) * SPLIT_VER);
+
+ {
+ int j;
+ gdouble y = 0.0;
+ gdouble miny = 100000.0;
+ gdouble maxy = -100000.0;
+
+ // todo: draw markers for zero, min and max, with labels
+ // do all curves in one scaled space? - will break for 2 or more magnitudes diffs
+
+ for (j = -10; j < clip_get_frames (edl->active_clip) + 10; j ++)
+ {
+ gegl_path_calc_y_for_x (path, j, &y);
+ if (y < miny) miny = y;
+ if (y > maxy) maxy = y;
+ }
+
+ cairo_new_path (cr);
+ gegl_path_calc_y_for_x (path, 0, &y);
+ y = VID_HEIGHT * 0.9 - ((y - miny) / (maxy - miny)) * VID_HEIGHT * 0.8;
+ cairo_move_to (cr, 0, y);
+ for (j = -10; j < clip_get_frames (edl->active_clip) + 10; j ++)
+ {
+ gegl_path_calc_y_for_x (path, j, &y);
+ y = VID_HEIGHT * 0.9 - ((y - miny) / (maxy - miny)) * VID_HEIGHT * 0.8;
+ cairo_line_to (cr, j, y);
+ }
+
+
+ cairo_restore (cr);
+ cairo_set_line_width (cr, 2.0);
+ cairo_set_source_rgba (cr, 1.0, 0.5, 0.5, 255);
+ cairo_stroke (cr);
+ cairo_save (cr);
+
+ cairo_translate (cr, ((edl->active_clip?edl->active_clip->abs_start:0)-edl->t0) * 1.0 / edl->scale,
+ mrg_height (mrg) * SPLIT_VER);
+
+ cairo_set_source_rgba (cr, 1.0, 0.5, 0.5, 255);
+ int nodes = gegl_path_get_n_nodes (path);
+ GeglPathItem path_item;
+
+ for (j = 0; j < nodes; j ++)
+ {
+ gegl_path_get_node (path, j, &path_item);
+
+ cairo_arc (cr, path_item.point[0].x * 1.0/edl->scale, -0.5 * mrg_em (mrg),
+ mrg_em (mrg) * 0.5, 0.0, 3.1415*2);
+ mrg_listen (mrg, MRG_PRESS, jump_to_pos, edl, GINT_TO_POINTER( (int)(path_item.point[0].x +
edl->active_clip->abs_start)));
+ cairo_fill (cr);
+ }
+ cairo_restore (cr);
+ }
+ }
+ if (g_object_get_qdata (G_OBJECT (node), g_quark_from_string (props[i]->name)))
+ mrg_printf (mrg, "{???}");
+ }
+
+ return y;
+}
+
+static Clip *ui_clip = NULL;
+static GeglNode *source_start;
+static GeglNode *source_end;
+static GeglNode *filter_end;
+
+
+static void select_node (MrgEvent *e, void *data1, void *data2)
+{
+ if (selected_node == data1)
+ selected_node = NULL;
+ else
+ selected_node = data1;
+ snode = NULL;
+ sprop = NULL;
+
+ mrg_event_stop_propagate (e);
+ mrg_queue_draw (e->mrg, NULL);
+}
+
+float print_nodes (Mrg *mrg, GeglEDL *edl, GeglNode *node, float x, float y)
+{
+ while (node)
+ {
+
+ if ((node != source_start) &&
+ (node != source_end) &&
+ (node != filter_start) &&
+ (node != filter_end))
+ {
+ if (node == selected_node)
+ y = print_props (mrg, edl, node, x + mrg_em(mrg) * 0.5, y);
+
+ mrg_set_xy (mrg, x, y);
+ mrg_text_listen (mrg, MRG_CLICK, select_node, node, NULL);
+ mrg_printf (mrg, "%s", gegl_node_get_operation (node));
+ mrg_text_listen_done (mrg);
+ y -= mrg_em (mrg) * 1.5;
+ }
+
+ GeglNode **nodes = NULL;
+ const gchar **pads = NULL;
+
+ int count = gegl_node_get_consumers (node, "output", &nodes, &pads);
+ if (count)
+ {
+ node = nodes[0];
+ if (strcmp (pads[0], "input"))
+ node = NULL;
+ }
+ else
+ node = NULL;
+ g_free (nodes);
+ g_free (pads);
+ //if (node && node == clip->nop_crop)
+ // node = NULL;
+ if (node)
+ {
+ GeglNode *iter = gegl_node_get_producer (node, "aux", NULL);
+ if (iter)
+ {
+ GeglNode *next;
+ do {
+ next = gegl_node_get_producer (iter, "input", NULL);
+ if (next) iter = next;
+ } while(next);
+
+ y = print_nodes (mrg, edl, iter, x + mrg_em (mrg) * 2, y);
+ }
+ }
+ }
+ return y;
+}
+
+void update_ui_clip (Clip *clip, int clip_frame_no)
+{
+ GError *error = NULL;
+ if (ui_clip == NULL ||
+ ui_clip != clip)
+ {
+ selected_node = NULL;
+ snode = NULL;
+ if (source_start)
+ {
+ remove_in_betweens (source_start, source_end);
+ g_object_unref (source_start);
+ source_start = NULL;
+ g_object_unref (source_end);
+ source_end = NULL;
+ }
+
+ if (filter_start)
+ {
+ remove_in_betweens (filter_start, filter_end);
+ g_object_unref (filter_start);
+ filter_start = NULL;
+ g_object_unref (filter_end);
+ filter_end = NULL;
+ }
+
+ source_start = gegl_node_new ();
+ source_end = gegl_node_new ();
+
+ gegl_node_set (source_start, "operation", "gegl:nop", NULL);
+ gegl_node_set (source_end, "operation", "gegl:nop", NULL);
+ gegl_node_link_many (source_start, source_end, NULL);
+
+ if (clip->is_chain)
+ gegl_create_chain (clip->path, source_start, source_end,
+ clip->edl->frame_no - clip->abs_start,
+ 1.0, NULL, &error);
+
+ filter_start = gegl_node_new ();
+ filter_end = gegl_node_new ();
+
+ gegl_node_set (filter_start, "operation", "gegl:nop", NULL);
+ gegl_node_set (filter_end, "operation", "gegl:nop", NULL);
+
+ gegl_node_link_many (filter_start, filter_end, NULL);
+ if (clip->filter_graph)
+ {
+ gegl_create_chain (clip->filter_graph, filter_start, filter_end,
+ clip->edl->frame_no - clip->abs_start,
+ 1.0, NULL, &error);
+ }
+ ui_clip = clip;
+ }
+
+ if (selected_node)
+ {
+ unsigned int n_props;
+
+ if (ui_tweaks)
+ {
+ char *serialized_filter = NULL;
+ char *serialized_source = NULL;
+ serialized_filter = gegl_serialize (filter_start, filter_end,
+ NULL,GEGL_SERIALIZE_TRIM_DEFAULTS|GEGL_SERIALIZE_VERSION);
+ serialized_source = gegl_serialize (source_start, source_end,
+ NULL,GEGL_SERIALIZE_TRIM_DEFAULTS|GEGL_SERIALIZE_VERSION);
+
+ if (clip->filter_graph)
+ {
+ gchar *old = clip->filter_graph;
+
+ if (g_str_has_suffix (serialized_filter, "gegl:nop opi=0:0"))
+ { /* XXX: ugly hack - we remove the common bit we do not want */
+ serialized_filter[strlen(serialized_filter)-strlen("gegl:nop opi=0:0")]='\0';
+ }
+ clip->filter_graph = serialized_filter;
+ g_free (old);
+ fprintf (stderr, "{%s}\n", clip->filter_graph);
+ }
+ else
+ g_free (serialized_filter);
+
+ if (clip->is_chain)
+ {
+ if (g_str_has_suffix (serialized_source, "gegl:nop opi=0:0"))
+ { /* XXX: ugly hack - we remove the common bit we do not want */
+ serialized_source[strlen(serialized_source)-strlen("gegl:nop opi=0:0")]='\0';
+ }
+ clip_set_path (clip, serialized_source);
+ }
+ else
+ g_free (serialized_source);
+
+ ui_tweaks = 0;
+ changed ++;
+
+ gedl_cache_invalid (clip->edl);
+ }
+
+ GParamSpec ** props = gegl_operation_list_properties (gegl_node_get_operation (selected_node), &n_props);
+
+ for (int i = 0; i <n_props; i ++)
+ {
+ char tmpbuf[1024];
+ sprintf (tmpbuf, "%s-anim", props[i]->name);
+ GQuark anim_quark = g_quark_from_string (tmpbuf);
+ // this only deals with double for now
+ if (g_object_get_qdata (G_OBJECT (selected_node), anim_quark))
+ {
+ GeglPath *path = g_object_get_qdata (G_OBJECT (selected_node), anim_quark);
+ gdouble val = 0.0;
+ gegl_path_calc_y_for_x (path, clip_frame_no * 1.0, &val);
+
+ gegl_node_set (selected_node, props[i]->name, val, NULL);
+ }
+ }
+ }
+}
+
+/* XXX: add some constarint? like having input, or output pad or both - or
+ * being a specific subclass - as well as sort, so that best (prefix_) match
+ * comes first
+ */
+char **gedl_get_completions (const char *filter_query)
+{
+ gchar **completions = NULL;
+ gchar **operations = gegl_list_operations (NULL);
+ gchar *alloc = NULL;
+ int matches = 0;
+ int memlen = sizeof (gpointer);
+
+ // score matches, sort them - and default to best
+
+ for (int i = 0; operations[i]; i++)
+ {
+ if (strstr (operations[i], filter_query))
+ {
+ matches ++;
+ memlen += strlen (operations[i]) + 1 + sizeof (gpointer);
+ }
+ }
+
+ completions = g_malloc0 (memlen);
+ alloc = ((void*)completions) + (matches + 1) * sizeof (gpointer);
+
+ int match = 0;
+ for (int i = 0; operations[i]; i++)
+ {
+ if (strstr (operations[i], filter_query))
+ {
+ completions[match] = alloc;
+ strcpy (alloc, operations[i]);
+ alloc += strlen (alloc) + 1;
+ match++;
+ }
+ }
+ g_free (operations);
+
+ if (match == 0)
+ {
+ return NULL;
+ g_free (completions);
+ }
+ return completions;
+}
+
+static void complete_filter_query_edit (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ gchar **completions = gedl_get_completions (filter_query);
+ if (!selected_node)
+ selected_node = filter_start;
+
+ if (!completions)
+ return;
+
+ GeglNode *new = NULL;
+ new = gegl_node_new_child (edl->gegl, "operation", completions[0], NULL);
+ if (filter_query)
+ g_free (filter_query);
+ filter_query = NULL;
+ insert_node (selected_node, new);
+ selected_node = new;
+
+
+ g_free (completions);
+ ui_tweaks++;
+ gedl_cache_invalid (edl);
+ mrg_queue_draw (e->mrg, NULL);
+ mrg_event_stop_propagate (e);
+}
+
+static void end_filter_query_edit (MrgEvent *e, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ if (filter_query)
+ g_free (filter_query);
+ filter_query = NULL;
+ mrg_event_stop_propagate (e);
+
+ ui_tweaks++;
+ gedl_cache_invalid (edl);
+ mrg_queue_draw (e->mrg, NULL);
+}
+
+static void update_filter_query (const char *new_string, void *user_data)
+{
+ if (filter_query)
+ g_free (filter_query);
+ filter_query = g_strdup (new_string);
+}
+
+
+void gedl_draw (Mrg *mrg,
+ GeglEDL *edl,
+ double x0,
+ double y,
+ double fpx,
+ double t0)
+{
+
+ GList *l;
+ cairo_t *cr = mrg_cr (mrg);
+ double t;
+ int duration = gedl_get_duration (edl); // causes update of abs_start
+
+ VID_HEIGHT = mrg_height (mrg) * (1.0 - SPLIT_VER) * 0.8;
+ int scroll_height = mrg_height (mrg) * (1.0 - SPLIT_VER) * 0.2;
+ t = 0;
+ int clip_frame_no;
+
+ if (duration == 0)
+ return;
+
+ float y2 = y - mrg_em (mrg) * 1.5;
+
+ edl->active_clip = gedl_get_clip (edl, edl->frame_no, &clip_frame_no);
+
+ if (edl->active_clip) // && edl->active_clip->filter_graph)
+ {
+ Clip *clip = edl->active_clip;
+
+ update_ui_clip (clip, clip_frame_no);
+
+ mrg_set_style (mrg, "font-size: 3%; background-color: #0008; color: #fff");
+
+ if (clip->is_chain)
+ {
+ GeglNode *iter = source_end;
+ while (gegl_node_get_producer (iter, "input", NULL))
+ {
+ iter = gegl_node_get_producer (iter, "input", NULL);
+ }
+ y2 = print_nodes (mrg, edl, iter, mrg_em (mrg), y2);
+ }
+ else
+ {
+ mrg_set_xy (mrg, mrg_em(mrg) * 1, y2);
+ mrg_printf (mrg, "%s", clip->path);
+ y2 -= mrg_em (mrg) * 1.5;
+ }
+ y2 = print_nodes (mrg, edl, filter_start, mrg_em (mrg), y2);
+
+ if (filter_query)
+ {
+ mrg_set_xy (mrg, mrg_em(mrg) * 1, y2);
+ mrg_edit_start (mrg, update_filter_query, edl);
+ mrg_printf (mrg, "%s", filter_query);
+ mrg_edit_end (mrg);
+
+ mrg_add_binding (mrg, "escape", NULL, "end edit", end_filter_query_edit, edl);
+ mrg_add_binding (mrg, "return", NULL, "end edit", complete_filter_query_edit, edl);
+
+ // XXX: print completions that are clickable?
+ {
+ gchar **operations = gedl_get_completions (filter_query);
+ mrg_start (mrg, NULL, NULL);
+ mrg_set_xy (mrg, mrg_em(mrg) * 1, mrg_height (mrg) * SPLIT_VER + mrg_em (mrg));
+ gint matches=0;
+ for (int i = 0; operations[i] && matches < 40; i++)
+ {
+ mrg_printf (mrg, "%s", operations[i]);
+ mrg_printf (mrg, " ");
+ matches ++;
+ }
+ mrg_end(mrg);
+ g_free (operations);
+ }
+ }
+ }
+
+ cairo_set_source_rgba (cr, 1, 1,1, 1);
+
+ if (edl->playing)
+ {
+ scroll_to_fit (edl, mrg);
+ t0 = edl->t0;
+ }
+
+ cairo_save (cr);
+ {
+ cairo_scale (cr, 1.0 / duration * mrg_width (mrg), 1.0);
+ }
+
+ y += VID_HEIGHT;
+
+ cairo_rectangle (cr, t0, y, mrg_width(mrg)*fpx, scroll_height);
+ mrg_listen (mrg, MRG_DRAG, drag_t0, edl, edl);
+ cairo_set_source_rgba (cr, 1, 1, 0.5, 0.25);
+ if (edl->playing)
+ cairo_stroke (cr);
+ else
+ cairo_fill (cr);
+
+ cairo_rectangle (cr, t0 + mrg_width(mrg)*fpx*0.9, y, mrg_width(mrg)*fpx * 0.1, scroll_height);
+ mrg_listen (mrg, MRG_DRAG, drag_fpx, edl, edl);
+ cairo_fill (cr);
+
+ /* we could cull drawing already here, we let cairo do it for now, */
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ int frames = clip_get_frames (clip);
+ cairo_rectangle (cr, t, y, frames, scroll_height);
+ cairo_stroke (cr);
+ t += frames;
+ }
+
+ int start = 0, end = 0;
+ gedl_get_range (edl, &start, &end);
+ cairo_rectangle (cr, start, y, end - start, scroll_height);
+ cairo_set_source_rgba (cr, 0, 0.11, 0.0, 0.5);
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+ cairo_stroke (cr);
+
+ {
+ double frame = edl->frame_no;
+ if (fpx < 1.0)
+ cairo_rectangle (cr, frame, y-5, 1.0, 5 + scroll_height);
+ else
+ cairo_rectangle (cr, frame, y-5, fpx, 5 + scroll_height);
+ cairo_set_source_rgba (cr,1,0,0,0.85);
+ cairo_fill (cr);
+ }
+
+ cairo_restore (cr);
+ y -= VID_HEIGHT;
+ t = 0;
+
+ cairo_move_to (cr, x0 + PAD_DIM, y + VID_HEIGHT + PAD_DIM * 3);
+
+ cairo_save (cr);
+ cairo_translate (cr, x0, 0);
+ cairo_scale (cr, 1.0/fpx, 1);
+ cairo_translate (cr, -t0, 0);
+
+ gedl_get_selection (edl, &start, &end);
+ cairo_rectangle (cr, start + 0.5, y - PAD_DIM, end - start, VID_HEIGHT + PAD_DIM * 2);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.75);
+ cairo_fill (cr);
+
+ cairo_rectangle (cr, t0, y, mrg_width(mrg)*fpx, VID_HEIGHT);
+ mrg_listen (mrg, MRG_DROP, drag_dropped, edl, edl);
+ cairo_new_path (cr);
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ int frames = clip_get_frames (clip);
+ if (clip->is_meta)
+ {
+ double tx = t, ty = y;
+ cairo_save (cr);
+ cairo_user_to_device (cr, &tx, &ty);
+ cairo_identity_matrix (cr);
+ mrg_set_xy (mrg, tx, y + VID_HEIGHT);
+ mrg_printf (mrg, "%s", clip->filter_graph); // only used for annotations for now - could script vars
+ cairo_restore (cr);
+ }
+ else
+ {
+ Clip *next = clip_get_next (clip);
+ render_clip (mrg, edl, clip->path, clip->start, frames, t, y, clip->fade, next?next->fade:0);
+ /* .. check if we are having anim things going on.. if so - print it here */
+ }
+
+ if (clip == edl->active_clip)
+ cairo_set_source_rgba (cr, 1, 1, 0.5, 1.0);
+ else
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+
+ mrg_listen (mrg, MRG_PRESS, clicked_clip, clip, edl);
+ mrg_listen (mrg, MRG_DRAG, drag_clip, clip, edl);
+ mrg_listen (mrg, MRG_RELEASE, released_clip, clip, edl);
+ cairo_stroke (cr);
+
+ t += frames;
+ }
+
+ if (!edl->playing){
+ static gint bitlen = 0;
+ static guchar *bitmap;
+ static long bitticks = 0;
+ int i;
+ int state = -1;
+ int length = 0;
+
+ if (bitlen && ( babl_ticks() - bitticks > 1000 * 1000 * 2))
+ {
+ /* update cache bitmap if it is more than 2s old */
+ bitlen = 0;
+ g_free (bitmap);
+ bitmap = NULL;
+ }
+
+ if (bitlen == 0)
+ {
+ bitmap = gedl_get_cache_bitmap (edl, &bitlen);
+ bitticks = babl_ticks ();
+ }
+ cairo_set_source_rgba (cr, 0.3, 1, 0.3, 1.0);
+ for (i = 0; i < bitlen * 8; i++)
+ {
+ if (bitmap[i / 8] & (1<< (i%8)))
+ {
+ if (state == 1)
+ {
+ length++;
+ }
+ else
+ {
+ length = 0;
+ state = 1;
+ }
+ }
+ else
+ {
+ if (state == 0)
+ {
+ length++;
+ }
+ else
+ {
+ cairo_rectangle (cr, i-length, y, length + 1, VID_HEIGHT * 0.05);
+ length = 0;
+ state = 0;
+ }
+ }
+ }
+ cairo_fill (cr);
+ }
+
+ double frame = edl->frame_no;
+ if (fpx < 1.0)
+ cairo_rectangle (cr, frame, y-PAD_DIM, 1.0, VID_HEIGHT + PAD_DIM * 2);
+ else
+ cairo_rectangle (cr, frame, y-PAD_DIM, fpx, VID_HEIGHT + PAD_DIM * 2);
+ cairo_set_source_rgba (cr,1,0,0,1);
+ cairo_fill (cr);
+ cairo_restore (cr);
+
+ cairo_rectangle (cr, 0, y - PAD_DIM, mrg_width (mrg), VID_HEIGHT + PAD_DIM * 4);
+ mrg_listen (mrg, MRG_SCROLL, zoom_timeline, edl, NULL);
+ cairo_new_path (cr);
+}
+
+static const char *css =
+" document { background: black; }"
+"";
+
+#if 0
+static void edit_filter_graph (MrgEvent *event, void *data1, void *data2)
+{ //XXX
+ GeglEDL *edl = data1;
+
+ //edl->active_source = NULL;
+ edl->filter_edited = !edl->filter_edited;
+ mrg_queue_draw (event->mrg, NULL);
+}
+#endif
+
+#if 0
+static void update_filter (const char *new_string, void *user_data)
+{
+ GeglEDL *edl = user_data;
+ if (edl->active_clip->filter_graph)
+ g_free (edl->active_clip->filter_graph);
+ edl->active_clip->filter_graph = g_strdup (new_string);
+ gedl_cache_invalid (edl);
+ mrg_queue_draw (edl->mrg, NULL);
+}
+#endif
+
+static void toggle_ui_mode (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ edl->ui_mode ++;
+ if (edl->ui_mode > GEDL_LAST_UI_MODE)
+ edl->ui_mode = 0;
+
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ changed++;
+}
+
+void help_ui (Mrg *mrg, GeglEDL *edl)
+{
+ if (help)
+ {
+ MrgBinding * bindings = mrg_get_bindings (mrg);
+ int i;
+
+ //cairo_set_source_rgba (mrg_cr (mrg), 0, 0, 0, 0.85);
+ //cairo_paint (mrg_cr(mrg));
+ mrg_set_font_size (mrg, mrg_height (mrg) / 20.0);
+ mrg_set_style (mrg, "color: white;background: transparent; text-stroke: 4.5px #000");
+ mrg_set_edge_right (mrg, mrg_width (mrg) - mrg_em (mrg) *2);
+ mrg_set_edge_left (mrg, mrg_em (mrg));
+ mrg_set_xy (mrg, mrg_em (mrg), mrg_em (mrg) * 2);
+
+
+ for (i = 0; bindings[i].cb; i++)
+ {
+ if (bindings[i].label)
+ mrg_printf_xml (mrg, "<div style='display:inline-block; padding-right: 1em;'><b>%s</b> %s</div>
", bindings[i].nick, bindings[i].label);
+ }
+ }
+ else
+ {
+ mrg_set_xy (mrg, mrg_width(mrg) - 10 * mrg_em (mrg), mrg_height(mrg) * SPLIT_VER);
+ mrg_printf (mrg, "F1 toggle help");
+ }
+}
+
+gchar *message = NULL;
+
+extern int cache_hits;
+extern int cache_misses;
+
+long babl_ticks (void);
+
+void gedl_ui (Mrg *mrg, void *data)
+{
+ State *o = data;
+ GeglEDL *edl = o->edl;
+
+ int long start_time = babl_ticks ();
+
+ mrg_stylesheet_add (mrg, css, NULL, 0, NULL);
+ mrg_set_style (mrg, "font-size: 11px");
+#if 0
+ cairo_set_source_rgb (mrg_cr (mrg), 0,0,0);
+ cairo_paint (mrg_cr (mrg));
+#endif
+
+ if (message)
+ {
+ mrg_set_style (mrg, "font-size: 0.1yh");
+ mrg_printf (mrg, "%s", message);
+ return;
+ }
+
+ /* XXX: sync ui changes here, rather than deferred */
+
+ g_mutex_lock (&edl->buffer_copy_mutex);
+ if (edl->buffer_copy_temp)
+ g_object_unref (edl->buffer_copy_temp);
+ edl->buffer_copy_temp = edl->buffer_copy;
+ g_object_ref (edl->buffer_copy);
+ gegl_node_set (edl->cached_result, "buffer", edl->buffer_copy_temp, NULL);
+ g_mutex_unlock (&edl->buffer_copy_mutex);
+
+ switch (edl->ui_mode)
+ {
+ case GEDL_UI_MODE_FULL:
+ case GEDL_UI_MODE_TIMELINE:
+ case GEDL_UI_MODE_NONE:
+ mrg_gegl_blit (mrg, (int)(mrg_width (mrg) * 0.0), 0,
+ (int)(mrg_width (mrg) * 1.0),
+ mrg_height (mrg),// * SPLIT_VER,
+ o->edl->cached_result,
+ 0, 0,
+ /* opacity */ 1.0 //edl->frame_no == done_frame?1.0:0.5
+ ,edl);
+ break;
+ case GEDL_UI_MODE_PART:
+ mrg_gegl_blit (mrg, (int)(mrg_width (mrg) * 0.2), 0,
+ (int)(mrg_width (mrg) * 0.8),
+ mrg_height (mrg) * SPLIT_VER,
+ o->edl->cached_result,
+ 0, 0,
+ /* opacity */ 1.0 //edl->frame_no == done_frame?1.0:0.5
+ ,edl);
+ break;
+ }
+
+
+ switch (edl->ui_mode)
+ {
+ case GEDL_UI_MODE_FULL:
+ case GEDL_UI_MODE_TIMELINE:
+ case GEDL_UI_MODE_PART:
+ gedl_draw (mrg, edl, 0, mrg_height (mrg) * SPLIT_VER, edl->scale, edl->t0);
+
+
+
+
+ break;
+ case GEDL_UI_MODE_NONE:
+ break;
+ break;
+ }
+
+ if(0)fprintf (stderr, "%f\n", 1.0 / ((babl_ticks () - start_time)/1000.0/ 1000.0));
+ if (edl->ui_mode != GEDL_UI_MODE_NONE)
+ {
+
+ mrg_set_xy (mrg, mrg_em (mrg), mrg_height(mrg) * SPLIT_VER);
+ mrg_set_style (mrg, "color: white;background: transparent; text-stroke: 1.5px #000");
+ mrg_set_edge_right (mrg, mrg_width (mrg));// * 0.25 - 8);
+#if 0
+ {
+ GeglRectangle rect;
+ rect = gegl_node_get_bounding_box (o->edl->cached_result);
+
+ mrg_printf (mrg, "%ix%i\n", rect.width, rect.height);
+ }
+#endif
+
+#if 0
+ mrg_printf (mrg, "cache hit: %2.2f%% of %i\n", 100.0 * cache_hits / (cache_hits + cache_misses),
cache_hits + cache_misses);
+#endif
+
+#if 0
+ if (done_frame != edl->frame_no)
+ mrg_printf (mrg, "frame %i (%i shown)",edl->frame_no, done_frame);
+ else
+#endif
+ mrg_printf (mrg, " %i ", edl->frame_no);
+
+#if 0
+ if (edl->active_source)
+ {
+ char *basename = g_path_get_basename (edl->active_source->path);
+ mrg_printf (mrg, "%i\n", edl->source_frame_no);
+ mrg_printf (mrg, "%s\n", basename);
+ }
+#endif
+
+ //mrg_printf (mrg, "%i %i %i %i %i\n", edl->frame, edl->frame_no, edl->source_frame_no, rendering_frame,
done_frame);
+
+ if (!renderer_done (edl))
+ mrg_printf (mrg, "... ");
+
+ }
+
+ if (snode)
+ {
+ mrg_add_binding (mrg, "escape", NULL, "end edit", end_edit, edl);
+ }
+
+ if (!edl->clip_query_edited &&
+ !edl->filter_edited &&
+ !filter_query &&
+ !snode)
+ {
+ mrg_add_binding (mrg, "F1", NULL, "toggle help", toggle_help, edl);
+ mrg_add_binding (mrg, "q", NULL, "quit", (void*)do_quit, mrg);
+
+ if (edl->playing)
+ {
+ mrg_add_binding (mrg, "space", NULL, "pause", renderer_toggle_playing, edl);
+ if (edl->active_clip && edl->frame_no != edl->active_clip->abs_start)
+ mrg_add_binding (mrg, "v", NULL, "split clip", split_clip, edl);
+ }
+ else
+ {
+ mrg_add_binding (mrg, "space", NULL, "play", renderer_toggle_playing, edl);
+
+ mrg_add_binding (mrg, "tab", NULL, "cycle ui amount", toggle_ui_mode, edl);
+ mrg_add_binding (mrg, "e", NULL, "zoom timeline to fit", zoom_fit, edl);
+ mrg_add_binding (mrg, "1", NULL, "zoom timeline 1px = 1 frame", zoom_1, edl);
+ if (edl->use_proxies)
+ mrg_add_binding (mrg, "p", NULL, "don't use proxies", toggle_use_proxies, edl);
+ else
+ mrg_add_binding (mrg, "p", NULL, "use proxies", toggle_use_proxies, edl);
+
+ mrg_add_binding (mrg, "s", NULL, "save", save, edl);
+ mrg_add_binding (mrg, "a", NULL, "select all", select_all, edl);
+
+ mrg_add_binding (mrg, "left/right", NULL, "step frame", step_frame, edl);
+ mrg_add_binding (mrg, "right", NULL, NULL, step_frame, edl);
+ mrg_add_binding (mrg, "left", NULL, NULL, step_frame_back, edl);
+ mrg_add_binding (mrg, "l", NULL, NULL, step_frame, edl);
+ mrg_add_binding (mrg, "h", NULL, NULL, step_frame_back, edl);
+
+ mrg_add_binding (mrg, "up/down", NULL, "previous/next cut", prev_cut, edl);
+ mrg_add_binding (mrg, "up", NULL, NULL, prev_cut, edl);
+ mrg_add_binding (mrg, "k", NULL, NULL, prev_cut, edl);
+ mrg_add_binding (mrg, "down", NULL, NULL, next_cut, edl);
+ mrg_add_binding (mrg, "j", NULL, NULL, next_cut, edl);
+
+ mrg_add_binding (mrg, "shift-left/right", NULL, "extend selection", extend_selection_to_the_right,
edl);
+ mrg_add_binding (mrg, "shift-right", NULL, NULL, extend_selection_to_the_right, edl);
+ mrg_add_binding (mrg, "shift-left", NULL, NULL, extend_selection_to_the_left, edl);
+ mrg_add_binding (mrg, "shift-up", NULL, NULL, extend_selection_to_previous_cut, edl);
+ mrg_add_binding (mrg, "shift-down", NULL, NULL, extend_selection_to_next_cut, edl);
+ mrg_add_binding (mrg, "L", NULL, NULL, extend_selection_to_the_right, edl);
+ mrg_add_binding (mrg, "H", NULL, NULL, extend_selection_to_the_left, edl);
+ mrg_add_binding (mrg, "K", NULL, NULL, extend_selection_to_previous_cut, edl);
+ mrg_add_binding (mrg, "J", NULL, NULL, extend_selection_to_next_cut, edl);
+
+ if (empty_selection (edl))
+ {
+ mrg_add_binding (mrg, "x", NULL, "remove clip", remove_clip, edl);
+ mrg_add_binding (mrg, "d", NULL, "duplicate clip", duplicate_clip, edl);
+
+ if (edl->active_clip)
+ {
+ if (edl->frame_no == edl->active_clip->abs_start)
+ {
+ GList *iter = g_list_find (edl->clips, edl->active_clip);
+ Clip *clip2 = NULL;
+ if (iter) iter = iter->prev;
+ if (iter) clip2 = iter->data;
+
+
+ if (are_mergable (clip2, edl->active_clip, 0))
+ mrg_add_binding (mrg, "v", NULL, "merge clip", merge_clip, edl);
+ }
+ else
+ {
+ mrg_add_binding (mrg, "v", NULL, "split clip", split_clip, edl);
+ }
+ mrg_add_binding (mrg, "f", NULL, "toggle fade", toggle_fade, edl);
+ }
+
+ }
+ else
+ {
+ mrg_add_binding (mrg, "x", NULL, "cut selection", remove_clip, edl);
+ mrg_add_binding (mrg, "c", NULL, "copy selection", remove_clip, edl);
+ mrg_add_binding (mrg, "r", NULL, "set playback range", set_range, edl);
+ }
+
+ if (edl->active_clip)
+ {
+ mrg_add_binding (mrg, "i", NULL, "insert filter", insert_filter, edl);
+
+ if (edl->frame_no == edl->active_clip->abs_start)
+ {
+
+ if (empty_selection (edl))
+ {
+ mrg_add_binding (mrg, "control-left/right", NULL, "adjust in", clip_start_inc, edl);
+ mrg_add_binding (mrg, "control-right", NULL, NULL, clip_start_inc, edl);
+ mrg_add_binding (mrg, "control-left", NULL, NULL, clip_start_dec, edl);
+ mrg_add_binding (mrg, "control-h", NULL, NULL, clip_start_inc, edl);
+ mrg_add_binding (mrg, "control-l", NULL, NULL, clip_start_dec, edl);
+ mrg_add_binding (mrg, "control-up/down", NULL, "shuffle clip backward/forward", shuffle_back,
edl);
+ mrg_add_binding (mrg, "control-up", NULL, NULL, shuffle_back, edl);
+ mrg_add_binding (mrg, "control-down", NULL, NULL, shuffle_forward, edl);
+ mrg_add_binding (mrg, "control-k", NULL, NULL, shuffle_back, edl);
+ mrg_add_binding (mrg, "control-j", NULL, NULL, shuffle_forward, edl);
+ }
+ }
+ else
+ {
+ if (empty_selection (edl))
+ {
+ if (edl->frame_no == edl->active_clip->abs_start + clip_get_frames (edl->active_clip)-1)
+ {
+ mrg_add_binding (mrg, "control-left/right", NULL, "adjust out", clip_end_inc, edl);
+ mrg_add_binding (mrg, "control-right", NULL, NULL, clip_end_inc, edl);
+ mrg_add_binding (mrg, "control-left", NULL, NULL, clip_end_dec, edl);
+ }
+ else
+ {
+ mrg_add_binding (mrg, "control-left/right", NULL, "slide clip backward/forward", slide_back,
edl);
+ mrg_add_binding (mrg, "control-left", NULL, NULL, slide_back, edl);
+ mrg_add_binding (mrg, "control-right", NULL, NULL, slide_forward, edl);
+
+
+ mrg_add_binding (mrg, "control-up/down", NULL, "slide cut window", clip_start_end_inc, edl);
+ mrg_add_binding (mrg, "control-up", NULL, NULL, clip_start_end_inc, edl);
+ mrg_add_binding (mrg, "control-down", NULL, NULL, clip_start_end_dec, edl);
+ }
+ }
+ else {
+ Clip *start_clip = gedl_get_clip (edl, edl->selection_start, NULL);
+ Clip *end_clip = gedl_get_clip (edl, edl->selection_end, NULL);
+ GList *start_iter = g_list_find (edl->clips, start_clip);
+ GList *end_iter = g_list_find (edl->clips, end_clip);
+
+ if (start_iter &&
+ (start_iter->next == end_iter ||
+ start_iter->prev == end_iter))
+ {
+ mrg_add_binding (mrg, "control-left/right", NULL, "move cut", clip_end_start_inc, edl);
+ mrg_add_binding (mrg, "control-right", NULL, NULL, clip_end_start_inc, edl);
+ mrg_add_binding (mrg, "control-left", NULL, NULL, clip_end_start_dec, edl);
+ }
+ }
+ }
+ }
+ }
+ }
+
+#if 0
+ if (edl->active_source)
+ mrg_add_binding (mrg, "return", NULL, NULL, toggle_edit_source, edl);
+ else //if (edl->filter_edited)
+ mrg_add_binding (mrg, "return", NULL, NULL, edit_filter_graph, edl);
+#endif
+
+ switch (edl->ui_mode)
+ {
+ case GEDL_UI_MODE_FULL:
+ case GEDL_UI_MODE_TIMELINE:
+ case GEDL_UI_MODE_PART:
+ default:
+ help_ui (mrg, edl);
+ break;
+ case GEDL_UI_MODE_NONE:
+ break;
+ }
+
+ if(0)fprintf (stderr, "b:%f\n", 1.0 / ((babl_ticks () - start_time)/1000.0/ 1000.0));
+}
+
+gboolean cache_renderer_iteration (Mrg *mrg, gpointer data)
+{
+ GeglEDL *edl = data;
+ if (!edl->playing)
+ {
+ int i;
+ int render_slaves = g_get_num_processors ();
+ killpg(0, SIGUSR2); // this will cause previous set of renderers to quite after current frame
+ for (i = 0; i < render_slaves; i ++)
+ {
+ char *cmd = g_strdup_printf ("%s %s cache %i %i&",
+ gedl_binary_path,
+ edl->path, i, render_slaves);
+ save_edl (edl);
+ system (cmd);
+ g_free (cmd);
+ }
+ }
+ return TRUE;
+}
+
+int gedl_ui_main (GeglEDL *edl);
+int gedl_ui_main (GeglEDL *edl)
+{
+ Mrg *mrg = mrg_new (800, 600, NULL);
+ //Mrg *mrg = mrg_new (-1, -1, NULL);
+ State o = {NULL,};
+ o.mrg = mrg;
+ o.edl = edl;
+
+ edl->mrg = mrg;
+
+ edl->cache_flags = CACHE_TRY_ALL;// | CACHE_MAKE_ALL;
+ mrg_set_ui (mrg, gedl_ui, &o);
+
+ mrg_add_timeout (mrg, 10100, save_idle, edl);
+
+ cache_renderer_iteration (mrg, edl);
+ mrg_add_timeout (mrg, 90 /* seconds */ * 1000, cache_renderer_iteration, edl);
+
+ gedl_get_duration (edl);
+ //mrg_set_target_fps (mrg, -1);
+// gedl_set_use_proxies (edl, 1);
+ toggle_use_proxies (NULL, edl, NULL);
+ renderer_start (edl);
+ mrg_main (mrg);
+ gedl_free (edl);
+ gegl_exit ();
+
+ return 0;
+}
diff --git a/gcut/gedl.c b/gcut/gedl.c
new file mode 100644
index 0000000..1ac5a36
--- /dev/null
+++ b/gcut/gedl.c
@@ -0,0 +1,1608 @@
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <gegl.h>
+#include <gexiv2/gexiv2.h>
+#include <gegl-audio-fragment.h>
+
+/* GEGL edit decision list - a digital video cutter and splicer */
+
+/* take a string and expand {t=v i t=v t=v } to numeric or string
+ value. Having it that way.. makes it hard to keep parts of graph,.
+ unless graph is kept when constructing... and values are filled in
+ if topology of graphs match..
+ */
+
+#include "gedl.h"
+
+#define DEFAULT_output_path "output.mp4"
+#define DEFAULT_video_codec "auto"
+#define DEFAULT_audio_codec "auto"
+#define DEFAULT_video_width 0
+#define DEFAULT_video_height 0
+#define DEFAULT_proxy_width 0
+#define DEFAULT_proxy_height 0
+#define DEFAULT_video_bufsize 0
+#define DEFAULT_video_bitrate 256
+#define DEFAULT_video_tolerance -1
+#define DEFAULT_audio_bitrate 64
+#define DEFAULT_audio_samplerate 64
+#define DEFAULT_frame_start 0
+#define DEFAULT_frame_end 0
+#define DEFAULT_selection_start 0
+#define DEFAULT_selection_end 0
+#define DEFAULT_range_start 0
+#define DEFAULT_range_end 0
+#define DEFAULT_framedrop 0
+
+char *gedl_binary_path = NULL;
+
+const char *default_edl =
+#include "default.edl.inc"
+;
+
+static char *escaped_base_path (GeglEDL *edl, const char *clip_path)
+{
+ char *path0= g_strdup (clip_path);
+ char *path = path0;
+ int i;
+ char *ret = 0;
+ if (!strncmp (path, edl->parent_path, strlen(edl->parent_path)))
+ path += strlen (edl->parent_path);
+ for (i = 0; path[i];i ++)
+ {
+ switch (path[i]){
+ case '/':
+ case ' ':
+ case '\'':
+ case '#':
+ case '%':
+ case '?':
+ case '*':
+ path[i]='_';
+ break;
+ }
+ }
+ ret = g_strdup (path);
+ g_free (path0);
+ return ret;
+}
+
+char *gedl_make_thumb_path (GeglEDL *edl, const char *clip_path)
+{
+ gchar *ret;
+ gchar *path = escaped_base_path (edl, clip_path);
+ ret = g_strdup_printf ("%s.gedl/thumb/%s.png", edl->parent_path, path); // XXX: should escape
relative/absolute path instead of basename - or add bit of its hash
+ g_free (path);
+ return ret;
+}
+
+char *gedl_make_proxy_path (GeglEDL *edl, const char *clip_path)
+{
+ gchar *ret;
+ gchar *path = escaped_base_path (edl, clip_path);
+ ret = g_strdup_printf ("%s.gedl/proxy/%s-%ix%i.mp4", edl->parent_path, path, edl->proxy_width,
edl->proxy_height);
+ g_free (path);
+ return ret;
+}
+
+
+#include <stdlib.h>
+
+GeglEDL *gedl_new (void)
+{
+ GeglRectangle roi = {0,0,1024, 1024};
+ GeglEDL *edl = g_malloc0(sizeof (GeglEDL));
+
+ edl->gegl = gegl_node_new ();
+ edl->cache_flags = 0;
+ edl->cache_flags = CACHE_TRY_ALL;
+ //edl->cache_flags = CACHE_TRY_ALL | CACHE_MAKE_ALL;
+ edl->selection_start = 23;
+ edl->selection_end = 42;
+
+ edl->cache_loader = gegl_node_new_child (edl->gegl, "operation", "gegl:" CACHE_FORMAT "-load", NULL);
+
+ edl->output_path = DEFAULT_output_path;
+ edl->video_codec = DEFAULT_video_codec;
+ edl->audio_codec = DEFAULT_audio_codec;
+ edl->video_width = DEFAULT_video_width;
+ edl->video_height = DEFAULT_video_height;
+ edl->proxy_width = DEFAULT_proxy_width;
+ edl->proxy_height = DEFAULT_proxy_height;
+ edl->video_size_default = 1;
+ edl->video_bufsize = DEFAULT_video_bufsize;
+ edl->video_bitrate = DEFAULT_video_bitrate;
+ edl->video_tolerance = DEFAULT_video_tolerance;;
+ edl->audio_bitrate = DEFAULT_audio_bitrate;
+ edl->audio_samplerate = DEFAULT_audio_samplerate;
+ edl->framedrop = DEFAULT_framedrop;
+ edl->frame_no = 0; /* frame-no in ui shell */
+ edl->frame = -1; /* frame-no in renderer thread */
+ edl->scale = 1.0;
+
+ edl->buffer = gegl_buffer_new (&roi, babl_format ("R'G'B'A u8"));
+ edl->buffer_copy = gegl_buffer_new (&roi, babl_format ("R'G'B'A u8"));
+ edl->buffer_copy_temp = gegl_buffer_new (&roi, babl_format ("R'G'B'A u8"));
+
+ edl->clip_query = strdup ("");
+ edl->use_proxies = 0;
+
+ g_mutex_init (&edl->buffer_copy_mutex);
+ return edl;
+}
+
+void gedl_set_size (GeglEDL *edl, int width, int height);
+void gedl_set_size (GeglEDL *edl, int width, int height)
+{
+ edl->width = width;
+ edl->height = height;
+}
+
+void gedl_free (GeglEDL *edl)
+{
+ while (edl->clips)
+ {
+ clip_free (edl->clips->data);
+ edl->clips = g_list_remove (edl->clips, edl->clips->data);
+ }
+ if (edl->path)
+ g_free (edl->path);
+ if (edl->parent_path)
+ g_free (edl->parent_path);
+
+ g_object_unref (edl->gegl);
+ if (edl->buffer)
+ g_object_unref (edl->buffer);
+ if (edl->buffer_copy)
+ g_object_unref (edl->buffer_copy);
+ if (edl->buffer_copy_temp)
+ g_object_unref (edl->buffer_copy_temp);
+ g_mutex_clear (&edl->buffer_copy_mutex);
+ g_free (edl);
+}
+
+
+Clip *gedl_get_clip (GeglEDL *edl, int frame, int *clip_frame_no)
+{
+ GList *l;
+ int clip_start = 0;
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ int clip_frames = clip_get_frames (clip);
+ if (clip->is_meta)
+ continue;
+
+ if (frame - clip_start < clip_frames)
+ {
+ /* found right clip */
+ if (clip_frame_no)
+ *clip_frame_no = (frame - clip_start) + clip_get_start (clip);
+ return clip;
+ }
+ clip_start += clip_frames;
+ }
+ return NULL;
+}
+
+int cache_hits = 0;
+int cache_misses = 0;
+
+void gedl_set_use_proxies (GeglEDL *edl, int use_proxies)
+{
+ int frame;
+ edl->use_proxies = use_proxies;
+
+ if (edl->use_proxies)
+ gedl_set_size (edl, edl->proxy_width, edl->proxy_height);
+ else
+ gedl_set_size (edl, edl->video_width, edl->video_height);
+
+ frame = edl->frame;
+ if (frame > 0)
+ {
+ edl->frame--;
+ gedl_set_frame (edl, frame);
+ }
+
+}
+
+/* computes the hash of a given rendered frame - without altering
+ * any state
+ */
+gchar *gedl_get_frame_hash_full (GeglEDL *edl, int frame,
+ Clip **clip0, int *clip0_frame,
+ Clip **clip1, int *clip1_frame,
+ double *mix)
+{
+ GList *l;
+ int clip_start = 0;
+ int prev_clip_start = 0;
+
+
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ int clip_frames = clip_get_frames (clip);
+
+ if (clip->is_meta)
+ continue;
+
+ if (frame - clip_start < clip_frames)
+ {
+ int clip_frame_no = (frame - clip_start) + clip_get_start (clip);
+
+ GList *lp = l->prev;
+ GList *ln = l->next;
+ Clip *prev = lp?lp->data:NULL;
+ Clip *next = ln?ln->data:NULL;
+ int prev_fade_len;
+ int next_fade_len;
+
+ while (prev && prev->is_meta)
+ {
+ lp = lp->prev;
+ prev = lp?lp->data:NULL;
+ }
+
+ while (next && next->is_meta)
+ {
+ ln = ln->next;
+ next = ln?ln->data:NULL;
+ }
+
+ /* XXX: fade in from black if there is no previous clip */
+
+ prev_fade_len = prev ? clip_get_frames (prev) : clip_frames;
+ next_fade_len = next ? clip_get_frames (next) : clip_frames;
+
+ if (prev_fade_len > clip_frames) prev_fade_len = clip_frames;
+ if (next_fade_len > clip_frames) next_fade_len = clip_frames;
+
+ prev_fade_len /= 2;
+ next_fade_len /= 2; /* 1/4 the length of the smallest of this or other
+ clip is our maximum fade - should be adjusted to
+ 1/2 later - when perhaps a scaling factor or a
+ per-case duration is set which gets maxed by this
+ heuristic */
+ if (clip->fade/2 < prev_fade_len)
+ prev_fade_len = clip->fade/2;
+
+ if (next)
+ {
+ if (next->fade/2 < next_fade_len)
+ next_fade_len = next->fade/2;
+ }
+
+ if (prev && frame - clip_start < prev_fade_len) /* in */
+ {
+ char *clip0_hash = clip_get_frame_hash (clip, clip_frame_no);
+ char *clip1_hash = clip_get_frame_hash (prev, frame - prev_clip_start + clip_get_start (prev));
+ double ratio = 0.5 + ((frame-clip_start) * 1.0 / prev_fade_len)/2;
+ char *str = g_strdup_printf ("%s %s %f", clip1_hash, clip0_hash, ratio);
+ g_free (clip0_hash);
+ g_free (clip1_hash);
+ GChecksum *hash = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (hash, (void*)str, -1);
+ g_free (str);
+ char *ret = g_strdup (g_checksum_get_string(hash));
+ g_checksum_free (hash);
+ if (clip0) *clip0 = prev;
+ if (clip0_frame) *clip0_frame = frame - prev_clip_start + clip_get_start (prev);
+ if (clip1) *clip1 = clip;
+ if (clip1_frame) *clip1_frame = clip_frame_no;
+ if (mix) *mix = ratio;
+
+ return ret;
+ }
+
+ if (next && frame - clip_start > clip_frames - next_fade_len)/* out*/
+ {
+ char *clip0_hash = clip_get_frame_hash (clip, clip_frame_no);
+ char *clip1_hash = clip_get_frame_hash (next, frame - (clip_start + clip_frames) + clip_get_start
(next));
+ double ratio = (1.0-(clip_frames-(frame-clip_start)) * 1.0 / next_fade_len)/2;
+ char *str = g_strdup_printf ("%s %s %f", clip0_hash, clip1_hash, ratio);
+ g_free (clip0_hash);
+ g_free (clip1_hash);
+ GChecksum *hash = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (hash, (void*)str, -1);
+ g_free (str);
+ char *ret = g_strdup (g_checksum_get_string(hash));
+ g_checksum_free (hash);
+ if (clip0) *clip0 = clip;
+ if (clip0_frame) *clip0_frame = clip_frame_no;
+ if (clip1_frame) *clip1_frame = frame - (clip_start +clip_frames) + clip_get_start (next);
+ if (clip1) *clip1 = next;
+ if (mix) *mix = ratio;
+ return ret;
+ }
+ else
+ {
+ if (clip0) *clip0 = clip;
+ if (clip0_frame) *clip0_frame = clip_frame_no;
+ if (clip1) *clip1 = NULL;
+ if (mix) *mix = 0.0;
+ return clip_get_frame_hash (clip, clip_frame_no);
+ }
+ }
+ prev_clip_start = clip_start;
+ clip_start += clip_frames;
+ }
+
+ if (clip0) *clip0 = NULL;
+ if (clip0_frame) *clip0_frame = 0;
+ if (clip1_frame) *clip1_frame = 0;
+ if (clip1) *clip1 = NULL;
+ if (mix) *mix = 0.0;
+ return NULL;
+}
+
+gchar *gedl_get_frame_hash (GeglEDL *edl, int frame)
+{
+ return gedl_get_frame_hash_full (edl, frame, NULL, NULL, NULL, NULL, NULL);
+}
+
+void gedl_update_buffer (GeglEDL *edl)
+{
+ g_mutex_lock (&edl->buffer_copy_mutex);
+ {
+ GeglBuffer *t = edl->buffer_copy;
+ edl->buffer_copy = gegl_buffer_dup (edl->buffer);
+ if (t)
+ g_object_unref (t);
+ }
+ g_mutex_unlock (&edl->buffer_copy_mutex);
+}
+/* calling this causes gedl to rig up its graphs for providing/rendering this frame
+ */
+void gedl_set_frame (GeglEDL *edl, int frame)
+{
+ if ((edl->frame) == frame && (frame != 0))
+ {
+ return;
+ }
+
+ edl->frame = frame;
+
+ Clip *clip0; int clip0_frame;
+ Clip *clip1; int clip1_frame;
+ double mix;
+
+ char *frame_hash = gedl_get_frame_hash_full (edl, frame, &clip0, &clip0_frame, &clip1, &clip1_frame, &mix);
+ char *cache_path = g_strdup_printf ("%s.gedl/cache/%s", edl->parent_path, frame_hash);
+ g_free (frame_hash);
+ if (g_file_test (cache_path, G_FILE_TEST_IS_REGULAR) &&
+ (edl->cache_flags & CACHE_TRY_ALL))
+ {
+ Clip *clip = NULL;
+ gegl_node_set (edl->cache_loader, "path", cache_path, NULL);
+ gegl_node_link_many (edl->cache_loader, edl->result, NULL);
+ clip = edl_get_clip_for_frame (edl, edl->frame);
+ if (clip)
+ {
+ if (clip->audio)
+ {
+ g_object_unref (clip->audio);
+ clip->audio = NULL;
+ }
+ clip->audio = gegl_audio_fragment_new (44100, 2, 0, 44100);
+ gegl_meta_get_audio (cache_path, clip->audio);
+ }
+ GeglRectangle ext = gegl_node_get_bounding_box (edl->result);
+ gegl_buffer_set_extent (edl->buffer, &ext);
+ gegl_node_process (edl->store_final_buf);
+
+ gedl_update_buffer (edl);
+ g_free (cache_path);
+ return;
+ }
+
+ if (clip0 == NULL)
+ {
+ g_free (cache_path);
+ return;
+ }
+
+ if (clip1 == NULL)
+ {
+ clip_render_frame (clip0, clip0_frame);
+ gegl_node_link_many (clip0->nop_crop, edl->result, NULL);
+ }
+ else
+ {
+ gegl_node_set (edl->mix, "ratio", mix, NULL);
+ clip_render_frame (clip0, clip0_frame);
+ clip_render_frame (clip1, clip1_frame);
+ gegl_node_link_many (clip0->nop_crop, edl->mix, edl->result, NULL);
+ gegl_node_connect_to (clip1->nop_crop, "output", edl->mix, "aux");
+ }
+ gegl_node_process (edl->store_final_buf);
+ gedl_update_buffer (edl);
+
+ /* write cached render of this frame */
+ if (cache_path &&
+ !strstr (clip0->path, ".gedl/cache") && (!edl->use_proxies))
+ {
+ const gchar *cache_path_final = cache_path;
+ gchar *cache_path = g_strdup_printf ("%s~", cache_path_final);
+
+ if (!g_file_test (cache_path_final, G_FILE_TEST_IS_REGULAR) && !edl->playing)
+ {
+ GeglNode *save_graph = gegl_node_new ();
+ GeglNode *save;
+ save = gegl_node_new_child (save_graph,
+ "operation", "gegl:" CACHE_FORMAT "-save",
+ "path", cache_path,
+ NULL);
+ if (!strcmp (CACHE_FORMAT, "png"))
+ {
+ gegl_node_set (save, "bitdepth", 8, NULL);
+ }
+ gegl_node_link_many (edl->result, save, NULL);
+ gegl_node_process (save);
+ if (clip1 && clip1->audio && mix > 0.5)
+ gegl_meta_set_audio (cache_path, clip1->audio);
+ else if (clip0->audio) // XXX: mix audio
+ gegl_meta_set_audio (cache_path, clip0->audio);
+ rename (cache_path, cache_path_final);
+ g_object_unref (save_graph);
+ }
+ g_free (cache_path);
+ }
+
+ g_free (cache_path);
+}
+
+void gedl_set_time (GeglEDL *edl, double seconds)
+{
+ gedl_set_frame (edl, seconds * edl->fps);
+}
+
+void gedl_set_fps (GeglEDL *edl, double fps)
+{
+ edl->fps = fps;
+}
+double gedl_get_fps (GeglEDL *edl)
+{
+ return edl->fps;
+}
+int gedl_get_frame (GeglEDL *edl)
+{
+ return edl->frame;
+}
+double gedl_get_time (GeglEDL *edl)
+{
+ return edl->frame / edl->fps;
+}
+GeglAudioFragment *gedl_get_audio (GeglEDL *edl)
+{
+ Clip * clip = edl_get_clip_for_frame (edl, edl->frame);
+ return clip?clip->audio:NULL;
+}
+const char *gedl_get_clip_path (GeglEDL *edl)
+{
+ Clip * clip = edl_get_clip_for_frame (edl, edl->frame);
+ return clip?clip->clip_path:"";
+}
+
+void gedl_get_video_info (const char *path, int *duration, double *fps)
+{
+ GeglNode *gegl = gegl_node_new ();
+ GeglNode *probe = gegl_node_new_child (gegl, "operation",
+ "gegl:ff-load", "path", path, NULL);
+ gegl_node_process (probe);
+
+ if (duration)
+ gegl_node_get (probe, "frames", duration, NULL);
+ if (fps)
+ gegl_node_get (probe, "frame-rate", fps, NULL);
+ g_object_unref (gegl);
+}
+
+int gedl_get_duration (GeglEDL *edl)
+{
+ int count = 0;
+ GList *l;
+ for (l = edl->clips; l; l = l->next)
+ {
+ ((Clip*)(l->data))->abs_start = count;
+ count += clip_get_frames (l->data);
+ }
+ return count;
+}
+#include <string.h>
+
+void gedl_parse_clip (GeglEDL *edl, const char *line)
+{
+ int start = 0; int end = 0; int duration = 0;
+ const char *rest = NULL;
+ char path[1024];
+ if (line[0] == '#' ||
+ line[1] == '#' ||
+ strlen (line) < 4)
+ return;
+
+ if (strstr (line, "--"))
+ rest = strstr (line, "--") + 2;
+
+ if (rest) while (*rest == ' ')rest++;
+
+ sscanf (line, "%s %i %i %i", path, &start, &end, &duration);
+ if (strlen (path) > 3)
+ {
+ SourceClip *sclip = g_new0 (SourceClip, 1);
+ edl->clip_db = g_list_append (edl->clip_db, sclip);
+ sclip->path = g_strdup (path);
+ sclip->start = start;
+ sclip->end = end;
+ sclip->duration = duration;
+ if (rest)
+ sclip->title = g_strdup (rest);
+ }
+ /* todo: parse hh:mm:ss.nn timestamps,
+ */
+}
+
+void gedl_parse_line (GeglEDL *edl, const char *line)
+{
+ int start = 0; int end = 0;
+ const char *rest = NULL;
+ char path[1024];
+ if (line[0] == '#' ||
+ line[1] == '#' ||
+ strlen (line) < 4)
+ return;
+
+ if (strchr (line, '=') && !strstr(line, "--"))
+ {
+ char *key = g_strdup (line);
+ char *value = strchr (key, '=') + 1;
+ value[-1]='\0';
+
+ while (value[strlen(value)-1]==' ' ||
+ value[strlen(value)-1]=='\n')
+ value[strlen(value)-1]='\0';
+ if (!strcmp (key, "fps")) gedl_set_fps (edl, g_strtod (value, NULL));
+ if (!strcmp (key, "framedrop")) edl->framedrop = g_strtod (value, NULL);
+ if (!strcmp (key, "output-path")) edl->output_path = g_strdup (value);
+ if (!strcmp (key, "video-codec")) edl->video_codec = g_strdup (value);
+ if (!strcmp (key, "audio-codec")) edl->audio_codec = g_strdup (value);
+ if (!strcmp (key, "audio-sample-rate")) edl->audio_samplerate = g_strtod (value, NULL);
+ if (!strcmp (key, "video-bufsize")) edl->video_bufsize = g_strtod (value, NULL);
+ if (!strcmp (key, "video-bitrate")) edl->video_bitrate = g_strtod (value, NULL);
+ if (!strcmp (key, "audio-bitrate")) edl->audio_bitrate = g_strtod (value, NULL);
+ if (!strcmp (key, "video-width")) edl->video_width = g_strtod (value, NULL);
+ if (!strcmp (key, "video-height")) edl->video_height = g_strtod (value, NULL);
+ if (!strcmp (key, "proxy-width")) edl->proxy_width = g_strtod (value, NULL);
+ if (!strcmp (key, "proxy-height")) edl->proxy_height = g_strtod (value, NULL);
+ if (!strcmp (key, "frame-start")) edl->range_start = g_strtod (value, NULL);
+ if (!strcmp (key, "frame-end")) edl->range_end = g_strtod (value, NULL);
+ if (!strcmp (key, "selection-start")) edl->selection_start = g_strtod (value, NULL);
+ if (!strcmp (key, "selection-end")) edl->selection_end = g_strtod (value, NULL);
+ //if (!strcmp (key, "range-start")) edl->range_start = g_strtod (value, NULL);
+ //if (!strcmp (key, "range-end")) edl->range_end = g_strtod (value, NULL);
+ if (!strcmp (key, "frame-no")) edl->frame_no = g_strtod (value, NULL);
+ if (!strcmp (key, "frame-scale")) edl->scale = g_strtod (value, NULL);
+ if (!strcmp (key, "t0")) edl->t0 = g_strtod (value, NULL);
+
+ g_free (key);
+ return;
+ }
+ if (strstr (line, "--"))
+ rest = strstr (line, "--") + 2;
+
+
+ {
+ const char *p = strstr (line, "--");
+ if (!p)
+ p = line + strlen(line)-1;
+ {
+ if (p>line) p --;
+ while (p>line && *p == ' ') p --;
+
+ while (p>line && isdigit (*p)) p --;
+ end = atoi (p+1);
+
+ if (p>line) p --;
+ while (p>line && *p == ' ') p --;
+
+ while (p>line && isdigit (*p)) p --;
+ start = atoi (p+1);
+
+ if (p>line) p --;
+ while (p>line && *p == ' ') p --;
+
+ memcpy (path, line, (p-line) + 1);
+ path[(p-line)+1]=0;
+ }
+ }
+
+ if (strlen (path) > 3)
+ {
+ Clip *clip = NULL;
+ int ff_probe = 0;
+ clip = clip_new_full (edl, path, start, end);
+
+
+ if (!clip_is_static_source (clip) &&
+ (start == 0 && end == 0))
+ ff_probe = 1;
+ edl->clips = g_list_append (edl->clips, clip);
+ if (strstr (line, "[fade="))
+ {
+ ff_probe = 1;
+ rest = strstr (line, "[fade=") + strlen ("[fade=");
+ clip->fade = atoi (rest);
+ while (*rest && *rest != ']') rest++;
+ if (*rest == ']') rest++;
+ }
+
+ if (rest) while (*rest == ' ')rest++;
+
+
+ if (clip == edl->clips->data)
+ {
+ ff_probe = 1;
+ }
+
+ if (ff_probe && !clip_is_static_source (clip))
+ {
+ gedl_get_video_info (clip->path, &clip->duration, &clip->fps);
+
+ if (edl->fps == 0.0)
+ {
+ gedl_set_fps (edl, clip->fps);
+ }
+ }
+
+ if (rest)
+ {
+ clip->filter_graph = g_strdup (rest);
+ while (clip->filter_graph[strlen(clip->filter_graph)-1]==' ' ||
+ clip->filter_graph[strlen(clip->filter_graph)-1]=='\n')
+ clip->filter_graph[strlen(clip->filter_graph)-1]='\0';
+ }
+
+ if (clip->end == 0)
+ {
+ clip->end = clip->duration;
+ }
+ }
+ else if (start == 0 && end == 0 && rest)
+ {
+ Clip *clip = clip_new_full (edl, NULL, 0, 0);
+ clip->filter_graph = g_strdup (rest);
+ edl->clips = g_list_append (edl->clips, clip);
+ }
+}
+
+#include <string.h>
+
+void gedl_update_video_size (GeglEDL *edl);
+GeglEDL *gedl_new_from_string (const char *string, const char *parent_path);
+GeglEDL *gedl_new_from_string (const char *string, const char *parent_path)
+{
+ GString *line = g_string_new ("");
+ GeglEDL *edl = gedl_new ();
+ int clips_done = 0;
+ int newlines = 0;
+ edl->parent_path = g_strdup (parent_path);
+
+ for (const char *p = string; p==string || *(p-1); p++)
+ {
+ switch (*p)
+ {
+ case 0:
+ case '\n':
+ if (clips_done)
+ {
+ if (line->len > 2)
+ gedl_parse_clip (edl, line->str);
+ g_string_assign (line, "");
+ }
+ else
+ {
+ if (line->str[0] == '-' &&
+ line->str[1] == '-' &&
+ line->str[2] == '-')
+ {
+ clips_done = 1;
+ g_string_assign (line, "");
+ }
+ else
+ {
+ if (*p == 0)
+ {
+ newlines = 2;
+ }
+ else if (*p == '\n')
+ {
+ newlines ++;
+ }
+ else
+ {
+ newlines = 0;
+ }
+ if (strchr (line->str, '='))
+ newlines = 3;
+
+ if (newlines >= 2)
+ {
+ gedl_parse_line (edl, line->str);
+ g_string_assign (line, "");
+ }
+ else
+ g_string_append_c (line, *p);
+ }
+ }
+ break;
+ default: g_string_append_c (line, *p);
+ break;
+ }
+ }
+ g_string_free (line, TRUE);
+
+ gedl_update_video_size (edl);
+ gedl_set_use_proxies (edl, edl->use_proxies);
+
+ return edl;
+}
+
+void gedl_save_path (GeglEDL *edl, const char *path)
+{
+ char *serialized;
+ serialized = gedl_serialize (edl);
+
+ if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+ {
+ char backup_path[4096];
+ struct tm *tim;
+ char *old_contents = NULL;
+
+ g_file_get_contents (path, &old_contents, NULL, NULL);
+ if (old_contents)
+ {
+ char *oc, *s;
+
+ oc = strstr (old_contents, "\n\n");
+ s = strstr (serialized, "\n\n");
+ if (oc && s && !strcmp (oc, s))
+ {
+ g_free (old_contents);
+ return;
+ }
+ g_free (old_contents);
+ }
+
+ sprintf (backup_path, "%s.gedl/history/%s-", edl->parent_path, basename(edl->path));
+
+ time_t now = time(NULL);
+ tim = gmtime(&now);
+
+ strftime(backup_path + strlen(backup_path), sizeof(backup_path)-strlen(backup_path), "%Y%m%d_%H%M%S",
tim);
+ rename (path, backup_path);
+ }
+
+ FILE *file = fopen (path, "w");
+ if (!file)
+ {
+ g_free (serialized);
+ return;
+ }
+
+ if (serialized)
+ {
+ fprintf (file, "%s", serialized);
+ g_free (serialized);
+ }
+ fclose (file);
+}
+
+void gedl_update_video_size (GeglEDL *edl)
+{
+ if ((edl->video_width == 0 || edl->video_height == 0) && edl->clips)
+ {
+ Clip *clip = edl->clips->data;
+ GeglNode *gegl = gegl_node_new ();
+ GeglRectangle rect;
+ // XXX: is ff-load good for pngs and jpgs as well?
+ GeglNode *probe;
+ probe = gegl_node_new_child (gegl, "operation", "gegl:ff-load", "path", clip->path, NULL);
+ gegl_node_process (probe);
+ rect = gegl_node_get_bounding_box (probe);
+ edl->video_width = rect.width;
+ edl->video_height = rect.height;
+ g_object_unref (gegl);
+ }
+ if ((edl->proxy_width <= 0) && edl->video_width)
+ {
+ edl->proxy_width = 320;
+ }
+ if ((edl->proxy_height <= 0) && edl->video_width)
+ {
+ edl->proxy_height = edl->proxy_width * (1.0 * edl->video_height / edl->video_width);
+ }
+}
+
+static void generate_gedl_dir (GeglEDL *edl)
+{
+ char *tmp = g_strdup_printf ("cd %s; mkdir .gedl 2>/dev/null ; mkdir .gedl/cache 2>/dev/null mkdir
.gedl/proxy 2>/dev/null mkdir .gedl/thumb 2>/dev/null ; mkdir .gedl/video 2>/dev/null; mkdir .gedl/history
2>/dev/null", edl->parent_path);
+ system (tmp);
+ g_free (tmp);
+}
+
+static GTimer * timer = NULL;
+static guint timeout_id = 0;
+static gdouble throttle = 4.0;
+
+void gedl_reread (GeglEDL *edl)
+{
+ GeglEDL *new_edl = gedl_new_from_path (edl->path);
+ GList *l;
+
+ /* swap clips */
+ l = edl->clips;
+ edl->clips = new_edl->clips;
+ new_edl->clips = l;
+ edl->active_clip = NULL; // XXX: better to resolve?
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ clip->edl = edl;
+ }
+
+ for (l = new_edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ clip->edl = new_edl;
+ }
+
+ gedl_free (new_edl);
+}
+
+static gboolean timeout (gpointer user_data)
+{
+ GeglEDL *edl = user_data;
+ gedl_reread (edl);
+ // system (user_data);
+ g_timer_start (timer);
+ timeout_id = 0;
+ return FALSE;
+}
+
+
+static void file_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GeglEDL *edl)
+{
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
+ {
+ if (!timeout_id)
+ {
+ gdouble elapsed = g_timer_elapsed (timer, NULL);
+ gdouble wait = throttle - elapsed;
+
+ if (wait <= 0.0)
+ wait = 0.0;
+
+ timeout_id = g_timeout_add (wait * 1000, timeout, edl);
+ }
+ }
+}
+
+void
+gedl_monitor_start (GeglEDL *edl)
+{
+ if (!edl->path)
+ return;
+ /* save to make sure file exists */
+ gedl_save_path (edl, edl->path);
+ /* start monitor */
+ timer = g_timer_new ();
+ edl->monitor = g_file_monitor_file (g_file_new_for_path (edl->path),
+ G_FILE_MONITOR_NONE,
+ NULL, NULL);
+ if(0)g_signal_connect (edl->monitor, "changed", G_CALLBACK (file_changed), edl);
+}
+
+GeglEDL *gedl_new_from_path (const char *path)
+{
+ GeglEDL *edl = NULL;
+ gchar *string = NULL;
+
+ g_file_get_contents (path, &string, NULL, NULL);
+ if (string)
+ {
+ char *rpath = realpath (path, NULL);
+ char *parent = g_strdup (rpath);
+ strrchr(parent, '/')[1]='\0';
+
+ edl = gedl_new_from_string (string, parent);
+
+ g_free (parent);
+ g_free (string);
+ if (!edl->path)
+ edl->path = g_strdup (realpath (path, NULL)); // XXX: leak
+ }
+ else
+ {
+ char *parent = NULL;
+ if (path[0] == '/')
+ {
+ parent = strdup (path);
+ strrchr(parent, '/')[1]='\0';
+ }
+ else
+ {
+ parent = g_malloc0 (PATH_MAX);
+ getcwd (parent, PATH_MAX);
+ }
+ edl = gedl_new_from_string ("", parent);
+ if (!edl->path)
+ {
+ if (path[0] == '/')
+ {
+ edl->path = g_strdup (path);
+ }
+ else
+ {
+ edl->path = g_strdup_printf ("%s/%s", parent, basename (path));
+ }
+ }
+ g_free (parent);
+ }
+ generate_gedl_dir (edl);
+
+ return edl;
+}
+
+static void setup (GeglEDL *edl)
+{
+ edl->result = gegl_node_new_child (edl->gegl, "operation", "gegl:nop", NULL);
+ edl->mix = gegl_node_new_child (edl->gegl, "operation", "gegl:mix", NULL);
+ edl->encode = gegl_node_new_child (edl->gegl, "operation", "gegl:ff-save",
+ "path", edl->output_path,
+ "frame-rate", gedl_get_fps (edl),
+ "video-bit-rate", edl->video_bitrate,
+ "video-bufsize", edl->video_bufsize,
+ "audio-bit-rate", edl->audio_bitrate,
+ "audio-codec", edl->audio_codec,
+ "video-codec", edl->video_codec,
+ NULL);
+ edl->cached_result = gegl_node_new_child (edl->gegl, "operation", "gegl:buffer-source", "buffer",
edl->buffer, NULL);
+ edl->store_final_buf = gegl_node_new_child (edl->gegl, "operation", "gegl:write-buffer", "buffer",
edl->buffer, NULL);
+
+ gegl_node_link_many (edl->result, edl->store_final_buf, NULL);
+ gegl_node_link_many (edl->cached_result, edl->encode, NULL);
+}
+
+static void init (int argc, char **argv)
+{
+ gegl_init (&argc, &argv);
+ g_object_set (gegl_config (),
+ "application-license", "GPL3",
+ NULL);
+}
+
+static void encode_frames (GeglEDL *edl)
+{
+ int frame_no;
+ for (frame_no = edl->range_start; frame_no <= edl->range_end; frame_no++)
+ {
+ edl->frame_no = frame_no;
+ gedl_set_frame (edl, edl->frame_no);
+
+ fprintf (stdout, "\r%1.2f%% %04d / %04d ",
+ 100.0 * (frame_no-edl->range_start) * 1.0 / (edl->range_end - edl->range_start),
+ frame_no, edl->range_end);
+
+ gegl_node_set (edl->encode, "audio", gedl_get_audio (edl), NULL);
+ gegl_node_process (edl->encode);
+ fflush (0);
+ }
+ fprintf (stdout, "\n");
+}
+
+void nop_handler(int sig)
+{
+}
+
+static int stop_cacher = 0;
+
+void handler1(int sig)
+{
+ stop_cacher = 1;
+}
+
+static int cacheno = 0;
+static int cachecount = 2;
+
+static inline int this_cacher (int frame_no)
+{
+ if (frame_no % cachecount == cacheno) return 1;
+ return 0;
+}
+
+static void process_frames_cache (GeglEDL *edl)
+{
+ int frame_no = edl->frame_no;
+ int frame_start = edl->frame_no;
+ int duration;
+ signal(SIGUSR2, handler1);
+ duration = gedl_get_duration (edl);
+
+ GList *l;
+ int clip_start = 0;
+
+ // TODO: use bitmap from ui to speed up check
+
+ edl->frame_no = frame_start;
+ if (this_cacher (edl->frame_no))
+ gedl_set_frame (edl, edl->frame_no);
+ if (stop_cacher)
+ return;
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ int clip_frames = clip_get_frames (clip);
+ edl->frame_no = clip_start;
+ if (this_cacher (edl->frame_no))
+ {
+ gedl_set_frame (edl, edl->frame_no);
+ }
+
+ clip_start += clip_frames;
+ if (stop_cacher)
+ return;
+ }
+
+ for (frame_no = frame_start - 3; frame_no < duration; frame_no++)
+ {
+ edl->frame_no = frame_no;
+ if (this_cacher (edl->frame_no))
+ gedl_set_frame (edl, edl->frame_no);
+ if (stop_cacher)
+ return;
+ }
+ for (frame_no = 0; frame_no < frame_start; frame_no++)
+ {
+ edl->frame_no = frame_no;
+ if (this_cacher (edl->frame_no))
+ gedl_set_frame (edl, edl->frame_no);
+ if (stop_cacher)
+ return;
+ }
+}
+
+static inline void set_bit (guchar *bitmap, int no)
+{
+ bitmap[no/8] |= (1 << (no % 8));
+}
+
+guchar *gedl_get_cache_bitmap (GeglEDL *edl, int *length_ret)
+{
+ int duration = gedl_get_duration (edl);
+ int frame_no;
+ int length = (duration / 8) + 1;
+ guchar *ret = g_malloc0 (length);
+
+ if (length_ret)
+ *length_ret = length;
+
+ for (frame_no = 0; frame_no < duration; frame_no++)
+ {
+ const gchar *hash = gedl_get_frame_hash (edl, frame_no);
+ gchar *path = g_strdup_printf ("%s.gedl/cache/%s", edl->parent_path, hash);
+ if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+ set_bit (ret, frame_no);
+ g_free (path);
+ }
+
+ return ret;
+}
+
+static void process_frames_cache_stat (GeglEDL *edl)
+{
+ int frame_no = edl->frame_no;
+ int duration;
+ signal(SIGUSR2, handler1);
+ duration = gedl_get_duration (edl);
+
+ /* XXX: should probably do first frame of each clip - since
+ these are used for quick keyboard navigation of the
+ project
+ */
+
+ for (frame_no = 0; frame_no < duration; frame_no++)
+ {
+ const gchar *hash = gedl_get_frame_hash (edl, frame_no);
+ gchar *path = g_strdup_printf ("%s.gedl/cache/%s", edl->parent_path, hash);
+ if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
+ fprintf (stdout, "%i ", frame_no);
+ // fprintf (stdout, ". %i %s\n", frame_no, hash);
+ // else
+ // fprintf (stdout, " %i %s\n", frame_no, hash);
+ g_free (path);
+ }
+}
+
+int gegl_make_thumb_image (GeglEDL *edl, const char *path, const char *icon_path)
+{
+ GString *str = g_string_new ("");
+
+ g_string_assign (str, "");
+ g_string_append_printf (str, "%s iconographer -p -h -f 'mid-col 96 audio' %s -a %s",
+ //g_string_append_printf (str, "iconographer -p -h -f 'thumb 96' %s -a %s",
+ gedl_binary_path, path, icon_path);
+ system (str->str);
+
+ g_string_free (str, TRUE);
+
+ return 0;
+}
+
+int gegl_make_thumb_video (GeglEDL *edl, const char *path, const char *thumb_path)
+{
+ GString *str = g_string_new ("");
+
+ g_string_assign (str, "");
+ g_string_append_printf (str, "ffmpeg -y -i %s -vf scale=%ix%i %s", path, edl->proxy_width,
edl->proxy_height, thumb_path);
+ system (str->str);
+ g_string_free (str, TRUE);
+
+ return 0;
+#if 0 // much slower and worse for fps/audio than ffmpeg method for creating thumbs
+ int tot_frames; //
+ g_string_append_printf (str,
"video-bitrate=100\n\noutput-path=%s\nvideo-width=256\nvideo-height=144\n\n%s\n", thumb_path, path);
+ edl = gedl_new_from_string (str->str);
+ setup (edl);
+ tot_frames = gedl_get_duration (edl);
+
+ if (edl->range_end == 0)
+ edl->range_end = tot_frames-1;
+ process_frames (edl);
+ gedl_free (edl);
+ g_string_free (str, TRUE);
+ return 0;
+#endif
+}
+
+int gedl_ui_main (GeglEDL *edl);
+
+int gegl_make_thumb_video (GeglEDL *edl, const char *path, const char *thumb_path);
+void gedl_make_proxies (GeglEDL *edl)
+{
+ GList *l;
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ if (clip->is_chain == 0 && clip->static_source == 0 && clip->is_meta == 0)
+ {
+ char *proxy_path = gedl_make_proxy_path (edl, clip->path);
+ char *thumb_path = gedl_make_thumb_path (edl, clip->path);
+ if (!g_file_test (proxy_path, G_FILE_TEST_IS_REGULAR))
+ gegl_make_thumb_video (edl, clip->path, proxy_path);
+ if (!g_file_test (thumb_path, G_FILE_TEST_IS_REGULAR))
+ gegl_make_thumb_image(edl, proxy_path, thumb_path);
+ g_free (proxy_path);
+ g_free (thumb_path);
+ }
+ }
+}
+
+void gedl_start_sanity (void)
+{
+ int fails = 0;
+ if (system("which ffmpeg > /dev/null") != 0)
+ {
+ fprintf (stderr, "gedl missing runtime dependency: ffmpeg command in PATH\n");
+ fails ++;
+ }
+ if (!gegl_has_operation ("gegl:ff-load"))
+ {
+ fprintf (stderr, "gedl missing runtime dependenct: gegl:ff-load operation\n");
+ fails ++;
+ }
+ if (!gegl_has_operation ("gegl:ff-save"))
+ {
+ fprintf (stderr, "gedl missing runtime dependenct: gegl:ff-save operation\n");
+ fails ++;
+ }
+ if (fails)
+ exit (-1);
+}
+
+gint iconographer_main (gint argc, gchar **argv);
+
+int main (int argc, char **argv)
+{
+ GeglEDL *edl = NULL;
+ const char *edl_path = "input.edl";
+ int tot_frames;
+
+ gedl_binary_path = realpath (argv[0], NULL);
+ if (!gedl_binary_path)
+ gedl_binary_path = "gedl";
+
+ if (argv[1] && !strcmp (argv[1], "iconographer"))
+ {
+ argv[1] = argv[0];
+ return iconographer_main (argc-1, argv + 1);
+ }
+
+ setenv ("GEGL_USE_OPENCL", "no", 1);
+ setenv ("GEGL_MIPMAP_RENDERING", "1", 1);
+
+ init (argc, argv);
+ gedl_start_sanity ();
+
+ if (!argv[1])
+ {
+ static char *new_argv[3]={NULL, "gedl.edl", NULL};
+ new_argv[0] = argv[0];
+ argv = new_argv;
+ argc++;
+ g_file_set_contents (argv[1], default_edl, -1, NULL);
+ }
+
+ edl_path = argv[1]; //realpath (argv[1], NULL);
+
+ if (g_str_has_suffix (edl_path, ".mp4") ||
+ g_str_has_suffix (edl_path, ".ogv") ||
+ g_str_has_suffix (edl_path, ".mkv") ||
+ g_str_has_suffix (edl_path, ".MKV") ||
+ g_str_has_suffix (edl_path, ".avi") ||
+ g_str_has_suffix (edl_path, ".MP4") ||
+ g_str_has_suffix (edl_path, ".OGV") ||
+ g_str_has_suffix (edl_path, ".AVI"))
+ {
+ char str[1024];
+ int duration;
+ double fps;
+ GeglNode *gegl = gegl_node_new ();
+ GeglNode *probe = gegl_node_new_child (gegl, "operation",
+ "gegl:ff-load", "path", edl_path,
+ NULL);
+ gegl_node_process (probe);
+
+ gegl_node_get (probe, "frames", &duration, NULL);
+ gegl_node_get (probe, "frame-rate", &fps, NULL);
+ g_object_unref (gegl);
+
+ sprintf (str, "%s 0 %i\n", edl_path, duration);
+ {
+ char * path = realpath (edl_path, NULL);
+ char * rpath = g_strdup_printf ("%s.edl", path);
+ char * parent = g_strdup (rpath);
+ strrchr(parent, '/')[1]='\0';
+ edl = gedl_new_from_string (str, parent);
+ g_free (parent);
+ edl->path = rpath;
+ free (path);
+ }
+ generate_gedl_dir (edl);
+ }
+ else
+ {
+ edl = gedl_new_from_path (edl_path);
+ }
+
+ chdir (edl->parent_path); /* we try as good as we can to deal with absolute
+ paths correctly, */
+
+ setup (edl);
+
+ {
+#define RUNMODE_UI 0
+#define RUNMODE_RENDER 1
+#define RUNMODE_CACHE 2
+#define RUNMODE_CACHE_STAT 3
+#define RUNMODE_RESERIALIZE 4
+ int runmode = RUNMODE_UI;
+ for (int i = 0; argv[i]; i++)
+ {
+ if (!strcmp (argv[i], "render")) runmode = RUNMODE_RENDER;
+ if (!strcmp (argv[i], "reserialize")) runmode = RUNMODE_RESERIALIZE;
+ if (!strcmp (argv[i], "cachestat")) runmode = RUNMODE_CACHE_STAT;
+ if (!strcmp (argv[i], "cache"))
+ {
+ runmode = RUNMODE_CACHE;
+ if (argv[i+1])
+ {
+ cacheno = atoi (argv[i+1]);
+ if (argv[i+2])
+ cachecount = atoi (argv[i+2]);
+ }
+ }
+ }
+
+ switch (runmode)
+ {
+ case RUNMODE_RESERIALIZE:
+ printf ("%s", gedl_serialize (edl));
+ exit (0);
+ break;
+ case RUNMODE_UI:
+
+ signal(SIGUSR2, nop_handler);
+
+ gedl_monitor_start (edl);
+
+ return gedl_ui_main (edl);
+ case RUNMODE_RENDER:
+ tot_frames = gedl_get_duration (edl);
+ if (edl->range_end == 0)
+ edl->range_end = tot_frames-1;
+ encode_frames (edl);
+ gedl_free (edl);
+ return 0;
+ case RUNMODE_CACHE:
+ tot_frames = gedl_get_duration (edl);
+ if (edl->range_end == 0)
+ edl->range_end = tot_frames-1;
+ process_frames_cache (edl);
+ gedl_free (edl);
+ return 0;
+ case RUNMODE_CACHE_STAT:
+ process_frames_cache_stat (edl);
+ gedl_free (edl);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+char *gedl_serialize (GeglEDL *edl)
+{
+ GList *l;
+ char *ret;
+ GString *ser = g_string_new ("");
+
+ if (edl->proxy_width != DEFAULT_proxy_width)
+ g_string_append_printf (ser, "proxy-width=%i\n", edl->proxy_width);
+ if (edl->proxy_height != DEFAULT_proxy_height)
+ g_string_append_printf (ser, "proxy-height=%i\n", edl->proxy_height);
+ if (edl->framedrop != DEFAULT_framedrop)
+ g_string_append_printf (ser, "framedrop=%i\n", edl->framedrop);
+
+ if (strcmp(edl->output_path, DEFAULT_output_path))
+ g_string_append_printf (ser, "output-path=%s\n", edl->output_path);
+ if (strcmp(edl->video_codec, DEFAULT_video_codec))
+ g_string_append_printf (ser, "video-codec=%s\n", edl->video_codec);
+ if (strcmp(edl->audio_codec, DEFAULT_audio_codec))
+ g_string_append_printf (ser, "audio-codec=%s\n", edl->audio_codec);
+ if (edl->video_width != DEFAULT_video_width)
+ g_string_append_printf (ser, "video-width=%i\n", edl->video_width);
+ if (edl->video_height != DEFAULT_video_height)
+ g_string_append_printf (ser, "video-height=%i\n", edl->video_height);
+ if (edl->video_bufsize != DEFAULT_video_bufsize)
+ g_string_append_printf (ser, "video-bufsize=%i\n", edl->video_bufsize);
+ if (edl->video_bitrate != DEFAULT_video_bitrate)
+ g_string_append_printf (ser, "video-bitrate=%i\n", edl->video_bitrate);
+ if (edl->video_tolerance != DEFAULT_video_tolerance)
+ g_string_append_printf (ser, "video-tolerance=%i\n", edl->video_tolerance);
+ if (edl->audio_bitrate != DEFAULT_audio_bitrate)
+ g_string_append_printf (ser, "audio-bitrate=%i\n", edl->audio_bitrate);
+ if (edl->audio_samplerate != DEFAULT_audio_samplerate)
+ g_string_append_printf (ser, "audio-samplerate=%i\n", edl->audio_samplerate);
+
+ g_string_append_printf (ser, "fps=%f\n", gedl_get_fps (edl));
+
+ if (edl->range_start != DEFAULT_range_start)
+ g_string_append_printf (ser, "range-start=%i\n", edl->range_start);
+ if (edl->range_end != DEFAULT_range_end)
+ g_string_append_printf (ser, "range-end=%i\n", edl->range_end);
+
+ if (edl->selection_start != DEFAULT_selection_start)
+ g_string_append_printf (ser, "selection-start=%i\n", edl->selection_start);
+ if (edl->selection_end != DEFAULT_selection_end)
+ g_string_append_printf (ser, "selection-end=%i\n", edl->selection_end);
+ if (edl->scale != 1.0)
+ g_string_append_printf (ser, "frame-scale=%f\n", edl->scale);
+ if (edl->t0 != 1.0)
+ g_string_append_printf (ser, "t0=%f\n", edl->t0);
+
+ g_string_append_printf (ser, "frame-no=%i\n", edl->frame_no);
+ g_string_append_printf (ser, "\n");
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ gchar *path = clip->path;
+ if (!path)
+ path = "";
+ if (!strncmp (path, edl->parent_path, strlen(edl->parent_path)))
+ path += strlen (edl->parent_path);
+
+ if (strlen(path)== 0 &&
+ clip->start == 0 &&
+ clip->end == 0 &&
+ clip->filter_graph)
+ {
+ g_string_append_printf (ser, "--%s\n", clip->filter_graph);
+ }
+ else
+ {
+ g_string_append_printf (ser, "%s %d %d ", path, clip->start, clip->end);
+ if (clip->filter_graph||clip->fade)
+ g_string_append_printf (ser, "-- ");
+ if (clip->fade)
+ g_string_append_printf (ser, "[fade=%i] ", clip->fade);
+ if (clip->filter_graph)
+ g_string_append_printf (ser, "%s", clip->filter_graph);
+ g_string_append_printf (ser, "\n");
+ }
+ }
+ g_string_append_printf (ser, "-----\n");
+ for (l = edl->clip_db; l; l = l->next)
+ {
+ SourceClip *clip = l->data;
+ g_string_append_printf (ser, "%s %d %d %d%s%s%s\n", clip->path, clip->start, clip->end, clip->duration,
+ "", //(edl->active_source == clip)?" [active]":"",
+ clip->title?" -- ":"",clip->title?clip->title:"");
+ }
+ ret=ser->str;
+ g_string_free (ser, FALSE);
+ return ret;
+}
+
+void
+gegl_meta_set_audio (const char *path,
+ GeglAudioFragment *audio)
+{
+ GError *error = NULL;
+ GExiv2Metadata *e2m = gexiv2_metadata_new ();
+ gexiv2_metadata_open_path (e2m, path, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ }
+ else
+ {
+ int i, c;
+ GString *str = g_string_new ("");
+ int sample_count = gegl_audio_fragment_get_sample_count (audio);
+ int channels = gegl_audio_fragment_get_channels (audio);
+ if (gexiv2_metadata_has_tag (e2m, "Xmp.xmp.GEGL"))
+ gexiv2_metadata_clear_tag (e2m, "Xmp.xmp.GEGL");
+
+ g_string_append_printf (str, "%i %i %i %i",
+ gegl_audio_fragment_get_sample_rate (audio),
+ gegl_audio_fragment_get_channels (audio),
+ gegl_audio_fragment_get_channel_layout (audio),
+ gegl_audio_fragment_get_sample_count (audio));
+
+ for (i = 0; i < sample_count; i++)
+ for (c = 0; c < channels; c++)
+ g_string_append_printf (str, " %0.5f", audio->data[c][i]);
+
+ gexiv2_metadata_set_tag_string (e2m, "Xmp.xmp.GeglAudio", str->str);
+ gexiv2_metadata_save_file (e2m, path, &error);
+ if (error)
+ g_warning ("%s", error->message);
+ g_string_free (str, TRUE);
+ }
+ g_object_unref (e2m);
+}
+
+void
+gegl_meta_get_audio (const char *path,
+ GeglAudioFragment *audio)
+{
+ GError *error = NULL;
+ GExiv2Metadata *e2m = gexiv2_metadata_new ();
+ gexiv2_metadata_open_path (e2m, path, &error);
+ if (!error)
+ {
+ GString *word = g_string_new ("");
+ gchar *p;
+ gchar *ret = gexiv2_metadata_get_tag_string (e2m, "Xmp.xmp.GeglAudio");
+ int element_no = 0;
+ int channels = 2;
+ int max_samples = 2000;
+
+ if (ret)
+ for (p = ret; p==ret || p[-1] != '\0'; p++)
+ {
+ switch (p[0])
+ {
+ case '\0':case ' ':
+ if (word->len > 0)
+ {
+ switch (element_no++)
+ {
+ case 0:
+ gegl_audio_fragment_set_sample_rate (audio, g_strtod (word->str, NULL));
+ break;
+ case 1:
+ channels = g_strtod (word->str, NULL);
+ gegl_audio_fragment_set_channels (audio, channels);
+ break;
+ case 2:
+ gegl_audio_fragment_set_channel_layout (audio, g_strtod (word->str, NULL));
+ break;
+ case 3:
+ gegl_audio_fragment_set_sample_count (audio, g_strtod (word->str, NULL));
+ break;
+ default:
+ {
+ int sample_no = element_no - 4;
+ int channel_no = sample_no % channels;
+ sample_no/=2;
+ if (sample_no < max_samples)
+ audio->data[channel_no][sample_no] = g_strtod (word->str, NULL);
+ }
+ break;
+ }
+ }
+ g_string_assign (word, "");
+ break;
+ default:
+ g_string_append_c (word, p[0]);
+ break;
+ }
+ }
+ g_string_free (word, TRUE);
+ g_free (ret);
+ }
+ else
+ g_warning ("%s", error->message);
+ g_object_unref (e2m);
+}
+
+void gedl_set_selection (GeglEDL *edl, int start_frame, int end_frame)
+{
+ edl->selection_start = start_frame;
+ edl->selection_end = end_frame;
+}
+
+void gedl_get_selection (GeglEDL *edl,
+ int *start_frame,
+ int *end_frame)
+{
+ if (start_frame)
+ *start_frame = edl->selection_start;
+ if (end_frame)
+ *end_frame = edl->selection_end;
+}
+
+void gedl_set_range (GeglEDL *edl, int start_frame, int end_frame)
+{
+ edl->range_start = start_frame;
+ edl->range_end = end_frame;
+}
+
+void gedl_get_range (GeglEDL *edl,
+ int *start_frame,
+ int *end_frame)
+{
+ if (start_frame)
+ *start_frame = edl->range_start;
+ if (end_frame)
+ *end_frame = edl->range_end;
+}
+
+Clip * edl_get_clip_for_frame (GeglEDL *edl, int frame)
+{
+ GList *l;
+ int t = 0;
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ if (frame >= t && frame < t + clip_get_frames (clip))
+ {
+ return clip;
+ }
+ t += clip_get_frames (clip);
+ }
+ return NULL;
+}
diff --git a/gcut/gedl.h b/gcut/gedl.h
new file mode 100644
index 0000000..b47b6d6
--- /dev/null
+++ b/gcut/gedl.h
@@ -0,0 +1,259 @@
+#if TODO // this is the projects todo-list
+
+ bugs
+ huge video files cause (cairo) thumtrack overflow, vertical also has this problem - how to split?
+ if the initial frame is not a representative video frame of the comp, audio glitches
+ ogv render files are more compatible than generated mp4 files, firefox among few that renders the mp4 in
audio sync
+
+ roadmap/missing features
+ move gedl to gegl git repo
+
+ engine
+ rescaling playback speed of clips
+ support importing clips of different fps
+
+ support for configuring the final background render to be as high
+ fidelity as GEGL processing allows - rather than sharing tuning for
+ preview rendering.
+
+ support for other timecodes, mm:ss:ff and s
+ using edl files as clip sources - hopefully without even needing caches.
+
+ global filters
+ overlaying of audio from wav / mp3 files
+ operation chains
+ subtitles
+
+ ui
+ port gedl-ui.c to lua
+ detect locked or crashed ui, kill and respawn
+ trimming by mouse / dragging clips around by mouse
+ show a modal ui-block when generating proxies/thumbtrack on media import, instead of blocking/being
blank
+ gaps in timeline (will be implemented as blank clips - but ui could be different)
+ insert videos from the commandline
+ ui for adding/editing global filters/annotation/sound bits/beeps
+
+
+#endif
+
+#define CACHE_FORMAT "jpg"
+#define GEDL_SAMPLER GEGL_SAMPLER_NEAREST
+
+#ifndef GEDL_H
+#define GEDL_H
+
+#include <gio/gio.h>
+
+typedef struct _GeglEDL GeglEDL;
+typedef struct _Clip Clip;
+
+void gedl_set_use_proxies (GeglEDL *edl, int use_proxies);
+int gegl_make_thumb_video (GeglEDL *edl, const char *path, const char *thumb_path);
+char *gedl_make_proxy_path (GeglEDL *edl, const char *clip_path);
+const char *compute_cache_path (const char *path);
+
+#define CACHE_TRY_SIMPLE (1<<0)
+#define CACHE_TRY_MIX (1<<1)
+#define CACHE_TRY_FILTERED (1<<2)
+#define CACHE_TRY_ALL (CACHE_TRY_SIMPLE | CACHE_TRY_FILTERED | CACHE_TRY_MIX)
+#define CACHE_MAKE_FILTERED (1<<3)
+#define CACHE_MAKE_SIMPLE (1<<4)
+#define CACHE_MAKE_MIX (1<<5)
+#define CACHE_MAKE_ALL (CACHE_MAKE_SIMPLE|CACHE_MAKE_MIX|CACHE_MAKE_FILTERED)
+
+enum {
+ GEDL_UI_MODE_FULL = 0,
+ GEDL_UI_MODE_NONE = 1,
+ GEDL_UI_MODE_TIMELINE = 2,
+ GEDL_UI_MODE_PART = 3,
+};
+
+#define GEDL_LAST_UI_MODE 1
+
+GeglEDL *gedl_new (void);
+void gedl_free (GeglEDL *edl);
+void gedl_set_fps (GeglEDL *edl,
+ double fps);
+double gedl_get_fps (GeglEDL *edl);
+int gedl_get_duration (GeglEDL *edl);
+double gedl_get_time (GeglEDL *edl);
+void gedl_parse_line (GeglEDL *edl, const char *line);
+GeglEDL *gedl_new_from_path (const char *path);
+void gedl_load_path (GeglEDL *edl, const char *path);
+void gedl_save_path (GeglEDL *edl, const char *path);
+GeglAudioFragment *gedl_get_audio (GeglEDL *edl);
+Clip *gedl_get_clip (GeglEDL *edl, int frame, int *clip_frame_no);
+
+void gedl_set_frame (GeglEDL *edl, int frame);
+void gedl_set_time (GeglEDL *edl, double seconds);
+int gedl_get_frame (GeglEDL *edl);
+char *gedl_serialize (GeglEDL *edl);
+
+void gedl_set_range (GeglEDL *edl, int start_frame, int end_frame);
+void gedl_get_range (GeglEDL *edl,
+ int *start_frame,
+ int *end_frame);
+
+void gedl_set_selection (GeglEDL *edl, int start_frame, int end_frame);
+void gedl_get_selection (GeglEDL *edl,
+ int *start_frame,
+ int *end_frame);
+char *gedl_make_thumb_path (GeglEDL *edl, const char *clip_path);
+guchar *gedl_get_cache_bitmap (GeglEDL *edl, int *length_ret);
+
+
+Clip *clip_new (GeglEDL *edl);
+void clip_free (Clip *clip);
+const char *clip_get_path (Clip *clip);
+void clip_set_path (Clip *clip, const char *path);
+int clip_get_start (Clip *clip);
+int clip_get_end (Clip *clip);
+int clip_get_frames (Clip *clip);
+void clip_set_start (Clip *clip, int start);
+void clip_set_end (Clip *clip, int end);
+void clip_set_range (Clip *clip, int start, int end);
+int clip_is_static_source (Clip *clip);
+gchar * clip_get_frame_hash (Clip *clip, int clip_frame_no);
+Clip * clip_get_next (Clip *self);
+Clip * clip_get_prev (Clip *self);
+void clip_fetch_audio (Clip *clip);
+void clip_set_full (Clip *clip, const char *path, int start, int end);
+Clip * clip_new_full (GeglEDL *edl, const char *path, int start, int end);
+
+//void clip_set_frame_no (Clip *clip, int frame_no);
+void clip_render_frame (Clip *clip, int clip_frame_no);
+
+Clip * edl_get_clip_for_frame (GeglEDL *edl, int frame);
+void gedl_make_proxies (GeglEDL *edl);
+void gedl_get_video_info (const char *path, int *duration, double *fps);
+void gegl_meta_set_audio (const char *path,
+ GeglAudioFragment *audio);
+void gegl_meta_get_audio (const char *path,
+ GeglAudioFragment *audio);
+
+#define SPLIT_VER 0.8
+
+extern char *gedl_binary_path;
+
+/*********/
+
+typedef struct SourceClip
+{
+ char *path;
+ int start;
+ int end;
+ char *title;
+ int duration;
+ int editing;
+ char *filter_graph; /* chain of gegl filters */
+} SourceClip;
+
+struct _Clip
+{
+ char *path; /*path to media file */
+ int start; /*frame number starting with 0 */
+ int end; /*last frame, inclusive fro single frame, make equal to start */
+ char *title;
+ int duration;
+ int editing;
+ char *filter_graph; /* chain of gegl filters */
+ /* to here Clip must match start of SourceClip */
+ GeglEDL *edl;
+
+ double fps;
+ int fade; /* the main control for fading in.. */
+ int static_source;
+ int is_chain;
+ int is_meta;
+
+ int abs_start;
+
+ const char *clip_path;
+ GeglNode *gegl;
+ GeglAudioFragment *audio;
+ GeglNode *chain_loader;
+ GeglNode *full_loader;
+ GeglNode *proxy_loader;
+ GeglNode *loader; /* nop that one of the prior is linked to */
+
+ GeglNode *nop_scaled;
+ GeglNode *nop_crop;
+ GeglNode *nop_store_buf;
+
+ GMutex mutex;
+};
+
+struct _GeglEDL
+{
+ GFileMonitor *monitor;
+ char *path;
+ char *parent_path;
+ GList *clip_db;
+ GList *clips;
+ int frame; /* render thread, frame_no is ui side */
+ double fps;
+ GeglBuffer *buffer;
+ GeglBuffer *buffer_copy_temp;
+ GeglBuffer *buffer_copy;
+ GMutex buffer_copy_mutex;
+ GeglNode *cached_result;
+ GeglNode *gegl;
+ int playing;
+ int width;
+ int height;
+ GeglNode *cache_loader;
+ int cache_flags;
+ int selection_start;
+ int selection_end;
+ int range_start;
+ int range_end;
+ const char *output_path;
+ const char *video_codec;
+ const char *audio_codec;
+ int proxy_width;
+ int proxy_height;
+ int video_width;
+ int video_height;
+ int video_size_default;
+ int video_bufsize;
+ int video_bitrate;
+ int video_tolerance;
+ int audio_bitrate;
+ int audio_samplerate;
+ int frame_no;
+ int source_frame_no;
+ int use_proxies;
+ int framedrop;
+ int ui_mode;
+
+ GeglNode *result;
+ GeglNode *store_final_buf;
+ GeglNode *mix;
+
+ GeglNode *encode;
+ double scale;
+ double t0;
+ Clip *active_clip;
+
+ void *mrg;
+
+ char *clip_query;
+ int clip_query_edited;
+ int filter_edited;
+} _GeglEDL;
+
+void update_size (GeglEDL *edl, Clip *clip);
+void remove_in_betweens (GeglNode *nop_scaled, GeglNode *nop_filtered);
+int is_connected (GeglNode *a, GeglNode *b);
+void gedl_update_buffer (GeglEDL *edl);
+
+#ifdef MICRO_RAPTOR_GUI
+ /* renderer.h */
+void renderer_toggle_playing (MrgEvent *event, void *data1, void *data2);
+void gedl_cache_invalid (GeglEDL *edl);
+int renderer_done (GeglEDL *edl);
+void renderer_start (GeglEDL *edl);
+
+#endif
+
+#endif
diff --git a/gcut/iconographer.c b/gcut/iconographer.c
new file mode 100644
index 0000000..c0ebbd4
--- /dev/null
+++ b/gcut/iconographer.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2015 Rituwall Inc, authored by pippin gimp org
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define main iconographer_main
+
+#include <gegl.h>
+#include <gegl-audio-fragment.h>
+#include <glib/gprintf.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define NEGL_RGB_HEIGHT 42
+#define NEGL_RGB_THEIGHT 42
+#define NEGL_RGB_HIST_DIM 6 // if changing make dim * dim * dim divisible by 3
+#define NEGL_RGB_HIST_SLOTS (NEGL_RGB_HIST_DIM * NEGL_RGB_HIST_DIM * NEGL_RGB_HIST_DIM)
+#define NEGL_FFT_DIM 64
+
+/* each row in the video terrain is the following 8bit RGB (24bit) data: */
+typedef struct FrameInfo
+{
+ uint8_t rgb_hist[NEGL_RGB_HIST_SLOTS];
+ uint8_t rgb_square_diff[3];
+ uint8_t audio_energy[3];
+} FrameInfo;
+
+char *format="histogram diff audio 4 thumb 64 mid-col 20";
+
+int frame_start = 0;
+int frame_end = 0;
+int total_frames = 0;
+double frame_rate = 0;
+
+char *video_path = NULL;
+char *thumb_path = NULL;
+char *input_analysis_path = NULL;
+char *output_analysis_path = NULL;
+int show_progress = 0;
+int frame_thumb = 0;
+int horizontal = 0;
+int time_out = 0;
+
+long babl_ticks (void);
+
+void usage()
+{
+ printf ("usage: iconographer [options] <video> [thumb]\n"
+" -p, --progress - show /progress in terminal\n"
+" -a <analysis-path>, ---analysis\n"
+" - path to store information extraction result, if the file\n"
+" already exists it will be reused for best frame analysis\n"
+" instead of a full dump happening again.\n"
+" -h, --horizontal store a horizontal strata instead of vertical\n"
+" -t, --timeout - stop doing frame info dump after this many seconds have passed)\n"
+/*" -s <frame>, --start-frame <frame>\n"
+" - first frame to extract analysis from (default 0)\n"*/
+" -e <frame>, --end-frame <frame>\n"
+" - last frame to extract analysis from (default is 0 which means auto end)\n"
+" -f, --format - format string, specify which forms of analysis to put in the analysis file,\n"
+" the default format is: \"histogram audio thumb 40 mid-col 20\"\n"
+"\n"
+"\n"
+"Options can also follow the video (and thumb) arguments.\n"
+"\n");
+ exit (0);
+}
+
+void parse_args (int argc, char **argv)
+{
+ int i;
+ int stage = 0;
+ for (i = 1; i < argc; i++)
+ {
+ if (g_str_equal (argv[i], "-f")||
+ g_str_equal (argv[i], "--format"))
+ {
+ format = g_strdup (argv[i+1]);
+ i++;
+ }
+ else if (g_str_equal (argv[i], "-p") ||
+ g_str_equal (argv[i], "--progress"))
+ {
+ show_progress = 1;
+ }
+ else if (g_str_equal (argv[i], "-h") ||
+ g_str_equal (argv[i], "--horizontal"))
+ {
+ horizontal = 1;
+ }
+ else if (g_str_equal (argv[i], "-v") ||
+ g_str_equal (argv[i], "--vertical"))
+ {
+ horizontal = 0;
+ }
+ else if (g_str_equal (argv[i], "-a") ||
+ g_str_equal (argv[i], "--analysis"))
+ {
+ input_analysis_path = g_strdup (argv[i+1]);
+ output_analysis_path = g_strdup (argv[i+1]);
+ i++;
+ }
+
+ else if (g_str_equal (argv[i], "-s") ||
+ g_str_equal (argv[i], "--start-frame"))
+ {
+ frame_start = g_strtod (argv[i+1], NULL);
+ i++;
+ }
+ else if (g_str_equal (argv[i], "-t") ||
+ g_str_equal (argv[i], "--time-out"))
+ {
+ time_out = g_strtod (argv[i+1], NULL);
+ i++;
+ }
+ else if (g_str_equal (argv[i], "-e") ||
+ g_str_equal (argv[i], "--end-frame"))
+ {
+ frame_end = g_strtod (argv[i+1], NULL);
+ i++;
+ }
+ else if (g_str_equal (argv[i], "--help"))
+ {
+ usage();
+ }
+ else if (stage == 0)
+ {
+ video_path = g_strdup (argv[i]);
+ stage = 1;
+ } else if (stage == 1)
+ {
+ thumb_path = g_strdup (argv[i]);
+ stage = 2;
+ }
+ }
+}
+
+#include <string.h>
+
+GeglNode *gegl_decode = NULL;
+
+GeglBuffer *previous_video_frame = NULL;
+GeglBuffer *video_frame = NULL;
+GeglBuffer *terrain = NULL;
+
+uint8_t rgb_hist_shuffler[NEGL_RGB_HIST_SLOTS];
+uint8_t rgb_hist_unshuffler[NEGL_RGB_HIST_SLOTS];
+
+typedef struct {
+ int r;
+ int g;
+ int b;
+ int no;
+} Entry;
+
+static int rgb_hist_inited = 0;
+
+static gint sorted_color (gconstpointer a, gconstpointer b)
+{
+ const Entry *ea = a;
+ const Entry *eb = b;
+ return (ea->g * 110011 + ea->r * 213 + ea->b) -
+ (eb->g * 110011 + eb->r * 213 + eb->b);
+}
+
+static inline void init_rgb_hist (void)
+{
+ /* sort RGB histogram slots by luminance for human readability
+ */
+ if (!rgb_hist_inited)
+ {
+ GList *list = NULL;
+ GList *l;
+ int r, g, b, i;
+ i = 0;
+ for (r = 0; r < NEGL_RGB_HIST_DIM; r++)
+ for (g = 0; g < NEGL_RGB_HIST_DIM; g++)
+ for (b = 0; b < NEGL_RGB_HIST_DIM; b++, i++)
+ {
+ Entry *e = g_malloc0 (sizeof (Entry));
+ e->r = r;
+ e->g = g;
+ e->b = b;
+ e->no = i;
+ list = g_list_prepend (list, e);
+ }
+ list = g_list_sort (list, sorted_color);
+ i = 0;
+ for (l = list; l; l = l->next, i++)
+ {
+ Entry *e = l->data;
+ int no = e->no;
+ int li = i;
+ rgb_hist_shuffler[li] = no;
+ rgb_hist_unshuffler[no] = li;
+ g_free (e);
+ }
+ g_list_free (list);
+ rgb_hist_inited = 1;
+ }
+}
+
+int rgb_hist_shuffle (int in)
+{
+ init_rgb_hist();
+ return rgb_hist_unshuffler[in];
+}
+
+int rgb_hist_unshuffle (int in)
+{
+ init_rgb_hist();
+ return rgb_hist_unshuffler[in];
+}
+
+GeglNode *store, *load;
+
+static void decode_frame_no (int frame)
+{
+ if (video_frame)
+ {
+ if (strstr (format, "histogram"))
+ {
+ if (previous_video_frame)
+ g_object_unref (previous_video_frame);
+ previous_video_frame = gegl_buffer_dup (video_frame);
+ }
+ g_object_unref (video_frame);
+ }
+ video_frame = NULL;
+ gegl_node_set (load, "frame", frame, NULL);
+ gegl_node_process (store);
+}
+
+int count_color_bins (FrameInfo *info, int threshold)
+{
+ int count = 0;
+ int i;
+ for (i = 0; i < NEGL_RGB_HIST_SLOTS; i++)
+ if (info->rgb_hist[i] > threshold)
+ count ++;
+
+ return count;
+}
+
+float score_frame (FrameInfo *info, int frame_no)
+{
+ float sum_score = 0.0;
+ float rgb_histogram_count = count_color_bins (info, 1) * 1.0 / NEGL_RGB_HIST_SLOTS;
+ float audio_energy = info->audio_energy[1] / 255.0;
+ float new_scene = (info->rgb_square_diff[0] / 255.0 +
+ info->rgb_square_diff[1] / 255.0 +
+ info->rgb_square_diff[2] / 255.0) * 3;
+ float after_first_40_sec = frame_no / frame_rate > 40.0 ? 1.0 : 0.3;
+ float after_first_12_sec = frame_no / frame_rate > 12.0 ? 1.0 : 0.1;
+ float within_first_third = frame_no < total_frames / 3 ? 1 : 0.6;
+
+ sum_score = rgb_histogram_count;
+ sum_score *= within_first_third * 0.33;
+ sum_score *= after_first_40_sec * 0.33;
+ sum_score *= after_first_12_sec * 0.33;
+ sum_score *= (audio_energy + 0.1) * 0.7;
+ sum_score *= (new_scene + 0.05);
+ return sum_score;
+}
+
+static void find_best_thumb (void)
+{
+ int frame = 0;
+ float best_score = 0.0;
+ frame_thumb = 0;
+
+ for (frame = 0; frame < frame_end; frame++)
+ {
+ FrameInfo info;
+ GeglRectangle terrain_row;
+ if (horizontal)
+ terrain_row = (GeglRectangle){frame-frame_start, 0, 1, sizeof (FrameInfo)};
+ else
+ terrain_row = (GeglRectangle){0, frame-frame_start, sizeof (FrameInfo), 1};
+ gegl_buffer_get (terrain, &terrain_row, 1.0, babl_format("RGB u8"),
+ &info, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ float score = score_frame (&info, frame);
+ if (score > best_score)
+ {
+ best_score = score;
+ frame_thumb = frame;
+ }
+ }
+ fprintf (stderr, "best frame: %i\n", frame_thumb);
+}
+
+GeglNode *translate = NULL;
+
+static int extract_mid_col (GeglBuffer *buffer, void *rgb_mid_col, int samples)
+{
+ GeglRectangle mid_col;
+ mid_col.x = 0;
+ mid_col.y =
+ gegl_buffer_get_extent (buffer)-> height * 1.0 *
+ samples / gegl_buffer_get_extent (buffer)->width / 2.0;
+ mid_col.height = 1;
+ mid_col.width = samples;
+ gegl_buffer_get (video_frame, &mid_col,
+ 1.0 * samples / gegl_buffer_get_extent (buffer)->width,
+ babl_format ("R'G'B' u8"),
+ rgb_mid_col,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ return 3 * samples;
+}
+
+static int extract_thumb (GeglBuffer *buffer, void *rgb_thumb, int samples, int samples2)
+{
+ GeglRectangle thumb_scan;
+ static float vpos = 0.0;
+ if (horizontal)
+ {
+ thumb_scan.y = 0;
+ thumb_scan.x =
+ gegl_buffer_get_extent (buffer)-> width * 1.0 *
+ samples / gegl_buffer_get_extent (buffer)->width * vpos;
+ vpos += (1.0/samples2);
+ if (vpos > 1.0) vpos = 0.0;
+ thumb_scan.width = 1;
+ thumb_scan.height = samples;
+ }
+ else
+ {
+ thumb_scan.x = 0;
+ thumb_scan.y =
+ gegl_buffer_get_extent (buffer)-> height * 1.0 *
+ samples / gegl_buffer_get_extent (buffer)->width * vpos;
+ vpos += (1.0/samples2);
+ if (vpos > 1.0) vpos = 0.0;
+ thumb_scan.height = 1;
+ thumb_scan.width = samples;
+ }
+ gegl_buffer_get (buffer, &thumb_scan,
+ horizontal?1.0 * samples/ gegl_buffer_get_extent (buffer)->height:
+ 1.0 * samples/ gegl_buffer_get_extent (buffer)->width,
+ babl_format ("R'G'B' u8"),
+ (void*)rgb_thumb,
+ //(void*)&(info->rgb_thumb)[0],
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ return 3 * samples;
+}
+
+
+int extract_audio_energy (GeglAudioFragment *audio, uint8_t *audio_energy, int dups)
+{
+ int i;
+ float left_max = 0;
+ float right_max = 0;
+ float left_sum = 0;
+ float right_sum = 0;
+ int sample_count = gegl_audio_fragment_get_sample_count (audio);
+ for (i = 0; i < sample_count; i++)
+ {
+ left_sum += fabs (audio->data[0][i]);
+ right_sum += fabs (audio->data[1][i]);
+ if (fabs (audio->data[0][i]) > left_max)
+ left_max = fabs (audio->data[0][i]);
+ if (fabs (audio->data[1][i]) > right_max)
+ right_max = fabs (audio->data[1][i]);
+ }
+ left_sum /= sample_count;
+ right_sum /= sample_count;
+ left_max = left_sum;
+ right_max = right_sum;
+
+ left_max *= 255;
+ if (left_max > 255)
+ left_max = 255;
+ right_max *= 255;
+ if (right_max > 255)
+ right_max = 255;
+
+ for (i = 0; i < dups; i ++)
+ {
+ audio_energy[3*i+0] = left_max;
+ audio_energy[3*i+1] = (left_max+right_max)/2;
+ audio_energy[3*i+2] = right_max;
+ }
+ return 3 * dups;
+}
+
+int extract_mid_row (GeglBuffer *buffer, void *rgb_mid_row, int samples)
+{
+ GeglRectangle mid_row;
+ mid_row.width = 1;
+ mid_row.height = samples;
+ mid_row.x =
+ gegl_buffer_get_extent (buffer)-> width * 1.0 *
+ samples / gegl_buffer_get_extent (buffer)->height / 2.0;
+ mid_row.y = 0;
+ gegl_buffer_get (buffer, &mid_row,
+ 1.0 * samples / gegl_buffer_get_extent (buffer)->height,
+ babl_format ("R'G'B' u8"),
+ rgb_mid_row,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ return 3 * samples;
+}
+
+static void record_pix_stats (GeglBuffer *buffer, GeglBuffer *previous_buffer,
+ uint8_t *info_rgb_hist,
+ uint8_t *rgb_square_diff)
+{
+ int rgb_hist[NEGL_RGB_HIST_DIM * NEGL_RGB_HIST_DIM * NEGL_RGB_HIST_DIM]={0,};
+ int sum = 0;
+ int second_max_hist = 0;
+ int max_hist = 0;
+ GeglBufferIterator *it = gegl_buffer_iterator_new (buffer, NULL, 0,
+ babl_format ("R'G'B' u8"),
+ GEGL_BUFFER_READ,
+ GEGL_ABYSS_NONE);
+ if (previous_video_frame)
+ gegl_buffer_iterator_add (it, previous_buffer, NULL, 0,
+ babl_format ("R'G'B' u8"),
+ GEGL_BUFFER_READ,
+ GEGL_ABYSS_NONE);
+
+ int slot;
+ for (slot = 0; slot < NEGL_RGB_HIST_SLOTS; slot ++)
+ rgb_hist[slot] = 0;
+ long r_square_diff = 0;
+ long g_square_diff = 0;
+ long b_square_diff = 0;
+
+ while (gegl_buffer_iterator_next (it))
+ {
+ uint8_t *data = (void*)it->data[0];
+ int i;
+ if (strstr (format, "histogram"))
+ {
+ for (i = 0; i < it->length; i++)
+ {
+ int r = data[i * 3 + 0] / 256.0 * NEGL_RGB_HIST_DIM;
+ int g = data[i * 3 + 1] / 256.0 * NEGL_RGB_HIST_DIM;
+ int b = data[i * 3 + 2] / 256.0 * NEGL_RGB_HIST_DIM;
+ int slot = r * NEGL_RGB_HIST_DIM * NEGL_RGB_HIST_DIM +
+ g * NEGL_RGB_HIST_DIM +
+ b;
+ if (slot < 0) slot = 0;
+ if (slot >= NEGL_RGB_HIST_SLOTS)
+ slot = NEGL_RGB_HIST_SLOTS;
+
+ rgb_hist[slot]++;
+ if (rgb_hist[slot] > max_hist)
+ {
+ second_max_hist = max_hist;
+ max_hist = rgb_hist[slot];
+ }
+ sum++;
+ }
+ }
+
+ if (previous_buffer)
+ {
+ uint8_t *data_prev = (void*)it->data[1];
+ int i;
+ for (i = 0; i < it->length; i++)
+ {
+ #define square(a) ((a)*(a))
+ r_square_diff += square(data[i * 3 + 0] -data_prev [i * 3 + 0]);
+ g_square_diff += square(data[i * 3 + 1] -data_prev [i * 3 + 1]);
+ b_square_diff += square(data[i * 3 + 2] -data_prev [i * 3 + 2]);
+ }
+ }
+ }
+
+ if (strstr(format, "histogram"))
+ {
+ int slot;
+ for (slot = 0; slot < NEGL_RGB_HIST_SLOTS; slot ++)
+ {
+ int val = (sqrtf(rgb_hist[slot]) / (sqrtf(second_max_hist) * 0.9 + sqrtf(max_hist) * 0.1)) * 255;
+ if (val > 255)
+ val = 255;
+ info_rgb_hist[rgb_hist_shuffle (slot)] = val;
+ }
+ }
+ if (previous_buffer)
+ {
+ rgb_square_diff[0] = sqrt(r_square_diff) * 255 / sum;
+ rgb_square_diff[1] = sqrt(g_square_diff) * 255 / sum;
+ rgb_square_diff[2] = sqrt(b_square_diff) * 255 / sum;
+ }
+}
+
+gint
+main (gint argc,
+ gchar **argv)
+{
+ if (argc < 2)
+ usage();
+
+ gegl_init (&argc, &argv);
+ parse_args (argc, argv);
+
+ gegl_decode = gegl_node_new ();
+
+ store = gegl_node_new_child (gegl_decode,
+ "operation", "gegl:buffer-sink",
+ "buffer", &video_frame, NULL);
+ load = gegl_node_new_child (gegl_decode,
+ "operation", "gegl:ff-load",
+ "frame", 0,
+ "path", video_path,
+ NULL);
+ gegl_node_link_many (load, store, NULL);
+
+ decode_frame_no (0); /* we issue a processing/decoding of a frame - to get metadata */
+ {
+ gegl_node_get (load, "frame-rate", &frame_rate, NULL);
+ total_frames = 0; gegl_node_get (load, "frames", &total_frames, NULL);
+
+ if (frame_end == 0)
+ frame_end = total_frames;
+ }
+ GeglRectangle terrain_rect;
+
+ if (horizontal)
+ terrain_rect = (GeglRectangle){0, 0,
+ frame_end - frame_start + 1,
+ 1024};
+ else
+ terrain_rect = (GeglRectangle){0, 0,
+ 1024,
+ frame_end - frame_start + 1};
+
+ if (input_analysis_path && g_file_test (input_analysis_path, G_FILE_TEST_IS_REGULAR))
+ {
+ GeglNode *load_graph = gegl_node_new ();
+ GeglNode *load = gegl_node_new_child (load_graph, "operation", "gegl:load", "path", input_analysis_path,
NULL);
+ GeglNode *store = gegl_node_new_child (load_graph, "operation",
+ "gegl:buffer-sink",
+ "buffer", &terrain, NULL);
+ gegl_node_link_many (load, store, NULL);
+ gegl_node_process (store);
+ g_object_unref (load_graph);
+
+ frame_end = frame_start + gegl_buffer_get_extent (terrain)->height;
+ /* the last frame aavilavle for analysis is the last one loaded rom cache,.. perhaps with some timeout */
+ }
+ else
+ {
+ terrain = gegl_buffer_new (&terrain_rect, babl_format ("R'G'B' u8"));
+ {
+ gint frame;
+ gint max_buf_pos = 0;
+ for (frame = frame_start; frame <= frame_end; frame++)
+ {
+ FrameInfo info = {0};
+ uint8_t buffer[4096] = {0,};
+ int buffer_pos = 0;
+
+ if (show_progress)
+ {
+ double percent_full = 100.0 * (frame-frame_start) / (frame_end-frame_start);
+ double percent_time = time_out?100.0 * babl_ticks()/1000.0/1000.0 / time_out:0.0;
+ fprintf (stdout, "\r%2.1f%% %i/%i (%i)",
+ percent_full>percent_time?percent_full:percent_time,
+ frame-frame_start,
+ frame_end-frame_start,
+ frame);
+ fflush (stdout);
+ }
+
+ GeglRectangle terrain_row;
+ if (horizontal)
+ terrain_row = (GeglRectangle){frame-frame_start, 0, 1, 1024};
+ else
+ terrain_row = (GeglRectangle){0, frame-frame_start, 1024, 1};
+
+ decode_frame_no (frame);
+
+ //for (int i=0;i<(signed)sizeof(buffer);i++)buffer[i]=0;
+
+ char *p = format;
+
+ GString *word = g_string_new ("");
+ while (*p == ' ') p++;
+ for (p= format;p==format || p[-1]!='\0';p++)
+ {
+ if (*p != '\0' && *p != ' ')
+ {
+ g_string_append_c (word, *p);
+ }
+ else
+ {
+ if (!strcmp (word->str, "histogram"))
+ {
+ record_pix_stats (video_frame, previous_video_frame,
+ &(info.rgb_hist[0]),
+ &(info.rgb_square_diff)[0]);
+ for (int i = 0; i < NEGL_RGB_HIST_SLOTS; i++)
+ {
+ buffer[buffer_pos] = info.rgb_hist[i];
+ buffer_pos++;
+ }
+ for (int i = 0; i < 3; i++)
+ {
+ buffer[buffer_pos] = info.rgb_square_diff[i];
+ buffer_pos++;
+ }
+ }
+ else if (!strcmp (word->str, "mid-row"))
+ {
+ int samples = NEGL_RGB_HEIGHT;
+ if (p[1] >= '0' && p[1] <= '9')
+ {
+ samples = g_strtod (&p[1], &p);
+ }
+ buffer_pos += extract_mid_row (video_frame, &(buffer)[buffer_pos], samples);
+ }
+ else if (!strcmp (word->str, "mid-col"))
+ {
+ int samples = NEGL_RGB_HEIGHT;
+ if (p[1] >= '0' && p[1] <= '9')
+ {
+ samples = g_strtod (&p[1], &p);
+ }
+ buffer_pos += extract_mid_col (video_frame, &(buffer)[buffer_pos], samples);
+ }
+ else if (!strcmp (word->str, "thumb"))
+ {
+ int samples = NEGL_RGB_THEIGHT;
+ int samples2;
+
+ if (p[1] >= '0' && p[1] <= '9')
+ {
+ samples = g_strtod (&p[1], &p);
+ }
+
+ if (horizontal)
+ samples2 = samples * gegl_buffer_get_width
(video_frame)/gegl_buffer_get_height(video_frame);
+ else
+ samples2 = samples * gegl_buffer_get_height
(video_frame)/gegl_buffer_get_width(video_frame);
+
+ buffer_pos += extract_thumb (video_frame, &(buffer)[buffer_pos], samples, samples2);
+ }
+ else if (!strcmp (word->str, "audio"))
+ {
+ int dups = 1;
+ GeglAudioFragment *audio = NULL;
+
+ if (p[1] >= '0' && p[1] <= '9')
+ {
+ dups = g_strtod (&p[1], &p);
+ }
+ gegl_node_get (load, "audio", &audio, NULL);
+ if (audio)
+ {
+ extract_audio_energy (audio, &buffer[buffer_pos], dups);
+ g_object_unref (audio);
+ }
+ buffer_pos+=3 * dups;
+ }
+ g_string_assign (word, "");
+ }
+ }
+ max_buf_pos = buffer_pos;
+ g_string_free (word, TRUE);
+
+ gegl_buffer_set (terrain, &terrain_row, 0, babl_format("RGB u8"),
+ buffer,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (time_out > 1.0 &&
+ babl_ticks()/1000.0/1000.0 > time_out)
+ {
+ frame_end = frame;
+ if (horizontal)
+ terrain_rect.width = frame_end - frame_start + 1;
+ else
+ terrain_rect.height = frame_end - frame_start + 1;
+ // gegl_buffer_set_extent (terrain, &terrain_rect);
+ }
+
+ if (horizontal)
+ terrain_rect.height = max_buf_pos/3;
+ else
+ terrain_rect.width = max_buf_pos/3;
+ gegl_buffer_set_extent (terrain, &terrain_rect);
+ }
+ if (show_progress)
+ {
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ }
+ }
+
+ if (output_analysis_path)
+ {
+ GeglNode *save_graph = gegl_node_new ();
+ GeglNode *readbuf = gegl_node_new_child (save_graph, "operation", "gegl:buffer-source", "buffer",
terrain, NULL);
+ GeglNode *save = gegl_node_new_child (save_graph, "operation", "gegl:png-save",
+ "path", output_analysis_path, NULL);
+ gegl_node_link_many (readbuf, save, NULL);
+ gegl_node_process (save);
+ g_object_unref (save_graph);
+ }
+ }
+
+ if (thumb_path)
+ {
+ GeglNode *save_graph = gegl_node_new ();
+ find_best_thumb ();
+ if (frame_thumb != 0)
+ decode_frame_no (frame_thumb-1);
+ decode_frame_no (frame_thumb);
+ GeglNode *readbuf = gegl_node_new_child (save_graph, "operation", "gegl:buffer-source", "buffer",
video_frame, NULL);
+ GeglNode *save = gegl_node_new_child (save_graph, "operation", "gegl:png-save",
+ "path", thumb_path, NULL);
+ gegl_node_link_many (readbuf, save, NULL);
+ gegl_node_process (save);
+ g_object_unref (save_graph);
+ }
+
+ if (video_frame)
+ g_object_unref (video_frame);
+ video_frame = NULL;
+ if (previous_video_frame)
+ g_object_unref (previous_video_frame);
+ previous_video_frame = NULL;
+ if (terrain)
+ g_object_unref (terrain);
+ terrain = NULL;
+ g_object_unref (gegl_decode);
+
+ gegl_exit ();
+
+ return 0;
+}
diff --git a/gcut/renderer.c b/gcut/renderer.c
new file mode 100644
index 0000000..0cdd139
--- /dev/null
+++ b/gcut/renderer.c
@@ -0,0 +1,287 @@
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <gegl.h>
+#include "gedl.h"
+#include <mrg.h>
+#include <SDL.h>
+#include <gegl-audio-fragment.h>
+
+static GThread *thread = NULL;
+long babl_ticks (void);
+static long prev_ticks = 0;
+int rendering_frame = -1;
+int done_frame = -1;
+static int audio_started = 0;
+static int audio_len = 0;
+static int audio_pos = 0;
+static int audio_post = 0;
+
+#define AUDIO_BUF_LEN 819200000
+
+int16_t audio_data[AUDIO_BUF_LEN];
+
+void gedl_cache_invalid (GeglEDL *edl)
+{
+ edl->frame = -1;
+ done_frame=-1;
+ rendering_frame=-1;
+}
+
+
+static void sdl_audio_cb(void *udata, Uint8 *stream, int len)
+{
+ int audio_remaining = audio_len - audio_pos;
+ if (audio_remaining < 0)
+ return;
+
+ if (audio_remaining < len) len = audio_remaining;
+
+ //SDL_MixAudio(stream, (uint8_t*)&audio_data[audio_pos/2], len, SDL_MIX_MAXVOLUME);
+ memcpy (stream, (uint8_t*)&audio_data[audio_pos/2], len);
+ audio_pos += len;
+ audio_post += len;
+ if (audio_pos >= AUDIO_BUF_LEN)
+ {
+ audio_pos = 0;
+ }
+}
+
+static void sdl_add_audio_sample (int sample_pos, float left, float right)
+{
+ audio_data[audio_len/2 + 0] = left * 32767.0 * 0.46;
+ audio_data[audio_len/2 + 1] = right * 32767.0 * 0.46;
+ audio_len += 4;
+
+ if (audio_len >= AUDIO_BUF_LEN)
+ {
+ audio_len = 0;
+ }
+}
+
+static void open_audio (int frequency)
+{
+ SDL_AudioSpec spec = {0};
+ SDL_Init(SDL_INIT_AUDIO);
+ spec.freq = frequency;
+ spec.format = AUDIO_S16SYS;
+ spec.channels = 2;
+ spec.samples = 1024;
+ spec.callback = sdl_audio_cb;
+ SDL_OpenAudio(&spec, 0);
+
+ if (spec.format != AUDIO_S16SYS)
+ {
+ fprintf (stderr, "not getting format we wanted\n");
+ }
+ if (spec.freq != frequency)
+ {
+ fprintf (stderr, "not getting desires samplerate(%i) we wanted got %i instead\n", frequency,
spec.freq);
+ }
+}
+
+void end_audio (void)
+{
+}
+
+
+static inline void skipped_frames (int count)
+{
+ //fprintf (stderr, "[%i]", count);
+}
+
+static inline void wait_for_frame ()
+{
+ //fprintf (stderr, ".");
+}
+
+
+void playing_iteration (Mrg *mrg, GeglEDL *edl);
+
+extern int got_cached;
+
+static gpointer renderer_thread (gpointer data)
+{
+ GeglEDL *edl = data;
+
+ for (;;)
+ {
+ playing_iteration (edl->mrg, edl);
+ {
+ if (edl->frame_no != done_frame)
+ {
+ rendering_frame = edl->frame_no;
+
+ {
+ GeglRectangle ext = {0,0,edl->width, edl->height};
+ //GeglRectangle ext = gegl_node_get_bounding_box (edl->result);
+ gegl_buffer_set_extent (edl->buffer, &ext);
+ }
+ gedl_set_frame (edl, edl->frame_no); /* this does the frame-set, which causes render */
+#if 0
+ {
+ GeglRectangle ext = gegl_node_get_bounding_box (edl->result);
+ gegl_buffer_set_extent (edl->buffer, &ext);
+ }
+#endif
+
+ {
+ GeglAudioFragment *audio = gedl_get_audio (edl);
+ if (audio)
+ {
+ int sample_count = gegl_audio_fragment_get_sample_count (audio);
+ if (sample_count > 0)
+ {
+ int i;
+ if (!audio_started)
+ {
+ open_audio (gegl_audio_fragment_get_sample_rate (audio));
+ SDL_PauseAudio(0);
+ audio_started = 1;
+ }
+ for (i = 0; i < sample_count; i++)
+ {
+ sdl_add_audio_sample (0, audio->data[0][i], audio->data[1][i]);
+ }
+ }
+ }
+ }
+ done_frame = rendering_frame;
+ mrg_queue_draw (edl->mrg, NULL); // could queue only rect - if we had it
+ }
+ else
+ {
+ g_usleep (50);
+ }
+ }
+ }
+ end_audio ();
+ return NULL;
+}
+
+void renderer_start (GeglEDL *edl)
+{
+ if (!thread)
+ thread = g_thread_new ("renderer", renderer_thread, edl);
+}
+
+gboolean cache_renderer_iteration (Mrg *mrg, gpointer data);
+
+void renderer_toggle_playing (MrgEvent *event, void *data1, void *data2)
+{
+ GeglEDL *edl = data1;
+ edl->playing = !edl->playing;
+ if (!edl->playing)
+ {
+ cache_renderer_iteration (event->mrg, edl);
+ }
+ else
+ {
+ killpg(0, SIGUSR2);
+ }
+ mrg_event_stop_propagate (event);
+ mrg_queue_draw (event->mrg, NULL);
+ prev_ticks = babl_ticks ();
+}
+
+static int max_frame (GeglEDL *edl)
+{
+ GList *l;
+ int t = 0;
+ int start, end;
+
+ gedl_get_range (edl, &start, &end);
+ if (end)
+ return end;
+
+ for (l = edl->clips; l; l = l->next)
+ {
+ Clip *clip = l->data;
+ t += clip_get_frames (clip);
+ }
+
+ return t;
+}
+
+
+void playing_iteration (Mrg *mrg, GeglEDL *edl)
+{
+ long ticks = 0;
+ double delta = 1;
+ ticks = babl_ticks ();
+ if (prev_ticks == 0) prev_ticks = ticks;
+
+ if (edl->playing)
+ {
+#if 0
+ if (prev_ticks - ticks < 1000000.0 / gedl_get_fps (edl))
+ return;
+#endif
+ delta = (((ticks - prev_ticks) / 1000000.0) * ( edl->fps ));
+ //fprintf (stderr, "%f\n", delta);
+ if (delta < 1.0)
+ {
+ wait_for_frame ();
+ mrg_queue_draw (mrg, NULL);
+ return;
+ }
+ //delta = 0;
+ {
+#if 0
+ static int frameskip = -1;
+ if (frameskip < 0)
+ {
+ if (getenv ("GEDL_FRAMESKIP"))
+ frameskip = 1;
+ else
+ frameskip = 0;
+ }
+ if (!frameskip)
+ delta = 1;
+#else
+ if (edl->framedrop)
+ {
+ if (delta >= 2.0)
+ {
+ skipped_frames ( (int)(delta)-1 );
+ }
+ }
+ else
+ {
+ if (delta > 1.0)
+ delta = 1;
+ else
+ delta = 0;
+ }
+#endif
+ }
+ if (rendering_frame != done_frame)
+ return;
+ if (delta >= 1.0)
+ {
+
+ if (edl->active_clip)
+ {
+ edl->frame_no += delta;
+ int start, end;
+ gedl_get_range (edl, &start, &end);
+ if (edl->frame_no > max_frame (edl))
+ {
+ edl->frame_no = 0;
+ if (end)
+ edl->frame_no = start;
+ }
+ edl->active_clip = edl_get_clip_for_frame (edl, edl->frame_no);
+ prev_ticks = ticks;
+ }
+ }
+ }
+}
+
+int renderer_done (GeglEDL *edl)
+{
+ return done_frame == edl->frame_no; //rendering_frame;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]